gibwater
发表于 2015-3-10 15:01:06
仔细的看了遍写的很通熟易懂
西北狼
发表于 2015-3-11 19:50:45
学习了很好,楼主幸苦
jianhong_wu
发表于 2015-3-14 09:24:11
第四节:平台软件和编译器软件的简介。
C语言代码写在哪里,谁负责把它翻译成Hex格式机器码?这就涉及到编辑和编译,从而诞生了平台和编译这两种软件。平台软件负责编辑源代码,编译软件负责把源代码翻译成Hex格式的机器码。
不同厂家的单片机,它所用的平台和编译器软件都不一样。即使是同样一个厂家的单片机,它也有可能存在多种不同的第三方平台软件和编译器软件,下面列举的一些例子只是主流的平台和编译软件,并不是说它们是唯一的。
PIC单片机的平台软件是MPLAB,8位单片机是PICC编译器,12位单片机是PIC18编译器,16位单片机是C30编译器。这个例子从侧面也说明了一个平台软件可以嵌入多种不同的编译器软件,平台软件和编译器软件存在一对多的关系。
51单片机的平台软件是keil,编译器是C51。
以上所述,单片机程序开发需要用到两种软件,但是实际项目开发的时候,我们只是跟平台软件打交道就可以了,因为编译器软件是当做一种独立配件嵌入到平台软件里,统一受平台软件控制。我在用PIC的8位单片机时,需要安装一次MPLAB平台软件,也需要独立再安装一次PICC编译器软件,然后运行MPLAB平台软件,在里面操作某个菜单设置选项,把PICC编译器跟MPLAB平台软件关联起来,也就是我所说的把PICC编译器嵌入到MPLAB平台软件里,统一接受平台软件的控制,但我写代码只需要跟MPLAB平台软件打交道就可以了。我早期在做51单片机开发时,也是需要把keil平台软件和C51软件分开安装,然后再把它们关联起来,但是现在从keil2版本开始,在安装keil平台软件时就已经默认把C51安装好了,并且自动把C51嵌入到了keil平台软件。我现在用keil4这个版本的平台软件,只需要安装一次keil平台软件就可以了,不需要像早期那样再单独安装C51编译器。
下节预告:用keil软件新建,关闭,打开一个完整工程的操作流程。
(未完待续)
海强
发表于 2015-3-14 10:00:06
你这是什么脑瓜子!什么东西都能写到一块去!
jianhong_wu
发表于 2015-3-14 10:23:18
海强 发表于 2015-3-14 10:00
你这是什么脑瓜子!什么东西都能写到一块去!
哈哈...用心,多读书,就可以融会贯通。
jianhong_wu
发表于 2015-3-17 15:13:26
本帖最后由 jianhong_wu 于 2015-3-17 15:56 编辑
第五节:用keil软件新建,关闭,打开一个完整工程的操作流程。 Keil平台软件的安装我就不多讲了,网上这方面的资料很多,大家可以百度一下如何安装keil的教程。下面开始讲解用keil软件新建,关闭,打开一个完整工程的操作流程。 第一步:新建一个工程文件夹。先在电脑D盘目录下新建一个文件夹,取名为“stc89c52rc”。 有2个地方需要解释:(1)文件夹以及后面所取的文件名不要用中文,请全部用英文,数字,或者下划线这些字符。keil软件支不支持中文名无所谓,但是在单片机这个行业,有一些单片机厂家的平台软件,某些版本是不支持中文名的,所以大家养成这个习惯,以后可以避免遇到一些不必要的麻烦。(2)新建的文件夹请直接放在某盘的根目录下,而不要放到某个已有文件夹的目录下。一方面是因为已有的文件名往往带有中文字,另外一方面是有一些单片机厂家的平台软件不支持嵌入层次太深的文件目录,所以大家养成这个习惯,以后可以避免遇到一些不必要的麻烦。
第二步:启动keil软件。双击桌面”keil uVision4”的图标启动keil软件。 第三步:关闭默认被打开的已有工程。打开keil软件时,如果发现此软件默认打开了一个之前已经存在的工程,请先关闭此工程。如果默认没有打开已有工程,这一步可以忽略跳过。关闭已有工程的操作是这样子的:点击上面”Project”选项,在弹出的下拉菜单中选择“Close Project”即可。 第四步:利用工具向导新建一个工程。点击上面”Project”选项,在弹出的下拉菜单中选择“newuVision Project...”,在弹出的对话框中,选择保存的目录是刚才第一步新建的文件夹“stc89c52rc”目录下,输入跟文件夹名称一样的文件名“stc89c52rc”,然后单击“保存”按键,此时会弹出一个选择单片机型号的对话框,双击”Atmel”这个厂家,在展开的下拉选项中选中“AT89C52”这个型号,然后点击“OK”,此时会弹出一个英文询问框“是否要复制STARTUP.A51这个文件到工程里?”我们单击“否”即可。
有3个地方需要解释:(1)以上新建的保存文件名应该跟我们第一步在D盘新建的文件夹名称一致,因为有一些单片机厂家的平台软件是有这个要求的,所以大家养成这个习惯,以后可以避免遇到一些不必要的麻烦。(2)上面之所以选择Atmel厂家的AT89C52单片机,是因为朱兆祺51学习板所用的单片机是STC89C52RC这个单片机,而STC89C52RC跟AT89C52是兼容的。(3)在弹出的询问框“是否要复制STARTUP.A51这个文件到工程里?”中,STARTUP.A51这个文件有什么含义?STARTUP.A51是一个启动程序文件,在单片机进入.c程序执行main函数之前,先去执行这个启动程序,这个启动程序是专门用来初始化RAM和设置堆栈等,如果我们选“否”不添加这个启动程序,编译器也会自动加入一段我们不能更改的默认启动程序。如果选“是”,那么这个文件就会出现在我们工程里,我们可以根据需要进行更改。但是大多数的情况下,我们都不会去更改这个文件的,所以无论你选“是”还是“否”,只要你不更改START.A51这个文件,对我们都是一样的。我本人一般情况下都是选“否”。
第五步:新建一个.c源文件。点击上面”File”选项,在弹出的下拉菜单中选择“New...”,会看到出来一个名字为”Text1”的文件。再一次点击上面”File”选项,在弹出的下拉菜单中选择“Save”,会弹出一个保存的对话框,还是选择保存在第一步新建的文件夹目录下,文件名取“stc89c52rc.c”,单击“保存”。
有2个地方需要解释:(1)以上所取的文件名必须带.c这个扩展名,表示此文件是C文件格式。(2)第五步仅仅相当于在工程文件夹里新建了一个.c格式的C文件,此C文件目前跟工程还没有任何关联。
第六步:把刚才新建的.c源文件添加到工程里,跟工程建立起关联的关系。点击左边”Porject”选项框里面的”Target 1”前面的“+”号(如果没有发现Project,请按以下第2条解释操作),在展开的下拉菜单下看到“Source Group 1”。右键单击“Source Group 1”选项,在下拉菜单中选择“Add Existing Files to Group ‘Source Group 1’...”选项,弹出一个文件选择对话框,单击选中刚才新建的.c源文件,然后单击一次“Add”按钮,此时虽然对话框没有关闭,但是已经把.c源文件添加到工程里了,这时只要再点击一次“Close”按钮即可把此对话框关闭。这时发现左边的“Source Group 1”前面多了一个”+”号,单击此”+”号展开,发现下面刚才我们新添加进去的.c源文件“stc89c52rc.c”。 有2个地方需要解释:(1)以上有一个地方,我本人觉得keil软件的用户体验做得不够好,容易引起误解。在弹出一个文件选择对话框时,先单击选中刚才新建的.c源文件,此时单击一次“Add”按钮,已经相当于把.c文件添加进工程了,但是此时keil软件并没有自动关闭对话框,这样很容易让初学者误以为.c源文件还没有被添加进去。(2)如果没有以上操作的时候没有发现左边Project窗口,请点击左下角的Project选项来切换。 第七步:双击打开左边被添加进工程的“stc89c52rc.c”.c源文件,就可以在此“stc89c52rc.c”文件下输入我们的C语言代码了,请把以下范例代码复制进去,然后再一次点击”File”选项,在弹出的下拉菜单中选择“Save”保存。此时,新建一个工程的步骤已经完成。供复制的范例代码:#include "REG52.H"
void delay_long(unsigned int uiDelayLong); //延时函数
sbit led_dr=P3^5;
void main()
{
while(1)
{
led_dr=1;//LED亮
delay_long(100); //延时50000个空指令的时间
led_dr=0;//LED灭
delay_long(100); //延时50000个空指令的时间
}
}
void delay_long(unsigned int uiDelayLong) //延时函数
{
unsigned int i;
unsigned int j;
for(i=0;i<uiDelayLong;i++)
{
for(j=0;j<500;j++);//内嵌循环的空指令数量
}
}
有1个地方需要解释: (1)把代码复制到keil4时,中文注释出现乱码怎么办?解决办法如下: 点击左上角"Edit",在下拉菜单中选最后一项“Configuration”,在弹出的对话框中把Encoding的选项改成“Chinese GB2312(Simplified)”.
重新复制一次代码进去就恢复正常了。
第八步:打开一个现成的工程。前面七步已经讲解完了如何新建一个工程,现在教如何打开一个现成的工程。先单击右上角”X”关闭整个keil软件,然后双击桌面”keil uVision4”的图标重新启动keil软件,如果发现此软件默认打开了一个之前已经存在的工程,请先按照前面第三步关闭此工程。然后,点击上面”Project”选项,在弹出的下拉菜单中选择“Open Project...”,在弹出的文件对话框中,找到第一步新建的工程文件夹,单击选中“stc89c52rc.uvproj”这个文件名,然后点击“打开”,就可以打开一个现有的工程文件了。
下节预告:把.c源代码编译成.hex机器码的操作流程。(未完待续)
西北狼
发表于 2015-3-18 10:28:22
通俗易懂,非常适合初学者 顶一个{:soso_e179:}
jianhong_wu
发表于 2015-3-18 10:59:15
本帖最后由 jianhong_wu 于 2015-3-18 14:30 编辑
第六节:把.c源代码编译成.hex机器码的操作流程。 第一步:打开一个现成的工程。双击桌面”keil uVision4”的图标启动keil软件,如果发现此软件默认打开了一个之前已经存在的工程,请点击上面”Project”选项,在弹出的下拉菜单中选择“Close Project”先关闭当前工程。然后,继续点击上面”Project”选项,在弹出的下拉菜单中选择“Open Project...”,在弹出的文件对话框中,在D盘找到上一节已经建立的工程文件夹stc89c52rc,单击选中“stc89c52rc.uvproj”这个文件名,点击“打开”,就可以打开一个现有的工程了。 第二步:设置编译环境让它允许产生.hex格式的机器码文件。鼠标右键点击选中左边”Porject”选项框里面的”Target 1”选项,在右键下拉菜单中选择“Options for Target‘Target 1’...”选项,弹出一个编译环境设置对话框,左键单击上面子菜单切换到“Output”窗口下,把“Create Hex File”勾选上。点击“OK”退出。 有1个地方需要解释:(1)这个选项很重要,必须把“Create Hex File”选项勾上,否则后续的操作不能在工程文件夹的目录里生成.Hex的机器码文件。对于一个工程模板,只需要设置一次就可以保存起来的,下次开电脑重新打开此工程模板时不需要再设置,这些被设置的参数都是能掉电保存起来的。
第三步:启动编译。在确保stc89c52rc.c源文件里面有C语言源代码的情况下,点击上面”Project”选项,在弹出的下拉菜单中点击“Rebuild all target files”编译命令,编译器开始编译工作。 第四步:在”Build Output”窗口下观察编译结果。可以在最下方的”Build Output”窗口下观察到编译的过程提示。如果没有发现”Build Output”窗口,请把鼠标的光标移动到最下方的滑动条下边,当它呈现移动光标的形状时,按住左键往上拖动就可以看到“Build Output”窗口了。当“Build Output”窗口提示显示“creating hex file from "stc89c52rc"..."stc89c52rc" - 0 Error(s), 0 Warning(s).”等信息时,表示翻译工程结束了。其中0 Error(s)代表编译成功,没有任何错误。0 Warning(s)代表没有任何警告。只要有一个错误Error产生,就说明编译不通过。如果没有任何错误Error产生,但是有几个警告Warning产生,在这种情况下很多时候都不影响程序的正常运行,只有少数情况下是会影响代码的正常运行的,因此我本人建议哪怕是一个警告,大家也不要放过它,要找到产生这个警告的原因。查找错误的时候,只需要双击错误提示error那行内容,光标就会自动跳到源代码错误的附近,方便大家寻找语法错误。 最终观察到的Build Output窗口如下: 第五步:编译后生成.hex机器码文件的目录位置。以上编译成功后,我们只要打开电脑D盘的stc89c52rc文件夹,就可以找到.hex扩展名的机器码文件,这个文件就是我们要下载到单片机的机器码文件。 下节预告:利用现有工程模板编译不同项目源代码的方法以及代码备份管理技巧。(未完待续)
zengmiao
发表于 2015-3-18 19:30:58
jianhong_wu 发表于 2015-2-24 14:40
第一节:跟我学单片机到底是学什么?我的两个比喻和一个规则。
开篇第一节,我问大家一个问题,跟我 ...
不错的比喻,值得引用,引用你的思路没关系吧!
jianhong_wu
发表于 2015-3-18 22:32:02
zengmiao 发表于 2015-3-18 19:30
不错的比喻,值得引用,引用你的思路没关系吧!
当然没关系啦。写出来就是跟大家分享的。
jianhong_wu
发表于 2015-3-22 09:52:27
本帖最后由 jianhong_wu 于 2015-3-22 09:55 编辑
第七节:重复利用现有工程模板进行程序开发的方法以及代码备份管理技巧。 是不是每做一个新项目都要新建一个工程?在同一个项目中,是不是每修改一次源代码都要新建一个工程?很多情况下都不用。这节介绍如何重复利用现有工程模板进行程序开发的方法以及代码备份管理技巧。 重复利用现有工程模板,有三个必须。第一个必须是一个源文件的,而不是多文件编程(大家暂时不了解啥叫多文件编程也没关系)。第二个必须是同样的厂家同样的单片机型号。第三个必须进行代码备份管理,每完成一个项目的小进度,都要及时把源代码存储到电脑硬盘里,电脑硬盘里每个项目对应一个项目文件夹,每个项目文件夹里包含很多不同版本编号的源代码文件,每个源代码文件名必须有流水编号,方便识别最新版本的程序,每天下班前都要把最新版本的源代码文件上传到网盘备份,在互联网时代,把源代码存到自己的网盘,可以随时异地存取,即使遇到电脑故障损坏也不担心数据永久丢失。 现在举一个例子来介绍它的操作流程。要修改一个LED项目的源代码,电脑D盘上已经有一个“LED项目”的文件夹,文件夹里已经有一个名称为”LED_1”的源代码文件,这个文件是.txt格式的文本文档,文件名称的后缀_1代表流水编号,要求修改此源代码后,再保存在此文件夹目录下的”LED_2”文本文档里,并且上传到网盘进行备份。 第一步:打开一个现有的keil工程。双击桌面”keil uVision4”的图标启动keil软件,如果发现此软件默认打开了一个之前已经存在的工程,请点击上面”Project”选项,在弹出的下拉菜单中选择“Close Project”先关闭当前工程。然后,继续点击上面”Project”选项,在弹出的下拉菜单中选择“Open Project...”,在弹出的文件对话框中,在D盘目录下找到之前已经建立的工程文件夹stc89c52rc,单击选中“stc89c52rc.uvproj”这个文件名,点击“打开”,就可以打开一个现有的工程了。 第二步:把当前keil工程的全部源代码清空。用Ctrl+A快捷键选中当前工程的全部源代码,按下Backspace退格按键就可以清空当前工程的全部源代码。 第三步:把最新版本的源代码导入到当前的keil工程中。在电脑D盘的“LED项目”文件夹目录下,双击打开“LED_1”的文本文档,用Ctrl+A快捷键选中文本文档的全部源代码,再用Ctrl+C快捷键复制此源代码,切换到keil工程中,把光标移动到工程的源代码编辑区,再用Ctrl+V快捷键粘贴此源代码到keil工程里。以下是复制粘贴到keil工程的源代码:
#include "REG52.H"
void delay_long(unsigned int uiDelayLong); //延时函数
sbit led_dr=P3^5;
void main()
{
while(1)
{
led_dr=1; //LED亮
delay_long(100); //延时50000个空指令的时间
led_dr=0; //LED灭
delay_long(100); //延时50000个空指令的时间
}
}
void delay_long(unsigned int uiDelayLong) //延时函数
{
unsigned int i;
unsigned int j;
for(i=0;i<uiDelayLong;i++)
{
for(j=0;j<500;j++); //内嵌循环的空指令数量
}
}
第四步:在keil工程中修改此源代码。把“led_dr=0;//LED灭”这行代码删掉,修改后变成以下代码:#include "REG52.H"
void delay_long(unsigned int uiDelayLong); //延时函数
sbit led_dr=P3^5;
void main()
{
while(1)
{
led_dr=1; //LED亮
delay_long(100); //延时50000个空指令的时间
delay_long(100); //延时50000个空指令的时间
}
}
void delay_long(unsigned int uiDelayLong) //延时函数
{
unsigned int i;
unsigned int j;
for(i=0;i<uiDelayLong;i++)
{
for(j=0;j<500;j++); //内嵌循环的空指令数量
}
}
第五步:启动编译。点击上面”Project”选项,在弹出的下拉菜单中点击“Rebuild all target files”编译命令,编译结束后显示编译操作成功。
第六步:把在keil工程里修改后的源代码备份到电脑硬盘里。(1)先在D盘的”LED项目”文件夹目录下,点击鼠标右键新建一个文本文档,再右键选中此文本文档图标,重命名为”LED_2”,然后双击打开此文本文档。
(2)切换到keil工程的源代码中,用Ctrl+A快捷键选中keil工程的全部源代码,用Ctrl+C快捷键复制此代码,接着切换回D盘的”LED_2”的文本文档,用Ctrl+V快捷键把修改后的代码粘贴到D盘的”LED_2”的文本文档,并且打开文本文档左上角“文件”的下拉菜单,点击“保存”按钮保存,最后关闭此文本文档。第七步:把"LED_2"文本文档上传到网盘里备份。我本人比较喜欢用115网盘。关于115网盘的操作,大家可以百度搜索“115网盘”。下节预告:把.hex机器码下载到单片机的操作流程。(未完待续)
jianhong_wu
发表于 2015-3-29 20:53:32
本帖最后由 jianhong_wu 于 2015-6-3 18:20 编辑
第八节:把.hex机器码下载到单片机的操作流程。烧录程序也叫下载程序。下载程序的本质是什么?把单片机当做一个存储器,每一条程序指令都对应一个唯一的存储地址,把这些指令一条条存储到指定的存储地址中,这就是下载程序的本质。对于STC89C52RC单片机,在下载程序时需要上位机界面软件和一根USB转串口线。上位机界面软件负责把指定.hex格式的机器码文件打开,.hex格式的机器码文件里面记录着每条程序指令对应的地址信息,在下载过程中,上位机界面软件根据.hex记录的指令内容和对应的地址信息,经过USB转串口线,跟单片机的内置引导程序进行串口通讯,从而把.hex记录的信息传输到单片机内部的flash存储器中,实现了程序的下载。在讲操作流程之前,请读者先把以下一个LED灯闪烁的代码编译成.hex格式的文件,这个.hex文件保存在D盘的”stc89c52rc”文件夹里。#include "REG52.H"
void delay_long(unsigned int uiDelayLong); //延时函数
sbit led_dr=P3^5;
void main()
{
while(1)
{
led_dr=1;//LED亮
delay_long(100); //延时50000个空指令的时间
led_dr=0;//LED亮
delay_long(100); //延时50000个空指令的时间
}
}
void delay_long(unsigned int uiDelayLong) //延时函数
{
unsigned int i;
unsigned int j;
for(i=0;i<uiDelayLong;i++)
{
for(j=0;j<500;j++);//内嵌循环的空指令数量
}
}
下面详细讲解把.hex机器码下载到单片机的操作流程。
第一步:安装USB转串口驱动程序的操作流程。所谓上位机界面软件就是安装在电脑端的界面软件,电脑跟单片机进行通讯,需要一根USB转串口线,欲使USB转串口线正常工作,必须预先安装一个USB转串口的驱动程序。具体的操作是这样的:在网盘中下载”51CTO下载-CH340SER(win7 64位可用).zip”这个压缩包文件,解压后分成“CH341SER”和“INSTALL”这两个文件夹,双击打开“CH341SER”这个文件夹,找到“SETUP.EXE”这个安装应用程序,双击启动,在弹出的界面中,单击“安装”按钮即可完成驱动程序的安装。 第二步:记录串口号。我用的电脑是XP系统,现在以XP系统为例。插入USB转串口线,右击桌面“我的电脑”,选择下拉菜单的“设备管理器”,在弹出的窗口中,点击“端口”前面的+号,在展开的选项中,会看到“USB-SERTAL CH340(COM6)”这个信息,这个COM6就是要我们记住的串口号。你们的串口号不一定是COM6,请以你们电脑显示的串口号为准。 第三步:打开上位机界面软件“STC_ISP”。这个软件可以在宏晶单片机的官网下载获取此软件。双击打开“STC_ISP.exe”这个上位机界面软件。 第四步:选择单片机型号。在“单片机型号”的下拉菜单中选择“STC89C/LE52RC”这个型号。如果中途弹出推荐选用其它型号的窗口,可以按确定忽略它,我们只要认准“STC89C/LE52RC”这个型号就可以了。 第五步:设置串口号。在“串口号”的下拉菜单中,选择跟前面第二步所记录一样的串口号。 第六步:设置最高波特率。在“最高波特率”的下拉菜单中,选择9600波特率。 第七步:连接硬件USB转串口线和电源线。USB转串口线一端已经连接电脑USB口,另外一端9针串口端跟坚鸿51学习板的串口端连接。电源线一端用智能手机充电器的USB端口供电5V,电源线另一端连接坚鸿51学习板的USB供电端口。
第八步:导入.hex格式的机器码文件。点击上位机界面软件的“打开程序文件”的按钮,在弹出的对话框中,选择D盘下“stc89c52rc”文件夹目录下的“stc89c52rc.hex”,双击把“stc89c52rc.hex”导入到上位机界面软件。 第九步:启动下载。点击上位机界面软件的“下载/编程”的按钮,发现“正在检测目标单片机..”的提示信息,此时需要把51学习板重新断电后再上电,很多人也把这个重新上电的过程称为“冷启动”。 第十步:“冷启动”后观察是否操作成功的信息。执行完前面第九步的“冷启动”后,如果发现有“...操作成功!”的提示信息,就说明下载成功了。 第十一步:坚鸿51学习板下载程序失败时的解决办法。(1)可以先松一下卡座,稍微挪动一下单片机,然后再卡紧单片机。卡座必须卡紧单片机, 避免接触不良。(2)改变供电电源,很多电脑的USB口供电电源干扰非常大,严重影响下载程序,请把USB电源线插入到手机充电器5V的USB接口,效果显著,明显提高了下载的成功率。(3)检查确保选择单片机型号是STC89C/LE52RC,如果软件弹出推荐其它型号的单片机窗口,不用管它,我们就选STC89C/LE52RC。(4)检查STC-ISP烧写软件是否选择匹配的COM口。 (5)单片机是靠串口烧录程序进去的,单片机的串口是P3.0,P3.1两根线,在烧录程序时,确保P3.0,P3.1这两根线的黄颜色跳帽必须插上,同时P3.0,P3.1两个IO口不能跳线到外围器件上。(6)点击“下载/编程”后,记得再断电并且重新上电一次。看看是否烧录成功。(7)最低波特率一直设置为2400,然后把最高波特率先改成9600试一下,如果还不行再把最高波特率改成2400试试。(8)如果还不行,就退出软件,拔掉USB转串口线,同时断电(必须把整根电源线拔掉!),重新插入USB串口线,重新插入电源线开电,重新打开软件。(9)如果还不行,学习板先断电(必须把整根电源线拔掉!),然后重启一次电脑。(10)总之:如果还不行,就按上述步骤多折腾几次。最后实在不行,就尝试更换到其它USB口,或者尝试更换到其它电脑上试试。
下节预告:主程序的两个区域:初始化和循环。(未完待续)
jianhong_wu
发表于 2015-3-31 17:58:09
本帖最后由 jianhong_wu 于 2015-6-3 18:21 编辑
第九节:程序从哪里开始,要到哪里去?程序从哪里开始,要到哪里去?为了让初学者了解C语言程序的执行顺序,我把程序分成三个区域:进入主程序前的区域,主程序的初始化区域,主程序的循环区域。进入主程序前的区域。这是上电后,在单片机执行主程序代码之前就已经完成了的工作。包括头文件的包含,宏定义,内存分配这些工作。这部分的内容可以暂时不用去了解,我会在后面的一些章节中陆续深入讲解。主程序的初始化区域。这是上电后,单片机进入主程序后马上就要执行的程序代码,这部分区域的代码有一个特点,大家也必须记住的,就是单片机只执行一次。只要单片机不重启,不复位,那么上电后这部分的代码只被执行一次。主程序的循环区域。单片机在主程序中执行完了初始化区域的代码,紧接着就进入这片循环区域的代码。单片机一直在循环执行这段代码,这就是上电后单片机的最终归宿,一直处在循环的状态。下面我跟大家分析一个程序源代码的三个区域和执行顺序,大家先看中文解释部分的内容,暂时不用理解每行指令的语法。该源代码实现的功能是:上电后,蜂鸣器鸣叫一声就停止,然后看到一个LED灯一直在闪烁。本程序是基于坚鸿51单片机学习板。
#include "REG52.H"//进入主程序前的区域:头文件包含
sbit beep_dr=P2^7;//进入主程序前的区域:宏定义
sbit led_dr=P3^5; //进入主程序前的区域:宏定义
unsigned long i; //进入主程序前的区域:内存分配
void main() //主程序入口,即将进入初始化区域
{
beep_dr=0; //第一步:初始化区域:蜂鸣器开始鸣叫。
for(i=0;i<6250;i++); //第二步:初始化区域:延时0.5秒左右。也就是蜂鸣器鸣叫的持续时间。
beep_dr=1; //第三步:初始化区域:蜂鸣器停止鸣叫。
while(1) //执行完上面的初始化区域,即将进入循环区域
{
led_dr=1; //第四步:循环区域:LED开始点亮。
for(i=0;i<6250;i++); //第五步:循环区域:延时0.5秒左右。也就是LED点亮的持续时间。
led_dr=0;//LED灭 //第六步:循环区域:LED开始熄灭。
for(i=0;i<6250;i++); //第七步:循环区域:延时0.5秒左右。也就是LED熄灭的持续时间。马上返回上面第四步继续循环往下执行。
}
}
//解释:
//单片机进入主程序后,第一步到第三步是属于初始化区域,只被执行一次。然后进入循环区域,从第四步执行到第七步,
//执行完第七步之后,马上返回上面第四步继续循环往下执行,单片机一直处于第四步到第七步的循环区域中。
经过以上的分析,可以看出这三个区域的大概分布如下:
//...进入主程序前的区域void main() { //...初始化区域 while(1) { //...循环区域 }}
下节预告:一个用来学习C语言的模板程序。(未完待续)
jianhong_wu
发表于 2015-4-2 13:10:45
本帖最后由 jianhong_wu 于 2015-4-2 13:19 编辑
第十节:一个用来学习C语言的模板程序。目前,几乎所有的初学者在学习和上机练习C语言的时候,都是在电脑上安装VC这个调试软件,在源代码里只要调用打印语句printf就可以观察到不同的变量结果,挺方便的。但是现在我要提出另外一种方法,学习单片机的C语言,不一定非要用VC调试软件,也可以直接在坚鸿51学习板上学习和上机练习的。我可以做一个调试模板程序给初学者使用,利用8位数码管和16个LED灯来显示不同的变量结果,利用3个按键来切换显示不同的变量,这样就能达到类似在VC平台下用printf语句来观察变量的效果。甚至我个人认为这样比用VC调试的效果还更加直观。现在重点介绍这个模板程序的使用。在模板程序里,初学者只需要在主程序的初始化区域填入自己练习的C语言代码,最后把需要观察的变量赋值给窗口变量就可以了,其它部分的代码属于模板的监控调试代码,大家暂时不用读懂它,直接复制过来就可以了。上述所谓的“赋值”,就是“=”这个语句,它表面上像我们平时用的等于号,实际上不是等于号,而是代表“给”的意思,把“=”符号右边的数复制一份给左边的变量,比如“a=36;”就是代表把36这个数值复制一份给变量a,执行这条指令后,a就等于36了。这里的分号“;”代表一条程序指令的结束。窗口变量有几个?有哪些?一共有10个,分别是GuiWdData0,GuiWdData1,GuiWdData2,GuiWdData3,GuiWdData4,GuiWdData5,GuiWdData6,GuiWdData7,GuiWdData8,GuiWdData9。这10个窗口变量是给大家调试专用的,8位数码管可以切换显示10个窗口变量,最左边2位数码管代表窗口变量号,剩下6位数码管显示十进制的窗口变量数值,另外16个LED实时显示此数据的二进制格式。最左边2位数码管从“0-”到“9-”代表从第0个窗口变量到第9个窗口变量,也就是GuiWdData0依次到GuiWdData9。用S1和S5按键可以切换显示不同的窗口变量,按住S9不放可以观察到当前窗口变量的十六进制格式数据,松开S9按键后,又自动返回显示当前窗口变量的十进制数据。该模板程序是基于坚鸿51学习板,现在跟大家分享这个程序,要让这10个窗口变量分别显示10,11,12,13,14,15,16,17,18,19这10个数,用S1按键可以切换显示从小往大的窗口变量号,用S5按键可以切换显示从大往小的窗口变量号。再强调一次,大家只需要关注主程序main函数的初始化区域就可以了,其它的代码请直接复制过来,不用理解。比如: void main()//主程序 { //...初始化区域 while(1) { }} 详细的源代码如下: #include "REG52.H"
#define const_voice_short40
#define const_key_time120
#define const_key_time220
#define const_key_time320
void initial(void);
void delay_short(unsigned int uiDelayShort);
void dig_hc595_drive(unsigned char ucDigStatusTemp16_09,unsigned char ucDigStatusTemp08_01);
void display_drive(void);
void display_service(void);
void hc595_drive(unsigned char ucLedStatusTemp16_09,unsigned char ucLedStatusTemp08_01);
void T0_time(void);
void key_service(void);
void key_scan(void);
sbit beep_dr=P2^7;
sbit key_sr1=P0^0;
sbit key_sr2=P0^1;
sbit key_sr3=P0^2;
sbit key_gnd_dr=P0^4;
sbit led_dr=P3^5;
sbit dig_hc595_sh_dr=P2^0;
sbit dig_hc595_st_dr=P2^1;
sbit dig_hc595_ds_dr=P2^2;
sbit hc595_sh_dr=P2^3;
sbit hc595_st_dr=P2^4;
sbit hc595_ds_dr=P2^5;
unsigned char GucKeySec=0;
unsigned char GucKey3Sr=1;
unsigned intGuiVoiceCnt=0;
unsigned char GucVoiceStart=0;
unsigned char GucDigShow8;
unsigned char GucDigShow7;
unsigned char GucDigShow6;
unsigned char GucDigShow5;
unsigned char GucDigShow4;
unsigned char GucDigShow3;
unsigned char GucDigShow2;
unsigned char GucDigShow1;
unsigned char GucDisplayUpdate=1;
unsigned char GucWd=0;
unsigned int GuiWdData0=0;
unsigned int GuiWdData1=0;
unsigned int GuiWdData2=0;
unsigned int GuiWdData3=0;
unsigned int GuiWdData4=0;
unsigned int GuiWdData5=0;
unsigned int GuiWdData6=0;
unsigned int GuiWdData7=0;
unsigned int GuiWdData8=0;
unsigned int GuiWdData9=0;
code unsigned char dig_table[]=
{
0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f,0x77,0x7c,0x39,0x5e,0x79,0x71,0x00,0x40,
};
void main() //主程序
{
/*---C语言学习区域的开始---------------------------------------------------------------------------*/
GuiWdData0=10; //把10这个数值放到窗口变量0里面显示
GuiWdData1=11; //把11这个数值放到窗口变量1里面显示
GuiWdData2=12; //把12这个数值放到窗口变量2里面显示
GuiWdData3=13; //把13这个数值放到窗口变量3里面显示
GuiWdData4=14; //把14这个数值放到窗口变量4里面显示
GuiWdData5=15; //把15这个数值放到窗口变量5里面显示
GuiWdData6=16; //把16这个数值放到窗口变量6里面显示
GuiWdData7=17; //把17这个数值放到窗口变量7里面显示
GuiWdData8=18; //把18这个数值放到窗口变量8里面显示
GuiWdData9=19; //把19这个数值放到窗口变量9里面显示
/*---C语言学习区域的结束---------------------------------------------------------------------------*/
while(1)
{
initial();
key_service();
display_service();
}
}
void display_service(void)
{
static unsigned char SucLedStatus16_09=0;
static unsigned char SucLedStatus08_01=0;
static unsigned intSinWdDataTemp=0;
if(1==GucDisplayUpdate)
{
GucDisplayUpdate=0;
switch(GucWd)
{
case 0:
GucDigShow8=0;
SinWdDataTemp=GuiWdData0;
break;
case 1:
GucDigShow8=1;
SinWdDataTemp=GuiWdData1;
break;
case 2:
GucDigShow8=2;
SinWdDataTemp=GuiWdData2;
break;
case 3:
GucDigShow8=3;
SinWdDataTemp=GuiWdData3;
break;
case 4:
GucDigShow8=4;
SinWdDataTemp=GuiWdData4;
break;
case 5:
GucDigShow8=5;
SinWdDataTemp=GuiWdData5;
break;
case 6:
GucDigShow8=6;
SinWdDataTemp=GuiWdData6;
break;
case 7:
GucDigShow8=7;
SinWdDataTemp=GuiWdData7;
break;
case 8:
GucDigShow8=8;
SinWdDataTemp=GuiWdData8;
break;
case 9:
GucDigShow8=9;
SinWdDataTemp=GuiWdData9;
break;
}
GucDigShow7=17;
GucDigShow6=16;
if(1==GucKey3Sr)
{
if(SinWdDataTemp>=10000)
{
GucDigShow5=SinWdDataTemp/10000;
}
else
{
GucDigShow5=16;
}
if(SinWdDataTemp>=1000)
{
GucDigShow4=SinWdDataTemp%10000/1000;
}
else
{
GucDigShow4=16;
}
if(SinWdDataTemp>=100)
{
GucDigShow3=SinWdDataTemp%1000/100;
}
else
{
GucDigShow3=16;
}
if(SinWdDataTemp>=10)
{
GucDigShow2=SinWdDataTemp%100/10;
}
else
{
GucDigShow2=16;
}
GucDigShow1=SinWdDataTemp%10;
}
else
{
GucDigShow5=16;
if(SinWdDataTemp>=0x1000)
{
GucDigShow4=SinWdDataTemp/0x1000;
}
else
{
GucDigShow4=16;
}
if(SinWdDataTemp>=0x0100)
{
GucDigShow3=SinWdDataTemp%0x1000/0x0100;
}
else
{
GucDigShow3=16;
}
if(SinWdDataTemp>=0x0010)
{
GucDigShow2=SinWdDataTemp%0x0100/0x0010;
}
else
{
GucDigShow2=16;
}
GucDigShow1=SinWdDataTemp%0x0010;
}
SucLedStatus16_09=SinWdDataTemp>>8;
SucLedStatus08_01=SinWdDataTemp;
hc595_drive(SucLedStatus16_09,SucLedStatus08_01);
}
}
void key_scan(void)
{
static unsigned intSuiKeyTimeCnt1=0;
static unsigned char SucKeyLock1=0;
static unsigned intSuiKeyTimeCnt2=0;
static unsigned char SucKeyLock2=0;
static unsigned intSuiKey3Cnt1=0;
static unsigned intSuiKey3Cnt2=0;
if(1==key_sr1)
{
SucKeyLock1=0;
SuiKeyTimeCnt1=0;
}
else if(0==SucKeyLock1)
{
SuiKeyTimeCnt1++;
if(SuiKeyTimeCnt1>const_key_time1)
{
SuiKeyTimeCnt1=0;
SucKeyLock1=1;
GucKeySec=1;
}
}
if(1==key_sr2)
{
SucKeyLock2=0;
SuiKeyTimeCnt2=0;
}
else if(0==SucKeyLock2)
{
SuiKeyTimeCnt2++;
if(SuiKeyTimeCnt2>const_key_time2)
{
SuiKeyTimeCnt2=0;
SucKeyLock2=1;
GucKeySec=2;
}
}
if(1==key_sr3)
{
SuiKey3Cnt1=0;
SuiKey3Cnt2++;
if(SuiKey3Cnt2>const_key_time3)
{
SuiKey3Cnt2=0;
GucKey3Sr=1;
}
}
else
{
SuiKey3Cnt2=0;
SuiKey3Cnt1++;
if(SuiKey3Cnt1>const_key_time3)
{
SuiKey3Cnt1=0;
GucKey3Sr=0;
}
}
}
void key_service(void)
{
static unsigned char SucKey3SrRecord=1;
if(GucKey3Sr!=SucKey3SrRecord)
{
SucKey3SrRecord=GucKey3Sr;
GucDisplayUpdate=1;
}
switch(GucKeySec)
{
case 1:
GucWd++;
if(GucWd>9)
{
GucWd=9;
}
GucDisplayUpdate=1;
GuiVoiceCnt=const_voice_short;
GucVoiceStart=1;
GucKeySec=0;
break;
case 2:
GucWd--;
if(GucWd>9)
{
GucWd=0;
}
GucDisplayUpdate=1;
GuiVoiceCnt=const_voice_short;
GucVoiceStart=1;
GucKeySec=0;
break;
}
}
void display_drive()
{
static unsigned char SucDigShowTemp=0;
static unsigned char SucDisplayDriveStep=1;
switch(SucDisplayDriveStep)
{
case 1:
SucDigShowTemp=dig_table;
dig_hc595_drive(SucDigShowTemp,0xfe);
break;
case 2:
SucDigShowTemp=dig_table;
dig_hc595_drive(SucDigShowTemp,0xfd);
break;
case 3:
SucDigShowTemp=dig_table;
dig_hc595_drive(SucDigShowTemp,0xfb);
break;
case 4:
SucDigShowTemp=dig_table;
dig_hc595_drive(SucDigShowTemp,0xf7);
break;
case 5:
SucDigShowTemp=dig_table;
dig_hc595_drive(SucDigShowTemp,0xef);
break;
case 6:
SucDigShowTemp=dig_table;
dig_hc595_drive(SucDigShowTemp,0xdf);
break;
case 7:
SucDigShowTemp=dig_table;
dig_hc595_drive(SucDigShowTemp,0xbf);
break;
case 8:
SucDigShowTemp=dig_table;
dig_hc595_drive(SucDigShowTemp,0x7f);
break;
}
SucDisplayDriveStep++;
if(SucDisplayDriveStep>8)
{
SucDisplayDriveStep=1;
}
}
void dig_hc595_drive(unsigned char ucDigStatusTemp16_09,unsigned char ucDigStatusTemp08_01)
{
unsigned char i;
unsigned char ucTempData;
dig_hc595_sh_dr=0;
dig_hc595_st_dr=0;
ucTempData=ucDigStatusTemp16_09;
for(i=0;i<8;i++)
{
if(ucTempData>=0x80)dig_hc595_ds_dr=1;
else dig_hc595_ds_dr=0;
dig_hc595_sh_dr=0;
delay_short(1);
dig_hc595_sh_dr=1;
delay_short(1);
ucTempData=ucTempData<<1;
}
ucTempData=ucDigStatusTemp08_01;
for(i=0;i<8;i++)
{
if(ucTempData>=0x80)dig_hc595_ds_dr=1;
else dig_hc595_ds_dr=0;
dig_hc595_sh_dr=0;
delay_short(1);
dig_hc595_sh_dr=1;
delay_short(1);
ucTempData=ucTempData<<1;
}
dig_hc595_st_dr=0;
delay_short(1);
dig_hc595_st_dr=1;
delay_short(1);
dig_hc595_sh_dr=0;
dig_hc595_st_dr=0;
dig_hc595_ds_dr=0;
}
void hc595_drive(unsigned char ucLedStatusTemp16_09,unsigned char ucLedStatusTemp08_01)
{
unsigned char i;
unsigned char ucTempData;
hc595_sh_dr=0;
hc595_st_dr=0;
ucTempData=ucLedStatusTemp16_09;
for(i=0;i<8;i++)
{
if(ucTempData>=0x80)hc595_ds_dr=1;
else hc595_ds_dr=0;
hc595_sh_dr=0;
delay_short(1);
hc595_sh_dr=1;
delay_short(1);
ucTempData=ucTempData<<1;
}
ucTempData=ucLedStatusTemp08_01;
for(i=0;i<8;i++)
{
if(ucTempData>=0x80)hc595_ds_dr=1;
else hc595_ds_dr=0;
hc595_sh_dr=0;
delay_short(1);
hc595_sh_dr=1;
delay_short(1);
ucTempData=ucTempData<<1;
}
hc595_st_dr=0;
delay_short(1);
hc595_st_dr=1;
delay_short(1);
hc595_sh_dr=0;
hc595_st_dr=0;
hc595_ds_dr=0;
}
void T0_time(void) interrupt 1
{
TF0=0;
TR0=0;
if(1==GucVoiceStart)
{
if(GuiVoiceCnt!=0)
{
GuiVoiceCnt--;
beep_dr=0;
}
else
{
beep_dr=1;
GucVoiceStart=0;
}
}
key_scan();
display_drive();
TH0=0xfe;
TL0=0x0b;
TR0=1;
}
void delay_short(unsigned int uiDelayShort)
{
static unsigned int i;
for(i=0;i<uiDelayShort;i++);
}
void initial(void)
{
static unsigned char SucInitialLock=0;
if(0==SucInitialLock)
{
SucInitialLock=1;
key_gnd_dr=0;
led_dr=0;
beep_dr=1;
TMOD=0x01;
TH0=0xfe;
TL0=0x0b;
EA=1;
ET0=1;
TR0=1;
}
}
下节预告:三种类型变量的定义与赋值语句。(未完待续)
西北狼
发表于 2015-4-2 21:58:08
如果能附上坚鸿51学习板相关电路图,对照学习,会更好
jianhong_wu
发表于 2015-4-3 15:20:29
西北狼 发表于 2015-4-2 21:58
如果能附上坚鸿51学习板相关电路图,对照学习,会更好
你这个建议很好。我马上附上原理图。
fkddzm
发表于 2015-4-3 22:16:59
这个帖子的一楼就有坚鸿51学习板原理图(第三版)。
fkddzm
发表于 2015-4-3 22:18:15
非常精彩,期待后续。。。
jianhong_wu
发表于 2015-4-7 09:31:11
本帖最后由 jianhong_wu 于 2015-6-3 18:23 编辑
第十一节:变量的定义与赋值语句。 写程序到底是写什么?我用七个字概括是:对象之间的行为。假设以下a,b,c,d,e.这些都是对象,那么程序往往是对象之间的以下这些行为: (1)把某个数值赋值给对象a。 (2)把对象b赋值给对象a。 (3)把对象b与对象c运算的结果赋值给对象a。 (4)如果对象d等于某个数值,则把某个数值赋值给对象a。 (5)如果对象d等于某个数值,则把对象b赋值给对象a。 (6)如果对象d等于某个数值,则把对象b与对象c运算的结果赋值给对象a。 (7)如果对象d等于对象e,则把某个数值赋值给对象a。 (8)如果对象d等于对象e,则把对象b赋值给对象a。 (9)如果对象d等于对象e,则把对象b与对象c运算的结果赋值给对象a。 (10)...等等,不一一列举。 从上述可以看出,程序的两个要素是:对象和行为。如果把对象看作是单片机的RAM数据存储器,那么行为就是单片机的ROM程序存储器。如果把对象看作是变量,那么行为就是指令语句。本节标题“变量的定义与赋值语句”,其中“变量的定义”就是对象,“赋值语句”就是行为。 变量的定义。一个程序最大允许有多少个对象,是由数据存储器RAM的字节数决定的(字节是一种单位,后面章节会讲到)。stc89c52rc这个单片机有几百个字节的RAM,但是并不意味着程序就一定要全部占用这些RAM。程序需要占用多少RAM,完全是根据程序的实际情况来决定,需要多少就申请多少。这里的“对象”就是变量。这里的“申请”就是变量的定义。 定义变量的关键字。常用有3种容量的变量,每种变量的取值范围不一样。第一种是”unsigned char”变量,取值范围从0到255,占用RAM一个字节,比喻成一房一厅。第二种是”unsigned int”变量,取值范围从0到65535,占用RAM两个字节,比喻成两房一厅。第三种是“unsigned long”变量,取值范围从0到4294967295,占用RAM三个字节,比喻成三房一厅。unsigned char,unsigned int和unsigned long都是定义变量的关键字。 定义变量的语法格式。定义变量的语法格式由3部分组成:关键字,变量名,分号。比如: unsigned char a; 其中unsigned char就是关键字,a就是变量名,分号”;”就是一条语句的结束符号。 变量名的命名规则。变量名的第一个字符不能是数字,必须是字母或者下划线,字母或者下划线后面可以带数字,一个变量名之间的字符不能带空格。变量名不能跟编译器的关键字重名,不能跟函数名重名。比如: unsigned char 3a; //不合法,第一个字符不能是数字。 unsigned char char; //不合法,char是编译器的关键字。 unsigned char a b; //不合法,ab是一个变量名,a与b的中间不能有空格。 unsigned char a; //合法。 unsigned char abc; //合法。 unsigned char _ab; //合法。 unsigned char _3ab; //合法。 unsigned char a123; //合法。 unsigned char a12ced; //合法。 定义变量与RAM的内在关系。当我们定义一个变量时,相当于向单片机申请了一个RAM空间。C编译器会自动为这个变量名分配一个RAM空间,每个字节的RAM空间都有一个固定的地址。把每个字节的RAM空间比喻成 房间,这个地址就是房号。地址是纯数字编号,不利于我们记忆,C语言编译器为了降低我们的工作难度,不用我们记每个变量的地址,只需要记住这个变量的名称就可以了。操作某个变量名,就相当于操作到对应地址的RAM空间。变量名与对应地址RAM空间的映射关系是C编译器暗中帮我们做好了。比如: unsigned char a;//a占用一个字节的RAM空间,这个空间的地址由C编译自动分配。 unsigned char b;//b占用一个字节的RAM空间,这个空间的地址由C编译自动分配。 unsigned char c;//c占用一个字节的RAM空间,这个空间的地址由C编译自动分配。 上述a,b,c三个变量名占用一个字节的RAM空间,同时被C编译器分配了3个不同的RAM空间地址。 赋值语句的含义。赋值语句是行为。把右边对象的内容复制一份给左边对象。 赋值语句有一个很重要的特性,就是覆盖性,左边对象原来的内容会被右边对象复制过来的新内容所覆盖。比如,左边对象是变量a,原来a里面存的数据是3,右边对象是立即数6,执行赋值语句后,把6赋值给了对象a,那么a原来的数据3就被覆盖丢失了,变成了6.。赋值语句的格式。赋值语句的语法格式由4部分组成:左边对象,关键字,右边对象,分号。比如: a=b; 其中a就是左边对象。 其中“=”就是关键字。写法跟我们平时用的等于号是一样,但是在C语言里不是等于的意思,而是代表赋值的意思。跟等于号是两码事。 其中b就是右边对象。 其中分号“;”代表一条语句的结束符。 赋值语句与ROM的内在关系。赋值语句是行为,凡是程序的行为指令都存储在单片机的ROM区。C编译器会把一条赋值语句翻译成对应的一条或者几条机器码,机器码指令也是以字节为单位的。下载程序的时候,这些机器码就会被下载进单片机的ROM区。比如以下这行赋值语句: a=b; 经过C编译器编译后会生成以字节为单位的机器码。这些机器码记录着这些信息:变量a的RAM地址,变量b的RAM地址,以及把b变量RAM地址里面的内容赋值到a变量地址里面的RAM空间。 变量定义的初始化。讲了赋值语句之后,再回过头来讲变量定义的初始化。变量定义之后,等于被C编译器分配了一个RAM空间,那么这个空间里面存储的数据是什么?如果没有刻意给它初始化,那么RAM空间里面存储的数据是不太确定的,是默认的。有些场合,需要在给变量分配RAM空间时就给它一个固定的初始值,这就是变量定义的初始化。变量初始化的语法格式由3部分组成:关键字,变量名赋值,分号。比如: unsigned char a=9; 其中unsigned char就是关键字。 其中a=9就是变量名赋值。a从被C编译器分配RAM空间那一刻起,就默认是存了9这个数据。 分号”;”就是一条语句的结束符号。 接下来练习一个程序实例。直接复制前面章节中第十节的模板程序,只需要在main函数里编写练习代码,编译后,把程序下载进坚鸿51学习板,通过按S1或者S5按键即可在数码管上观察不同的变量数值。其它部分的模板程序代码就不贴出来了,详细的main函数源代码讲解如下:void main() //主程序
{
/*---C语言学习区域的开始---------------------------------------------------------------------------*/
unsigned char a; //定义一个变量a,并且分配了一个字节的RAM空间,里面保存的数据是默认值0.
unsigned char b; //定义一个变量b,并且分配了一个字节的RAM空间,里面保存的数据是默认值0.
unsigned char c; //定义一个变量c,并且分配了一个字节的RAM空间,里面保存的数据是默认值0.
unsigned char d=9; //定义一个变量d,并且分配了一个字节的RAM空间,里面保存的数据被初始化成9.
b=3;//把3赋值给变量b,b原来的默认数据是0被覆盖了,此时变量b保存的数值是3
c=b;//把右边变量b的内容复制一份赋值给左边的变量c,c原来的默认数据0被覆盖了,此时,c保存的数值跟b的数值一样,都是3.
GuiWdData0=a; //把变量a这个数值放到窗口变量0里面显示
GuiWdData1=b; //把变量b这个数值放到窗口变量1里面显示
GuiWdData2=c; //把变量c这个数值放到窗口变量2里面显示
GuiWdData3=d; //把变量d这个数值放到窗口变量3里面显示
/*---C语言学习区域的结束---------------------------------------------------------------------------*/
while(1)
{
initial();
key_service();
display_service();
}
}
上坚鸿51学习板观察程序执行的结果: 变量a的数值是0, 变量b的数值是3, 变量c的数值是3, 变量d的数值是9,
下节预告:两个变量的数据交换。(未完待续)
f晨星
发表于 2015-4-7 17:53:00
jianhong_wu 发表于 2015-4-2 13:10
第十节:一个用来学习C语言的模板程序。目前,几乎所有的初学者在学习和上机练习C语言的时候,都是在电脑上 ...
没有注释啊,:shutup: