[51单片机] 从业十年,教你单片机入门基础。(连载)

[复制链接]
59242|221
zhang891212 发表于 2015-3-16 16:10 | 显示全部楼层
讲的很不错!!!!!
秋风式街球 发表于 2015-3-16 23:22 | 显示全部楼层
吴大哥你谦卑啊,大赞一个!
 楼主| jianhong_wu 发表于 2015-3-17 15:37 | 显示全部楼层
本帖最后由 jianhong_wu 于 2015-3-17 15:52 编辑

第五节:用keil软件新建,关闭,打开一个完整工程的操作流程。
     Keil平台软件的安装我就不多讲了,网上这方面的资料很多,大家可以百度一下如何安装keil的教程。下面开始讲解用keil软件新建,关闭,打开一个完整工程的操作流程。
     第一步:新建一个工程文件夹。先在电脑D盘目录下新建一个文件夹,取名为“stc89c52rc”。
     
     有2个地方需要解释:
1)文件夹以及后面所取的文件名不要用中文,请全部用英文,数字,或者下划线这些字符。keil软件支不支持中文名无所谓,但是在单片机这个行业,有一些单片机厂家的平台软件,某些版本是不支持中文名的,所以大家养成这个习惯,以后可以避免遇到一些不必要的麻烦。
2)新建的文件夹请直接放在某盘的根目录下,而不要放到某个已有文件夹的目录下。一方面是因为已有的文件名往往带有中文字,另外一方面是有一些单片机厂家的平台软件不支持嵌入层次太深的文件目录,所以大家养成这个习惯,以后可以避免遇到一些不必要的麻烦。

第二步:启动keil软件。双击桌面”keil uVision4”的图标启动keil软件。
第三步:关闭默认被打开的已有工程。打开keil软件时,如果发现此软件默认打开了一个之前已经存在的工程,请先关闭此工程。如果默认没有打开已有工程,这一步可以忽略跳过。关闭已有工程的操作是这样子的:点击上面”Project”选项,在弹出的下拉菜单中选择“Close Project”即可。
第四步:利用工具向导新建一个工程。点击上面”Project”选项,在弹出的下拉菜单中选择“new  uVision Project...”,在弹出的对话框中,选择保存的目录是刚才第一步新建的文件夹“stc89c52rc”目录下,输入跟文件夹名称一样的文件名“stc89c52rc”,然后单击“保存”按键,此时会弹出一个选择单片机型号的对话框,双击”Atmel”这个厂家,在展开的下拉选项中选中“AT89C52”这个型号,然后点击“OK”,此时会弹出一个英文询问框“是否要复制STARTUP.A51这个文件到工程里?”我们单击“否”即可。
     有3个地方需要解释:
(1)以上新建的保存文件名应该跟我们第一步在D盘新建的文件夹名称一致,因为有一些单片机厂家的平台软件是有这个要求的,所以大家养成这个习惯,以后可以避免遇到一些不必要的麻烦。
(2)上面之所以选择Atmel厂家的AT89C52单片机,是因为朱兆祺51学习板所用的单片机是STC89C52RC这个单片机,而STC89C52RCAT89C52是兼容的。
(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”保存。此时,新建一个工程的步骤已经完成。
供复制的范例代码:
  1. #include "REG52.H"

  2. void delay_long(unsigned int uiDelayLong); //延时函数

  3. sbit led_dr=P3^5;  

  4. void main()  
  5. {
  6.    while(1)
  7.    {
  8.        led_dr=1;  //LED亮
  9.        delay_long(100);    //延时50000个空指令的时间
  10.        led_dr=0;  //LED灭
  11.        delay_long(100);    //延时50000个空指令的时间
  12.    }
  13. }

  14. void delay_long(unsigned int uiDelayLong) //延时函数
  15. {
  16.    unsigned int i;
  17.    unsigned int j;
  18.    for(i=0;i<uiDelayLong;i++)
  19.    {
  20.       for(j=0;j<500;j++);  //内嵌循环的空指令数量
  21.    }
  22. }

1个地方需要解释:
(1)把代码复制到keil4时,中文注释出现乱码怎么办?解决办法如下:
     点击左上角"Edit",在下拉菜单中选最后一项“Configuration”,在弹出的对话框中把Encoding的选项改成“Chinese GB2312(Simplified)”.
    重新复制一次代码进去就恢复正常了。
第八步:打开一个现成的工程。前面七步已经讲解完了如何新建一个工程,现在教如何打开一个现成的工程。先单击右上角”X”关闭整个keil软件,然后双击桌面”keil uVision4”的图标重新启动keil软件,如果发现此软件默认打开了一个之前已经存在的工程,请先按照前面第三步关闭此工程。然后,点击上面”Project”选项,在弹出的下拉菜单中选择“Open Project...”,在弹出的文件对话框中,找到第一步新建的工程文件夹,单击选中“stc89c52rc.uvproj”这个文件名,然后点击“打开”,就可以打开一个现有的工程文件了。
下节预告:把.c源代码编译成.hex机器码的操作流程。
(未完待续)



本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?注册

×
yuanquan12345 发表于 2015-3-18 07:52 | 显示全部楼层
吴老师辛苦了。
 楼主| jianhong_wu 发表于 2015-3-18 14:21 | 显示全部楼层
本帖最后由 jianhong_wu 于 2015-3-18 14:24 编辑

第六节:把.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扩展名的机器码文件,这个文件就是我们要下载到单片机的机器码文件。
下节预告:利用现有工程模板编译不同项目源代码的方法以及代码备份管理技巧。
(未完待续)







本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?注册

×

评分

参与人数 2威望 +2 收起 理由
sanxicool + 1 赞一个!
波板糖 + 1 很给力!

查看全部评分

WKK007 发表于 2015-3-18 14:32 | 显示全部楼层
正在学习,顶一个..
skysky01 发表于 2015-3-18 18:02 | 显示全部楼层
坚决保存
...
18068011934 发表于 2015-3-18 20:00 | 显示全部楼层
坐等更新,幸苦啦
松翰ic软件开发 发表于 2015-3-18 20:42 | 显示全部楼层
好东西,就是要分享,谢谢啦.
glpstanley 发表于 2015-3-18 22:29 | 显示全部楼层
很好的**,很期待大作横空出世。
波板糖 发表于 2015-3-19 09:22 | 显示全部楼层
很系统,很适合学习
老老实实 发表于 2015-3-20 11:59 | 显示全部楼层
对于初学者,真的是福音。写得很用心,赞一个
 楼主| jianhong_wu 发表于 2015-3-22 10:23 | 显示全部楼层
第七节:重复利用现有工程模板进行程序开发的方法以及代码备份管理技巧。
    是不是每做一个新项目都要新建一个工程?在同一个项目中,是不是每修改一次源代码都要新建一个工程?很多情况下都不用。这节介绍如何重复利用现有工程模板进行程序开发的方法以及代码备份管理技巧。
    重复利用现有工程模板,有三个必须。第一个必须是一个源文件的,而不是多文件编程(大家暂时不了解啥叫多文件编程也没关系)。第二个必须是同样的厂家同样的单片机型号。第三个必须进行代码备份管理,每完成一个项目的小进度,都要及时把源代码存储到电脑硬盘里,电脑硬盘里每个项目对应一个项目文件夹,每个项目文件夹里包含很多不同版本编号的源代码文件,每个源代码文件名必须有流水编号,方便识别最新版本的程序,每天下班前都要把最新版本的源代码文件上传到网盘备份,在互联网时代,把源代码存到自己的网盘,可以随时异地存取,即使遇到电脑故障损坏也不担心数据永久丢失。
    现在举一个例子来介绍它的操作流程。要修改一个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工程的源代码:

  1. #include "REG52.H"

  2. void delay_long(unsigned int uiDelayLong); //延时函数

  3. sbit led_dr=P3^5;

  4. void main()
  5. {
  6. while(1)
  7. {
  8. led_dr=1; //LED亮
  9. delay_long(100); //延时50000个空指令的时间
  10. led_dr=0; //LED灭
  11. delay_long(100); //延时50000个空指令的时间
  12. }
  13. }

  14. void delay_long(unsigned int uiDelayLong) //延时函数
  15. {
  16. unsigned int i;
  17. unsigned int j;
  18. for(i=0;i<uiDelayLong;i++)
  19. {
  20. for(j=0;j<500;j++); //内嵌循环的空指令数量
  21. }
  22. }

     第四步:在keil工程中修改此源代码。把“led_dr=0;  //LED灭”这行代码删掉,修改后变成以下代码:
  1. #include "REG52.H"

  2. void delay_long(unsigned int uiDelayLong); //延时函数

  3. sbit led_dr=P3^5;

  4. void main()
  5. {
  6. while(1)
  7. {
  8. led_dr=1; //LED亮
  9. delay_long(100); //延时50000个空指令的时间

  10. delay_long(100); //延时50000个空指令的时间
  11. }
  12. }

  13. void delay_long(unsigned int uiDelayLong) //延时函数
  14. {
  15. unsigned int i;
  16. unsigned int j;
  17. for(i=0;i<uiDelayLong;i++)
  18. {
  19. for(j=0;j<500;j++); //内嵌循环的空指令数量
  20. }
  21. }

第五步:启动编译。点击上面”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机器码下载到单片机的操作流程。
(未完待续)

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?注册

×
pingto 发表于 2015-3-27 15:26 | 显示全部楼层
谢谢分享,又涨姿势了:)
 楼主| jianhong_wu 发表于 2015-3-29 21:10 | 显示全部楼层
