51使用经验.docx
《51使用经验.docx》由会员分享,可在线阅读,更多相关《51使用经验.docx(19页珍藏版)》请在冰豆网上搜索。
51使用经验
一、混合编程
1、模块内接口:
使用如下标志符:
#pragmaasm
汇编语句
#pragmaendasm
注意:
如果在c51程序中使用了汇编语言,注意在keil编译器中需要激活Properties中的“GenerateAssemblerSRCFile”和“AssemblerSRCFile”两个选项
来个实例吧:
#include
voidmain(void)
{
P2=1;
#pragmaasm
MOVR7,#10
DEL:
MOVR6,#20
DJNZR6,$
DJNZR7,DEL
#pragmaendasm
P2=0;
}
另:
1、把"xx.c"加入工程中,右击"xx.c"选择“optionsforfile"xx.c"选择“GenerateAssemblerSRCFile”和“AssembleSRCFile”打上黑勾有效;
2、根据选择的编译模式,把相应的库文件象加"xx.c"一样加入工程中并放在"xx.c"下面,如smail模式下选"keil\c51\lib\c51s.lib"加入工程中,如果要进行浮点运算把"keil\c51\lib\c51fpl.lib"也加入工程中。
在Keil安装目录下的\C51\LIB\目录的LIB文件如下:
C51S.LIB-没有浮点运算的Smallmodel
C51C.LIB-没有浮点运算的Compactmodel
C51L.LIB-没有浮点运算的Largemodel
C51FPS.LIB-带浮点运算的Smallmodel
C51FPC.LIB-带浮点运算的Compactmodel
C51FPL.LIB-带浮点运算的Largemodel
3、在"xx.c"头文件中加入优化:
比如#pragmaOT(4,speed)
4、在"xx.c"中加入汇编代码
#pragmaASM
;AssemblerCodeHere
#pragmaENDASM
5、编译生成xx.hex
注意:
没有做第一步会有如下警告:
'asm/endasm'requiressrc-controltobeactive
没有做第二步会有如下警告:
UNRESOLVEDEXTERNALSYMBOL;
REFERENCEMADETOUNRESOLVEDEXTERNAL等
没有做第三步会有如下警告:
UNDEFINEDSYMBOL(PASS-2)
二、中断使用
interrupt xx using y
跟在interrupt 后面的xx 值得是中断号,就是说这个函数对应第几个中断端口,一般在51中
0 外部中断0
1 定时器0
2 外部中断1
3 定时器1
4 串行中断
其它的根据相应的单片机有自己的含义,实际上c在编译的时候就是把你这个函数的入口地址放到这个对应中断的跳转地址。
using y 这个y时说这个中断函数使用的那个寄存器组就是51里面一般有4个 r0 -- r7寄存器,如果你的终端函数和别的程序用的不是同一个寄存器组则进入中断的时候就不会将寄存器组压入堆栈返回时也不会弹出来节省代码和时间。
三、关于reentrant的使用方法
云清燕发表于2006-11-1521:
27:
00
我在程序中出现了如下警告:
***WARNINGL15:
MULTIPLECALLTOSEGMENT
SEGMENT:
?
PR?
_CRCDATA?
PANEL_DISP
CALLER1:
?
C_C51STARTUP
CALLER2:
?
PR?
UART_RECV?
PANEL_DISP
***WARNINGL15:
MULTIPLECALLTOSEGMENT
SEGMENT:
?
PR?
ANALOGALLBECKON?
PANEL_DISP
CALLER1:
?
C_C51STARTUP
CALLER2:
?
PR?
UART_RECV?
PANEL_DISP
***WARNINGL15:
MULTIPLECALLTOSEGMENT
SEGMENT:
?
PR?
SWITCHALLBECKON?
PANEL_DISP
CALLER1:
?
C_C51STARTUP
CALLER2:
?
PR?
UART_RECV?
PANEL_DISP
我的程序编译出来就这3个警告,但是程序可以正常下载运行。
但是我觉得有这些警告会使程序存在bug。
从字面上看是它的意思是我程序中接受函数UART_RECV()多调用了analogAllBeckon()、switchAllBeckon()。
因为51的普通函数是不可重入的,变量放在固定的地址,两个函数同时运行时,就会修改同一个变量,从而导致结果错误。
于是我在analogAllBeckon()、switchAllBeckon()函数后面加了voidanalogAllBeckon()reentrant{//AllAnalogdatabeckon使程序消除了警告。
这种方法是表明函数是可被多哥任务调用而不修改函数里边的变量值,以此来实现函数的重入性。
关于reentrant的使用keil的官方论坛上有详细的讨论.
AndyNeil(官方工程师)建议
"Areyousurethatyoureallyneedtomakeeverythingreentrant?
...AreadingoftheKeilappnotes&knowledgebasearticlesonthissubjectshowedthatitwasnotnecessary."
由于每一次调用被reentrant声明的函数都要把函数的参数和内部变量压栈,所以很容易使堆栈区溢出,S52只有256Bytes的data段,一个简单的函数如果有一个参数三个内部变量,则需要压栈4字节以上,这还不包括函数调用堆栈.reentrant其实并不是适合低端的单片机,keil论坛上有人说对于那些有KB以上RAM的单片机reentrant才适合.
四、变量声明有关
在51系列中data,idata,xdata,pdata的区别data:
固定指前面0x00-0x7f的128个RAM,可以用acc直接读写的,速度最快,生成的代码也最小。
idata:
固定指前面0x00-0xff的256个RAM,其中前128和data的128完全相同,只是因为访问的方式不同。
idata是用类似C中的指针方式访问的。
汇编中的语句为:
moxACC,@Rx.(不重要的补充:
c中idata做指针式的访问效果很好)xdata:
外部扩展RAM,一般指外部0x0000-0xffff空间,用DPTR访问。
pdata:
外部扩展RAM的低256个字节,地址出现在A0-A7的上时读写,用movxACC,@Rx读写。
这个比较特殊,而且C51好象有对此BUG,建议少用。
但也有他的优点,具体用法属于中级问题,这里不提。
startup.a51的作用和汇编一样,在C中定义的那些变量和数组的初始化就在startup.a51中进行,如果你在定义全局变量时带有数值,如unsignedchardataxxx="100";,那startup.a51中就会有相关的赋值。
如果没有=100,startup.a51就会把他清0。
(startup.a51==变量的初始化)。
这些初始化完毕后,还会设置SP指针。
对非变量区域,如堆栈区,将不会有赋值或清零动作。
有人喜欢改startup.a51,为了满足自己一些想当然的爱好,这是不必要的,有可能错误的。
比如掉电保护的时候想保存一些变量,但改startup.a51来实现是很笨的方法,实际只要利用非变量区域的特性,定义一个指针变量指向堆栈低部:
0xff处就可实现。
为什么还要去改?
可以这么说:
任何时候都可以不需要改startup.a51,如果你明白它的特性。
五、类型有关
用bit能够定义一个变量,用sbit却不行,sbit能够定义端口。
51单片机系统设计与实际应用
作者:
转载
正确估计单片机的能力,知道单片机能做什么,最大程度的挖掘单片机的潜力对一个单片机系统设计者来说是至关重要的。
我们应该有这样一个认识,即单片机的处理能力是非常强大的。
早期的PC机,其CPU(8086)处理能力和8051相当,却能处理相当复杂的任务。
单片机的能力的关键就在软件设计者编写的软件上。
只有充分地了解到单片机的能力,才不会做出“冗余”的系统设计。
而采用许多的外围芯片来实现单片机能实现的功能。
这样做,即增加了系统成本,也可能会降低了系统的可靠性。
1.2系统可靠性至关重要
【规则2】使用看门狗。
看门狗电路通常是一块在有规律的时间间隔中进行更新的硬件。
更新一般由单片机来完成,如果在一定间隔内没能更新看门狗,那看门狗将产生复位信号,重新复位单片机。
更新看门狗的具体形式多是给看门狗芯片相关引脚提供一个电平上升沿或读写它的某个寄存器。
使用看门狗电路将在单片机发生故障进行死机状态时,重新复位单片机。
当前有多种看门狗的芯片,如MAXIM公司的MAX802,MAX813等。
而且,有好多种单片机中本身就集成有看门狗。
一个外部的看门狗是最好的,因为它不依赖于单片机。
如果可能的话,看门狗更新程序不应该放在中断或是子程序中,原则上应该放在主程序中。
我曾经见过一个工程师,他所调试的程序在运行时偶而会引起看门狗的复位动作,于是他干脆在每10ms就中断一次的时钟中断程序中清看门狗。
我相信他也知道使看门狗失去作用,可他却没有不是去查明引起这个现象的真正原因。
因此,我想提醒大家:
不论什么理由,绝对不要忽略系统故障的真正原因。
高质量的产品来自于高素质的工程师,高质量的产品造就高素质的工程师。
【规则3】确定系统的复位信号可靠。
这是一个很容易忽略的问题。
当你在设计单片机系统时,你脑中有这个概念吗?
什么样的复位信号才是可靠的吗?
你用示波器查看过你设计的产品的复位信号吗?
不稳定的复位信号可能会产生什么样的后果?
你有没有发现过你所设计的单片机系统,每次重新上电启动后,数据变得乱七八糟,并且每一次现象并不相同,找不出规律,或者有时候干脆不运行,或者有时候进入一种死机状态,有时候又一点事都没有正常运行?
在这种情况下,你应该查一下你的系统的复位信号。
一般在单片机的数据手册(Datasheet)中都会提到该单片机需要的复位信号的要求。
一般复位信号的宽度应为。
复位电平的宽度和幅度都应满足芯片的要求,并且要求保持稳定。
还有特别重要的一点就是复位电平应与电源上电在同一时刻发生,即芯片一上电,复位信号就已产生。
不然,由于没有经过复位,单片机中的寄存器的值为随机值,上电时就会按PC寄存器中的随机内容开始运行程序,这样很容易进行误操作或进入死机状态。
【规则4】确定系统的初始化有效。
系统程序开始应延时一段时间。
这是很多单片机程序设计中的常用方法,为什么呢?
因为系统中的芯片以及器件从上电开始到正常工作的状态往往有一段时间,程序开始时延时一段时间,是让系统中所有器件到达正常工作状态。
究竟延时多少才算合适?
这取决于系统的各芯片中到达正常工作状态的时间,通常以最慢的为准。
一般来说,延时20-100毫秒已经足够。
对于系统中使用嵌入式MODEM等“慢热”型的器件来说,则应更长。
当然,这都需要在系统实际运行中进行调整。
【规则5】上电时对系统进行检测。
上电时对系统中进行检测是单片机程序中的一个良好设计。
在硬件设计时也应该细细考虑将各个使用到的芯片、接口设计成容易使用软件进行测试的模式。
很多有经验的单片机设计者都会在系统上电时(特别是第一次上电时)进行全面的检测,或者更进一步,将系统的运行状态中分为测试模式和正常运行模式,通过加入测试模式对系统进行详细的检测,使得系统的批量检测更为方便容易。
另外要注意的是,一个简单明了的故障显示界面也是颇要费得心思的。
比如:
系统的外部RAM(数据存储器)是单片机系统中常用的器件。
外部RAM如果存在问题,程序通常都会成为一匹脱缰的野马。
因此,程序在启动时(至少在第一次上电启动时)一定要对外部RAM进行检测。
检测内容包括:
1)检测RAM中的单元。
这主要通过写入和读出的数据保持一致。
2)检测单片机与RAM之间的地址数据总线。
总线即没有互相短路,也没有连接到“地”上。
另外,很多芯片,都提供了测试的方法。
如串行通信芯片UART,都带环路测试的功能。
【规则6】按EMC测试要求设计硬件。
EMC测试要求已经成为产品的必需。
有很多的文章关于这方面的。
1.3软件编程和调试
【规则7】尽可能使用Small模式编译
对比起Large模式和Compact模式,Small模式能生成更为紧凑的代码。
在Small模式下,C51编译器将没有使用关键词,如idata、pdata、xdata特殊声明的变量通通放在data单元中。
在编程中,对于在的数据区,可以指定放在外部存储器中。
【规则8】在仿真前做好充分的准备
单片机硬件仿真器给单片机开发者带来了极大的方便,同时也很容易造成人的依赖性。
很多时候,没有仿真器却能促使工程师写出更高质量的程序。
也许在硬件仿真调试之前,下面准备工作将会对你有用:
1)程序编完后,对代码仔细逐行检查。
检查代码的错误,建立自己的代码检查表,对经常易错的地方进行检查。
检查代码是否符合编程规范。
2)对各个子程序进行测试。
测试的方法:
用程序测试程序,编制一个调用该子程序的代码,建立要测试子程序的入口条件,再看看它是否按预期输出结果。
3)如果代码有修改,再次对代码进行检查。
4)有可能的话,进行软件仿真——KeilC的软件仿真功能十分强大。
软件仿真可以防止因硬件的错误,如器件损坏、线路断路或短路,而引起调试的错误。
5)开始硬件仿真。
【规则9】使用库函数
重用代码,尤其是是标准库的代码,而不是手工编写你自己的代码。
这样更快、更容易也更安全。
KeilC中提供了多个库函数,这些库函数的用法在KeilC的帮助文件中有详细的描述。
【规则10】使用const。
这一点在很多经典的关于C和C++的书籍中是必谈的要点。
在《Exceptional C++》一书中,对这点有很精彩的描述,现摘录如下:
“没有正确的安全意识的枪手在世界上是不可能活的很长的。
const观念不正确的程序员也是一样和没有时间戴紧帽子的正确,没有时间检查带电电线的电工一样不会活的很长。
”
在C语言中,const修饰符表示告诉编译器此函数将不会改变被修饰的变量的指向的任何值(除了强制类型转换)。
当把指针作为参数传递时,总是合适地使用const,不仅可以防止你无意中错误的赋值,而且还可以防止在作为参数将指针传递给函数时可能会修改了本不想改变的指针所指向的对象的值。
如:
constintnum=7;
num=9;file:
//有/可能得到编译器的警告。
constchar*ptr,则表示该指针所指向的内容不会被改变,如果在程序中被发生对其赋值的操作,编译时将出错误提示。
如:
constchar*ptr=“hello”;
*ptr=‘H’;file:
//错/误,所指内容不可改变也可将const放在星号后面来声明指针本身不可改变。
如:
char*constptr;
ptr++;file:
//错/误,指针本身不可改变
也可同时禁止改变指针和它所引用的内容,其形式如下:
constchar*constptr;
【规则11】使用static
static是一个能够减少命名冲突的有用工具。
将只在一个模块文件中的变量和函数使用static修饰,将不会和其他模块可能具有相同名称的函数和变量在模块连接时不会产生名称冲突。
一般来说,只要不是提供给其它模块使用的函数,和非全局变量,均应使用static修饰。
将子程序中的变量使用static修饰时,表示这个变量在程序开始时分配内存,在程序结束时释放,它们在程序执行期间保持它们的值。
如:
voidfunc1(void)
{
staticinttime=0;
time++
}
voidfunc2(void)
{
staticinttime=0;
time++;
}
两个子程序中的time变量使用static修饰,所以它们是静态变量,每调用一次time将进行加1,并保持这个值。
它们的功能与下面程序相似:
inttime1=0;
inttime2=0;
voidfunc1(void)
{
time1++
}
voidfunc2(void)
{
time2++;
}
我们可以看出,使用static修饰后,模块中的全局变量减少,使得程序的更为简单。
【规则12】不要忽视编译器的警告。
编译器的给出的警告都是有的放矢,在没有查清引起警告的真正原因之前,不要忽视它。
【规则13】注意溢出问题,写安全的代码。
1.4KeilC编程
【规则14】深入了解你所用的工具。
仔细查看KeilC附带的帮助文件,你能找到你期待已久的东西。
KeilC是当前最好用的单片机开发软件。
要充分利用该软件的功能,就必须对它深入的进行了解。
【规则15】不要使用语言的冷僻特性,并且记住,耍小聪明会贻害无穷。
最重要的是编写你理解的代码,理解你编写的代码,你就可能会做得很好。
2推荐书目
要成为一个优秀的单片机系统产品设计工程师,兴趣、热情、责任心至关重要。
2.1单片机技术学习
《微机原理及应用(从16位到32位)》戴梅萼等著清华大学出版社。
学校教材,也是当年我学习单片机的启蒙书。
2.2C51编程学习
《单片机高级语言C51Windows环境编程与应用》作者:
徐爱钧彭秀
华电子工业出版社。
这本书几乎覆盖了C51编程的方方面面,最新版本对当前使用最广的keilC也有很详细的讲述。
对于刚学C51编程的同志,本书是上上之选,强力推荐。
比起现今书市上的所谓什么“C51编程圣经”之类的书强得多。
2.3C语言编程必读
《C陷阱与缺陷》AndrewKoenig著
《C专家编程》PeterVanDerLinden著
C语言开发技术经典之作,C程序员必读之书,数十年来经久不衰。
如果你想对C语言全面的掌握,真正了解C语言的精髓,这两本书是必读之作。
由人民邮电出版社出版的中文译本也还不错。
2.4程序设计技术方面
《数据结构》,严蔚敏,清华大学出版社。
清华大学出版社的教材质量稳定,中规中矩,价格相对来说也便宜一点。
《程序设计实践》BrianW.Kernighan,RobPike著;《代码大全》(网上有下载)。
这两本是能让你看后,感觉有大突破的那种书籍,千万别吝惜银子。
;------------------------------------------------------------------------------
;STARTUP.A51:
用户上电初始化程序
;------------------------------------------------------------------------------
;
;用户定义需上电初始化的内存空间
;
;使用以下EQU命令可定义在CPU复位时需用0进行初始化的内存空间
;
;;IDATA存储器的空间的绝对起始地址总是0.
IDATALENEQU80H;需用0进行初始化的IDATA存储器空间的字节数
;
XDATASTARTEQU0H;XDATA存储器空间的绝对起始地址
XDATALENEQU0H;需用0进行初始化的XDATA存储器的空间字节数.
;
PDATASTARTEQU0H;PDATA存储器的空间的绝对起始地址
PDATALENEQU0H;需用0进行初始化的PDATA存储器的空间字节数.
;
;注意:
IDATA存储器的空间在物理上包括了8051单片机的DATA和BIT存储器空间.
;听说至少要保证与C51编译器运行库有关的存储器的空间进行0初始化不知是否
;------------------------------------------------------------------------------
;再入函数模拟初始化
;
;以下用EQU指令定义了再入函数模拟堆栈指针的初始化
;
;使用SMALL存储器模式时再入函数的堆栈空间.
IBPSTACKEQU0;使用SMALL存储器模式再入函数时将其设置成1.
IBPSTACKTOPEQU0FFH0FFH+1;将堆栈顶设置为最高地址+1.
;
;使用LARGE存储器模式时再入函数的堆栈空间.
XBPSTACKEQU0;使用LARGE存储器模式再入函数时将其设置成1.
XBPSTACKTOPEQU0FFFFH0FFFFH+1;将堆栈顶设置为最高地址+1.
;
;使用COMPACT存储器模式时再入函数的堆栈空间.
PBPSTACKEQU0;使用COMPACT存储器模式再入函数时将其设置成1.
PBPSTACKTOPEQU0FFFFH0FFFFH+1;将堆栈顶设置为最高地址+1.
;
;------------------------------------------------------------------------------
;
;使用COMPACT存储器模式时64K字节XDATA存储器空间的分页定义
;
;以下用EQU指令定义PDATA类型变量在XDATA存储器空间的页地址
;使用EQU指令定义PFAGE时必须与L51连接定位器PDATA指令的控制参数一致
;
PPAGEENABLEEQU0;使用PDATA类型变量时将其设置成1.
PPAGEEQU0;定义页号.
;
;------------------------------------------------------------------------------
NAME?
C_STARTUP;模块名为?
C_STAUTUP
?
C_C51STARTUPSEGMENTCODE;代码
?
STACKSEGMENTIDATA;堆栈
RSEG?
STACK;堆栈
随便打进的汉字有用就用吧有错就帮我改吧
欢迎光临下岗农民主页
DS1
EXTRNCODE(?
C_START);程序开始地址
PUBLIC?
C_STARTUP
CSEGAT