您好,欢迎访问三七文档
当前位置:首页 > 商业/管理/HR > 咨询培训 > 单片机实用编程技巧值得学习
单片机软件系统设计教程基础要求•熟练运用C语言编程。•熟练掌握和运用一款单片机。包括硬件结构、寄存器、外设、了解基本汇编指令等。•熟悉一款集成开发环境及编译器。•独立编写并调通2000行以上的小型程序。一、如何编写高质量软件•合理划分函数功能,合并常用函数•严格的函数、变量命名方法•合理划分软件层次。如硬件隔离层、驱动程序层、功能函数层、应用层。•合理运用宏定义,提高程序可移植性•经常查看反汇编代码,练习优化指令。函数划分•常用的功能集合,可以合理合并成一个函数•利用参数传递,将多个函数合并。•例如:某菜单需要将选中的菜单条反色显示,因此需要2个中文显示函数:DisplayString(char*ptr,charx,chary);DisplayStringInvert(char*ptr,charx,chary);可以合并成一个函数DisplayString(char*ptr,charx,chary,boolInvertFlag);注意函数过分合并会造成运算性能下降。函数与变量命名•严格按照模块名_功能名的方法命名,按正确的英文(甚至拼音也可)命名•单词首字母大写•例如:ADC_CailbrateZero()CPU_SleepDelay(intTime_ms)Flash_WriteWord(intAddr,intWord)软件层次划分•合理划分软件层次,将使程序结构清晰•层次良好的软件,移植性强•对软件开发者来说,便于管理和维护,甚至可实现“代码免维护”•大大提高开发速度。•例:在某液晶上需要做中文菜单、数字显示、波形显示、滚动条等。为该应用编写液晶驱动函数。LCD驱动层划分范例•每一层含义明确,功能完整独立•每一层只调用其下一层的函数,禁止跨层调用函数。•所有变量对于非其它层函数不透明。禁止跨层使用全局变量•层次划分越多,执行效率越低•更改或更换任何一层的函数,不影响其它层函数的执行。合理运用宏定义•1.用宏定义作硬件隔离#defineLED1_POUTP1OUT#defineLED1_PDIRP1DIR#defineLED1_BIT(BIT2)/*更换IO只需改以上3行*/#defineLED_ONLED1_POUT|=LED1_BIT/*亮*/#defineLED_OFFLED1_POUT&=~LED1_BIT/*灭*//*更换单片机只需修改以上2行*/用宏定义自动运算#defineOSC(1000)#defineBAUD(9600)...UBR00=((long)OSC*1000/8)/BAUD;UBR10=0x00;...#defineTIMER_PERIOD12000...TH1=(unsignedint)(65536-TIMER_PERIOD)/256;/*51单片机*/TL1=(unsignedint)(65536-TIMER_PERIOD)%256;...用宏定义作编译开关#defineAVE0#defineRMS1#definePEK2#defineUMODEAVE/AVE:平均值RMS真有效值PEK峰值*/...#if(UMODE==RMS)VAC_Ch1=(longint)TempIntV1*TempIntV1;VAC_Ch2=(longint)TempIntV2*TempIntV2;#endif#if(UMODE==AVE)VAC_Ch1=absv(TempIntV1);VAC_Ch2=absv(TempIntV2);#endif...用宏定义作功能开关•#defineON1•#defineOFF0•#defineHI_LO2•#defineLEV_21•#defineNONE0•#defineMINORCUTOFF/*是否打开小值切除功能*/•#defineRS485ON/*是否打开RS-485通讯功能*/•#defineDACON/*是否打开变送功能*/•#defineDEFAULTOFF/*是否打开参数备份功能*/•#defineALARM_MODENONE/*报警模式NONE=无报警LEV_2=双限HI_LO=高低限*/•#defineOFFSETON/*是否打开偏移补偿功能*/•#defineKEYTONEON/*按键音*/程序优化•打开编译器自带的优化选项。注意高级别优化会删除部分“冗余”代码,或改动程序结构,应注意挥发性变量(volatile)•查看反汇编代码,看汇编代码是否有冗余操作,合理修改。二、软件结构•如何让CPU尽可能多地执行多项任务•如何让CPU尽可能快地响应各个事件•如何让程序尽可能少地占用RAM•如何让程序具有尽可能强的可扩展性•如何让编出的程序尽可能通用•如何减少编程工作量?如何减少错误概率和调试工作量?•好的程序结构,可以解决上述问题实时性•实时性最高的任务,需要零延迟立即响应。应考虑硬件实现。如用CPLD/FPGA、捕获等实现。•实时性较高任务,允许微秒级延迟的事件,可用中断实现。但背景程序中不允许长时间关闭中断。且中断处理时间不宜过长。•实时性较低事件,可以查询。事件查询•1.事件出现时间较长,允许响应延迟也较长。如某个按键,可以在主循环内直接查询•2.事件出现时间较短,小于主循环时间,但允许响应延迟,可以中断置标志位,主程序处理,完毕后清除标志。比如串口数据桢判别。•3.情况2中,若事件无法产生中断,可以定时中断查询,置标志位。主程序处理顺序多任务程序voidmain(void){intcounter=0;while(1)/*repeatforever*/{SleepDelay_ms(10);check_serial_io();/*checkforserialinput*/process_serial_cmds();/*processserialinput*/check_kbd_io();/*checkforkeyboardinput*/process_kbd_cmds();/*processkeyboardinput*/adjust_ctrlr_parms();/*adjustthecontroller*/counter++;/*incrementcounter*/if(counter%100==0){…}}}并发多任务程序voidmain(){while(1){LPM0;}}串口中断{…}键盘中断{…}定时中断{…}休眠事件B事件C事件A事件D低功耗程序首选结构每个事件不宜执行时间过长注意中断随机嵌套,注意保护临界代码全部信息通过全局变量传递和保存前后台程序Voidmain(){while(1){等待1秒延迟读取电压计算温度显示温度报警处理}}定时中断串口中断最常见的程序结构背景程序与前台程序之间通过全局变量传递注意中断用到全局变量的保护注意临界代码的保护主程序不能长时间关闭中断中断程序尽量简短,尽快返回。注意避免函数递归调用RTOS•嵌入一个微型实时操作系统(RTOS),可以轻松实现多任务(进程)并发执行。•商品化或免费的的RTOS,如uC-OSII,RTX-TINY,国产Small-RTOS等。•允许每个任务都是死循环,由RTOS负责调度CPU程序指针分时执行每个任务。•CPU、RAM开销较大,8位CPU或简单程序不建议使用。避免函数重入•重入(reentrant):指函数直接或间接调用函数自身。•和PC不同,PC采用堆栈传递允许函数重入。单片机采用静态变量传递,不允许函数重入。•造成重入的原因:1.递归调用。例如A函数调用B函数,B函数调用C函数,C函数调用A函数。避免函数重入•2.中断重入假设主程序循环会调用A函数,中断也使用了A函数,那么有可能在主循环A函数执行一半的时候,中断,又执行A函数,造成A函数原来的变量被更改。3.硬件重入假设主程序操作液晶显示,中断也操作液晶显示,有可能在液晶时序一半的时候,中断,时序重新开始,造成液晶收到错误指令,发生不可恢复的错误。临界代码保护•临界代码(CriticalCode):指运行过程中不允许被中断打断的代码。•几种产生临界代码的原因:1.执行时间严格,不允许被延长。如软件模拟串口,软件循环产生精确延迟,软件模拟时间严格的异步时序(如1Wire总线),不允许被打断。临界代码保护•2.中断时间很长,某函数不允许长时间暂停。比如液晶。•3.因为避免函数重入产生的临界代码。a.再写一个一样的函数,专供中断用。b.用reentrant关键字定义函数(堆栈传递参数)c.进入临界代码前关中断,后开中断。临界代码保护•2.中断变量传递造成的临界代码:例如,主程序要得到存有周期计数值的长整形变量PeriodCnt。该值在中断内被更新。中断:{PeriodCnt=TAOF_Cnt*65536+TAR;}Main{…Freq=1.0/PeriodCnt;//错误!_DINT();TempL=PeriodCnt;_EINT();Freq=1.0/TempL;//正确!}对象化编程•单片机系统的任何一个独立单元/部件/外设/功能集合,都可以抽象成一个对象(Object)。•对象都有其属性、方法、事件。•以IO为例。属性:方向、电平。方法:置1,置0,取反,读取输入值,读取方向…事件:跳变,中断…对象化编程•IO1_DIRIO1_IN作为其属性,可以被设置。•方法IO1_Set(Bool),IO1_Neg();•事件IO1_Interrupt();基于状态机编程•例:当某对象处于A状态时,假设发生了B事件,就执行C代码,并且跳转到D状态。•例:电子表。键A键B1.当显示时间时(状态1),按下键A,就显示日期。2.当显示日期时,按下A键,就显示秒3.当显示秒时,按下A键,回到显示时间。例:电子表•4.当显示时间时,按下B键,时闪烁,提示设置“时”•5.当提示提示设置“时”,按B键,分闪烁•6.当分闪烁时按B键,月闪烁•7.当月闪烁时按B键,日闪烁•8.当日闪烁时按B键,恢复时间显示•9.时闪烁时,按A键,时+1,若超过12归0•10.分闪烁时,按A键,分+1,若超过59归0例:电子表•11.月闪烁时,按A键,月+1,若超过12归0•12.日闪烁时,按A键,日+1,若超过31归0•13.显示秒时,按B键,秒归0状态跳转图正常显示日期显示A秒显示AA设置时设置分设置月设置日BBBBB秒=0B分++A月++A日++A时++A系统抽象•属性可以为7种值•方法有5种•事件有2种
本文标题:单片机实用编程技巧值得学习
链接地址:https://www.777doc.com/doc-4658167 .html