本帖最后由 jianhong_wu 于 2015-6-3 16:58 编辑

第八节:把.hex机器码下载到单片机的操作流程。
烧录程序也叫下载程序。下载程序的本质是什么?把单片机当做一个存储器,每一条程序指令都对应一个唯一的存储地址,把这些指令一条条存储到指定的存储地址中,这就是下载程序的本质。对于STC89C52RC单片机,在下载程序时需要上位机界面软件和一根USB转串口线。上位机界面软件负责把指定.hex格式的机器码文件打开,.hex格式的机器码文件里面记录着每条程序指令对应的地址信息,在下载过程中,上位机界面软件根据.hex记录的指令内容和对应的地址信息,经过USB转串口线,跟单片机的内置引导程序进行串口通讯,从而把.hex记录的信息传输到单片机内部的flash存储器中,实现了程序的下载。
在讲操作流程之前,请读者先把以下一个LED灯闪烁的代码编译成.hex格式的文件,这个.hex文件保存在D盘的”stc89c52rc”文件夹里。
  1. #include "REG52.H"

  2. void delay_long(unsigned int uiDelayLong); //延时函数

  3. sbit led_dr=P3^5;  

  4. void main()  
  5. {
  6.    while(1)
  7.    {
  8.        led_dr=1;  //LED亮
  9.        delay_long(100);    //延时50000个空指令的时间
  10.        led_dr=0;  //LED亮
  11.        delay_long(100);    //延时50000个空指令的时间
  12.    }
  13. }

  14. void delay_long(unsigned int uiDelayLong) //延时函数
  15. {
  16.    unsigned int i;
  17.    unsigned int j;
  18.    for(i=0;i<uiDelayLong;i++)
  19.    {
  20.       for(j=0;j<500;j++);  //内嵌循环的空指令数量
  21.    }
  22. }


下面详细讲解把.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电源线插入到手机充电器5VUSB接口,效果显著,明显提高了下载的成功率。
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口,或者尝试更换到其它电脑上试试。

下节预告:主程序的两个区域:初始化和循环。
(未完待续)


本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?注册

×
泰山神泉 发表于 2015-3-30 12:48 | 显示全部楼层
学习一下,不知道单片机是硬件重要呢还软件重要啊
泰山神泉 发表于 2015-3-30 12:50 | 显示全部楼层
单片机不知道是软件重要还硬件重要啊
 楼主| jianhong_wu 发表于 2015-3-31 18:06 | 显示全部楼层
本帖最后由 jianhong_wu 于 2015-6-3 16:59 编辑

第九节:程序从哪里开始,要到哪里去?
程序从哪里开始,要到哪里去?为了让初学者了解C语言程序的执行顺序,我把程序分成三个区域:进入主程序前的区域,主程序的初始化区域,主程序的循环区域。
进入主程序前的区域。这是上电后,在单片机执行主程序代码之前就已经完成了的工作。包括头文件的包含,宏定义,内存分配这些工作。这部分的内容可以暂时不用去了解,我会在后面的一些章节中陆续深入讲解。
主程序的初始化区域。这是上电后,单片机进入主程序后马上就要执行的程序代码,这部分区域的代码有一个特点,大家也必须记住的,就是单片机只执行一次。只要单片机不重启,不复位,那么上电后这部分的代码只被执行一次。
主程序的循环区域。单片机在主程序中执行完了初始化区域的代码,紧接着就进入这片循环区域的代码。单片机一直在循环执行这段代码,这就是上电后单片机的最终归宿,一直处在循环的状态。
下面我跟大家分析一个程序源代码的三个区域和执行顺序,大家先看中文解释部分的内容,暂时不用理解每行指令的语法。该源代码实现的功能是:上电后,蜂鸣器鸣叫一声就停止,然后看到一个LED灯一直在闪烁。本程序是基于坚鸿51单片机学习板。

  1. #include "REG52.H"  //进入主程序前的区域:头文件包含

  2. sbit beep_dr=P2^7;  //进入主程序前的区域:宏定义
  3. sbit led_dr=P3^5;   //进入主程序前的区域:宏定义

  4. unsigned long i;    //进入主程序前的区域:内存分配

  5. void main()                    //主程序入口,即将进入初始化区域
  6. {
  7.          beep_dr=0;                  //第一步:初始化区域:蜂鸣器开始鸣叫。
  8.    for(i=0;i<6250;i++);       //第二步:初始化区域:延时0.5秒左右。也就是蜂鸣器鸣叫的持续时间。
  9.          beep_dr=1;                  //第三步:初始化区域:蜂鸣器停止鸣叫。
  10.    while(1)                    //执行完上面的初始化区域,即将进入循环区域
  11.    {
  12.        led_dr=1;               //第四步:循环区域:LED开始点亮。
  13.        for(i=0;i<6250;i++);   //第五步:循环区域:延时0.5秒左右。也就是LED点亮的持续时间。
  14.        led_dr=0;  //LED灭      //第六步:循环区域:LED开始熄灭。
  15.        for(i=0;i<6250;i++);   //第七步:循环区域:延时0.5秒左右。也就是LED熄灭的持续时间。马上返回上面第四步继续循环往下执行。
  16.    }
  17. }

  18. //解释:
  19. //单片机进入主程序后,第一步到第三步是属于初始化区域,只被执行一次。然后进入循环区域,从第四步执行到第七步,
  20. //执行完第七步之后,马上返回上面第四步继续循环往下执行,单片机一直处于第四步到第七步的循环区域中。


经过以上的分析,可以看出这三个区域的大概分布如下:


//...进入主程序前的区域
void main()               
    {
   //...初始化区域
   while(1)                     
   {
       //...循环区域
   }
}


下节预告:一个用来学习C语言的模板程序。
(未完待续)



 楼主| jianhong_wu 发表于 2015-4-2 13:28 | 显示全部楼层
本帖最后由 jianhong_wu 于 2015-6-3 17:01 编辑

第十节:一个用来学习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)                     
   {

   }
}

   详细的源代码如下:

  1. #include "REG52.H"
  2. #define const_voice_short  40  
  3. #define const_key_time1  20  
  4. #define const_key_time2  20  
  5. #define const_key_time3  20   
  6. void initial(void);
  7. void delay_short(unsigned int uiDelayShort);
  8. void dig_hc595_drive(unsigned char ucDigStatusTemp16_09,unsigned char ucDigStatusTemp08_01);  
  9. void display_drive(void);
  10. void display_service(void);
  11. void hc595_drive(unsigned char ucLedStatusTemp16_09,unsigned char ucLedStatusTemp08_01);
  12. void T0_time(void);
  13. void key_service(void);
  14. void key_scan(void);
  15. sbit beep_dr=P2^7;
  16. sbit key_sr1=P0^0;
  17. sbit key_sr2=P0^1;
  18. sbit key_sr3=P0^2;
  19. sbit key_gnd_dr=P0^4;
  20. sbit led_dr=P3^5;  
  21. sbit dig_hc595_sh_dr=P2^0;     
  22. sbit dig_hc595_st_dr=P2^1;  
  23. sbit dig_hc595_ds_dr=P2^2;  
  24. sbit hc595_sh_dr=P2^3;   
  25. sbit hc595_st_dr=P2^4;  
  26. sbit hc595_ds_dr=P2^5;  
  27. unsigned char GucKeySec=0;   
  28. unsigned char GucKey3Sr=1;
  29. unsigned int  GuiVoiceCnt=0;
  30. unsigned char GucVoiceStart=0;
  31. unsigned char GucDigShow8;
  32. unsigned char GucDigShow7;  
  33. unsigned char GucDigShow6;  
  34. unsigned char GucDigShow5;
  35. unsigned char GucDigShow4;
  36. unsigned char GucDigShow3;
  37. unsigned char GucDigShow2;
  38. unsigned char GucDigShow1;
  39. unsigned char GucDisplayUpdate=1;
  40. unsigned char GucWd=0;
  41. unsigned int GuiWdData0=0;
  42. unsigned int GuiWdData1=0;
  43. unsigned int GuiWdData2=0;
  44. unsigned int GuiWdData3=0;
  45. unsigned int GuiWdData4=0;
  46. unsigned int GuiWdData5=0;
  47. unsigned int GuiWdData6=0;
  48. unsigned int GuiWdData7=0;
  49. unsigned int GuiWdData8=0;
  50. unsigned int GuiWdData9=0;
  51. code unsigned char dig_table[]=
  52. {
  53.   0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f,0x77,0x7c,0x39,0x5e,0x79,0x71,0x00,0x40,
  54. };



  55. void main() //主程序
  56. {
  57. /*---C语言学习区域的开始---------------------------------------------------------------------------*/
  58.         

  59.         
  60.   GuiWdData0=10;   //把10这个数值放到窗口变量0里面显示
  61.   GuiWdData1=11;   //把11这个数值放到窗口变量1里面显示
  62.   GuiWdData2=12;   //把12这个数值放到窗口变量2里面显示
  63.   GuiWdData3=13;   //把13这个数值放到窗口变量3里面显示
  64.   GuiWdData4=14;   //把14这个数值放到窗口变量4里面显示
  65.   GuiWdData5=15;   //把15这个数值放到窗口变量5里面显示
  66.   GuiWdData6=16;   //把16这个数值放到窗口变量6里面显示
  67.   GuiWdData7=17;   //把17这个数值放到窗口变量7里面显示
  68.   GuiWdData8=18;   //把18这个数值放到窗口变量8里面显示
  69.   GuiWdData9=19;   //把19这个数值放到窗口变量9里面显示
  70.         
  71. /*---C语言学习区域的结束---------------------------------------------------------------------------*/
  72.    while(1)  
  73.    {
  74.                   initial();
  75.       key_service();
  76.       display_service();
  77.    }

  78. }


  79. void display_service(void)
  80. {
  81.     static unsigned char SucLedStatus16_09=0;  
  82.     static unsigned char SucLedStatus08_01=0;   
  83.     static unsigned int  SinWdDataTemp=0;

  84.     if(1==GucDisplayUpdate)
  85.     {
  86.         GucDisplayUpdate=0;
  87.                
  88.                           switch(GucWd)
  89.                           {
  90.            case 0:
  91.                 GucDigShow8=0;
  92.                                               SinWdDataTemp=GuiWdData0;
  93.                                               break;
  94.            case 1:
  95.                 GucDigShow8=1;
  96.                                               SinWdDataTemp=GuiWdData1;
  97.                                               break;
  98.            case 2:
  99.                 GucDigShow8=2;
  100.                                               SinWdDataTemp=GuiWdData2;
  101.                                               break;
  102.            case 3:
  103.                 GucDigShow8=3;  
  104.                                               SinWdDataTemp=GuiWdData3;
  105.                                               break;
  106.            case 4:
  107.                 GucDigShow8=4;
  108.                                               SinWdDataTemp=GuiWdData4;
  109.                                               break;
  110.            case 5:
  111.                 GucDigShow8=5;
  112.                                               SinWdDataTemp=GuiWdData5;
  113.                                               break;
  114.            case 6:
  115.                 GucDigShow8=6;
  116.                                               SinWdDataTemp=GuiWdData6;
  117.                                               break;
  118.            case 7:
  119.                 GucDigShow8=7;
  120.                                               SinWdDataTemp=GuiWdData7;
  121.                                               break;
  122.            case 8:
  123.                 GucDigShow8=8;
  124.                                               SinWdDataTemp=GuiWdData8;
  125.                                               break;
  126.            case 9:
  127.                 GucDigShow8=9;
  128.                                               SinWdDataTemp=GuiWdData9;
  129.                                               break;                 
  130.         }
  131.                                 
  132.         GucDigShow7=17;
  133.         GucDigShow6=16;         
  134.                                 
  135.                                 if(1==GucKey3Sr)
  136.                                 {
  137.                                    if(SinWdDataTemp>=10000)
  138.                              {
  139.               GucDigShow5=SinWdDataTemp/10000;
  140.            }
  141.                              else
  142.                                    {
  143.               GucDigShow5=16;
  144.            }
  145.                                 
  146.                                    if(SinWdDataTemp>=1000)
  147.                                    {
  148.               GucDigShow4=SinWdDataTemp%10000/1000;
  149.            }
  150.                                     else
  151.                                    {
  152.               GucDigShow4=16;
  153.            }
  154.                                 
  155.                                    if(SinWdDataTemp>=100)
  156.                                    {
  157.               GucDigShow3=SinWdDataTemp%1000/100;
  158.            }
  159.                                     else
  160.                                    {
  161.               GucDigShow3=16;
  162.            }        
  163.                                 
  164.                                    if(SinWdDataTemp>=10)
  165.                                    {
  166.               GucDigShow2=SinWdDataTemp%100/10;
  167.            }
  168.                                     else
  169.                                    {
  170.               GucDigShow2=16;
  171.            }        
  172.                                 
  173.            GucDigShow1=SinWdDataTemp%10;
  174.                           }
  175.                                 else
  176.                                 {
  177.                                          GucDigShow5=16;
  178.                                 
  179.                                    if(SinWdDataTemp>=0x1000)
  180.                                    {
  181.               GucDigShow4=SinWdDataTemp/0x1000;
  182.            }
  183.                                     else
  184.                                    {
  185.               GucDigShow4=16;
  186.            }
  187.                                 
  188.                                    if(SinWdDataTemp>=0x0100)
  189.                                    {
  190.               GucDigShow3=SinWdDataTemp%0x1000/0x0100;
  191.            }
  192.                                     else
  193.                                    {
  194.               GucDigShow3=16;
  195.            }        
  196.                                 
  197.                                    if(SinWdDataTemp>=0x0010)
  198.                                    {
  199.               GucDigShow2=SinWdDataTemp%0x0100/0x0010;
  200.            }
  201.                                     else
  202.                                    {
  203.               GucDigShow2=16;
  204.            }        
  205.                                 
  206.            GucDigShow1=SinWdDataTemp%0x0010;
  207.         }
  208.                                 
  209.                                 
  210.                                 SucLedStatus16_09=SinWdDataTemp>>8;  
  211.         SucLedStatus08_01=SinWdDataTemp;
  212.         hc595_drive(SucLedStatus16_09,SucLedStatus08_01);
  213.     }

  214. }


  215. void key_scan(void)
  216. {  
  217.         
  218.   static unsigned int  SuiKeyTimeCnt1=0;
  219.   static unsigned char SucKeyLock1=0;

  220.   static unsigned int  SuiKeyTimeCnt2=0;
  221.   static unsigned char SucKeyLock2=0;

  222.   static unsigned int  SuiKey3Cnt1=0;
  223.   static unsigned int  SuiKey3Cnt2=0;
  224.         
  225.         
  226.   if(1==key_sr1)
  227.   {
  228.      SucKeyLock1=0;
  229.      SuiKeyTimeCnt1=0;
  230.   }
  231.   else if(0==SucKeyLock1)
  232.   {
  233.      SuiKeyTimeCnt1++;
  234.      if(SuiKeyTimeCnt1>const_key_time1)
  235.      {
  236.         SuiKeyTimeCnt1=0;
  237.         SucKeyLock1=1;  
  238.         GucKeySec=1;   
  239.      }
  240.   }

  241.   if(1==key_sr2)
  242.   {
  243.      SucKeyLock2=0;
  244.      SuiKeyTimeCnt2=0;
  245.   }
  246.   else if(0==SucKeyLock2)
  247.   {
  248.      SuiKeyTimeCnt2++;
  249.      if(SuiKeyTimeCnt2>const_key_time2)
  250.      {
  251.         SuiKeyTimeCnt2=0;
  252.         SucKeyLock2=1;  
  253.         GucKeySec=2;  
  254.      }
  255.   }

  256.         
  257.   if(1==key_sr3)
  258.   {
  259.        SuiKey3Cnt1=0;
  260.        SuiKey3Cnt2++;
  261.        if(SuiKey3Cnt2>const_key_time3)
  262.        {
  263.            SuiKey3Cnt2=0;
  264.            GucKey3Sr=1;  
  265.        }
  266.    }
  267.    else   
  268.    {
  269.        SuiKey3Cnt2=0;
  270.        SuiKey3Cnt1++;
  271.        if(SuiKey3Cnt1>const_key_time3)
  272.        {
  273.           SuiKey3Cnt1=0;
  274.           GucKey3Sr=0;
  275.        }
  276.    }


  277. }


  278. void key_service(void)
  279. {
  280.         static unsigned char SucKey3SrRecord=1;
  281.         
  282.   if(GucKey3Sr!=SucKey3SrRecord)
  283.   {
  284.      SucKey3SrRecord=GucKey3Sr;
  285.                  GucDisplayUpdate=1;
  286.                
  287.   }
  288.         
  289.   switch(GucKeySec)
  290.   {
  291.     case 1:
  292.           GucWd++;
  293.                       if(GucWd>9)
  294.                                         {
  295.              GucWd=9;
  296.           }
  297.                       GucDisplayUpdate=1;
  298.                                        
  299.                                        
  300.           GuiVoiceCnt=const_voice_short;
  301.           GucVoiceStart=1;
  302.           GucKeySec=0;  
  303.           break;   
  304.    
  305.     case 2:
  306.           GucWd--;
  307.                       if(GucWd>9)
  308.                                         {
  309.              GucWd=0;
  310.           }
  311.                       GucDisplayUpdate=1;
  312.                
  313.           GuiVoiceCnt=const_voice_short;
  314.           GucVoiceStart=1;
  315.           GucKeySec=0;  
  316.           break;  
  317.   }     



  318.         
  319. }

  320. void display_drive()  
  321. {
  322.    static unsigned char SucDigShowTemp=0;
  323.    static unsigned char SucDisplayDriveStep=1;
  324.         
  325.    switch(SucDisplayDriveStep)
  326.    {
  327.       case 1:
  328.            SucDigShowTemp=dig_table[GucDigShow1];
  329.            dig_hc595_drive(SucDigShowTemp,0xfe);
  330.            break;
  331.       case 2:  
  332.            SucDigShowTemp=dig_table[GucDigShow2];
  333.            dig_hc595_drive(SucDigShowTemp,0xfd);
  334.            break;
  335.       case 3:
  336.            SucDigShowTemp=dig_table[GucDigShow3];
  337.            dig_hc595_drive(SucDigShowTemp,0xfb);
  338.            break;
  339.       case 4:  
  340.            SucDigShowTemp=dig_table[GucDigShow4];
  341.            dig_hc595_drive(SucDigShowTemp,0xf7);
  342.            break;
  343.       case 5:
  344.            SucDigShowTemp=dig_table[GucDigShow5];
  345.            dig_hc595_drive(SucDigShowTemp,0xef);
  346.            break;
  347.       case 6:  
  348.            SucDigShowTemp=dig_table[GucDigShow6];
  349.            dig_hc595_drive(SucDigShowTemp,0xdf);
  350.            break;
  351.       case 7:  
  352.            SucDigShowTemp=dig_table[GucDigShow7];
  353.            dig_hc595_drive(SucDigShowTemp,0xbf);
  354.            break;
  355.       case 8:
  356.            SucDigShowTemp=dig_table[GucDigShow8];
  357.            dig_hc595_drive(SucDigShowTemp,0x7f);
  358.            break;
  359.    }

  360.    SucDisplayDriveStep++;
  361.    if(SucDisplayDriveStep>8)  
  362.    {
  363.      SucDisplayDriveStep=1;
  364.    }



  365. }



  366. void dig_hc595_drive(unsigned char ucDigStatusTemp16_09,unsigned char ucDigStatusTemp08_01)
  367. {
  368.    unsigned char i;
  369.    unsigned char ucTempData;
  370.    dig_hc595_sh_dr=0;
  371.    dig_hc595_st_dr=0;

  372.    ucTempData=ucDigStatusTemp16_09;  
  373.    for(i=0;i<8;i++)
  374.    {
  375.          if(ucTempData>=0x80)dig_hc595_ds_dr=1;
  376.          else dig_hc595_ds_dr=0;

  377.          dig_hc595_sh_dr=0;   
  378.          delay_short(1);
  379.          dig_hc595_sh_dr=1;
  380.          delay_short(1);

  381.          ucTempData=ucTempData<<1;
  382.    }

  383.    ucTempData=ucDigStatusTemp08_01;  
  384.    for(i=0;i<8;i++)
  385.    {
  386.          if(ucTempData>=0x80)dig_hc595_ds_dr=1;
  387.          else dig_hc595_ds_dr=0;

  388.          dig_hc595_sh_dr=0;     
  389.          delay_short(1);
  390.          dig_hc595_sh_dr=1;
  391.          delay_short(1);

  392.          ucTempData=ucTempData<<1;
  393.    }

  394.    dig_hc595_st_dr=0;
  395.    delay_short(1);
  396.    dig_hc595_st_dr=1;
  397.    delay_short(1);

  398.    dig_hc595_sh_dr=0;
  399.    dig_hc595_st_dr=0;
  400.    dig_hc595_ds_dr=0;

  401. }

  402. void hc595_drive(unsigned char ucLedStatusTemp16_09,unsigned char ucLedStatusTemp08_01)
  403. {
  404.    unsigned char i;
  405.    unsigned char ucTempData;
  406.    hc595_sh_dr=0;
  407.    hc595_st_dr=0;

  408.    ucTempData=ucLedStatusTemp16_09;  
  409.    for(i=0;i<8;i++)
  410.    {
  411.          if(ucTempData>=0x80)hc595_ds_dr=1;
  412.          else hc595_ds_dr=0;

  413.          hc595_sh_dr=0;   
  414.          delay_short(1);
  415.          hc595_sh_dr=1;
  416.          delay_short(1);

  417.          ucTempData=ucTempData<<1;
  418.    }

  419.    ucTempData=ucLedStatusTemp08_01;  
  420.    for(i=0;i<8;i++)
  421.    {
  422.          if(ucTempData>=0x80)hc595_ds_dr=1;
  423.          else hc595_ds_dr=0;

  424.          hc595_sh_dr=0;     
  425.          delay_short(1);
  426.          hc595_sh_dr=1;
  427.          delay_short(1);

  428.          ucTempData=ucTempData<<1;
  429.    }

  430.    hc595_st_dr=0;  
  431.    delay_short(1);
  432.    hc595_st_dr=1;
  433.    delay_short(1);

  434.    hc595_sh_dr=0;   
  435.    hc595_st_dr=0;
  436.    hc595_ds_dr=0;

  437. }


  438. void T0_time(void) interrupt 1
  439. {
  440.   TF0=0;
  441.   TR0=0;

  442.   if(1==GucVoiceStart)
  443.         {

  444.                  if(GuiVoiceCnt!=0)
  445.                  {
  446.                           GuiVoiceCnt--;
  447.                           beep_dr=0;
  448.      }
  449.                  else
  450.                  {
  451.                           beep_dr=1;
  452.                           GucVoiceStart=0;
  453.      }
  454.   }

  455.   key_scan();
  456.   display_drive();  


  457.   TH0=0xfe;  
  458.   TL0=0x0b;
  459.   TR0=1;
  460. }


  461. void delay_short(unsigned int uiDelayShort)
  462. {
  463.    static unsigned int i;  
  464.    for(i=0;i<uiDelayShort;i++);
  465. }



  466. void initial(void)
  467. {
  468.          static unsigned char SucInitialLock=0;
  469.         
  470.          if(0==SucInitialLock)
  471.          {
  472.        SucInitialLock=1;
  473.                  
  474.              key_gnd_dr=0;
  475.        led_dr=0;  
  476.        beep_dr=1;
  477.        TMOD=0x01;
  478.        TH0=0xfe;  
  479.        TL0=0x0b;           
  480.                  
  481.        EA=1;     
  482.        ET0=1;   
  483.        TR0=1;   

  484.    }


  485. }


下节预告:三种类型变量的定义与赋值语句。
(未完待续)



yuanquan12345 发表于 2015-4-2 15:33 | 显示全部楼层
下载学习。
您需要登录后才可以回帖 登录 | 注册

本版积分规则

快速回复 在线客服 返回列表 返回顶部