资源描述
1. 编译器选择8级优化时,可能会出现错误。刚写好的程序,建议先用0级优化看能否正常运行,再用更高的优化等级进行优化。2. a、写中断程序一定要用using语句指定寄存器组。第1、2、3组都可以,不能是0,否则可能会main()函数冲突。从一个中断程序中调用函数必须和中断使用相同的寄存器组(摘自Keil Cx51 编译器用户手册中文版P129)。建议把原本中断函数需要调用的函数直接写在中断函数里,无须调用。b、51单片机的中断有两个优先级。一个中断不会打断另一个相同优先级的中断。这样相同级别中断可以使用同一个组。比如:低优先级的中断函数都用 using 1,高优先级的中断都用 using 2 。这样不会冲突。3. C语言无符号数容易犯的错误。若定义成有符号数char,则不会陷入死循环。main()unsigned char i;for (i = 2; i=0; i-)printf(%d,i);4. C51忌讳使用绝对定位_at_,因为只要定义变量和变量的作用域,编译器就会把一个固定地址给这个变量,无须人工将其绝对定位,这样可能引发其他问题。5. bit与sbit的区别:bit定义的位标量的地址是随机的,而sbit定义的位标量的地址是确定的。bit只能访问芯片内部RAM中的可寻址位20H-2FH,而sbit可以访问芯片内部RAM中的可寻址位和特殊功能寄存器中的可寻址位。注意不能直接在程序里用P10等位变量,需要经过sbit定义才可以使用。例如:bit tem ;sbit led=P10 ;tem的地址是随机分配的,而led的地址则固定为0x90.0 。sbit变量后面需要跟等号=。6. 为了避免由于使用参数宏而带来意外的错误,需要注意以下几点:6.1 宏的参数必须带括号,例如 #define CIRCLE_SQUARE(R) 3.141*(R)*(R)6.2 对所使用的参数宏进行简单地展开检查;6.3 使用简单表达式、对参数加括号、避免节外生枝的使用方式(例如“+”、“-”一类都属于不必要的附件运算);6.4 在参数宏定义时,对于运算顺序通过括号进行明确的限定,只要遵循以上几点,就可以避免大多数应用场合的意外错误。手把手教你写程序内容:从最简单的程序入手,手把手教你写程序,让同学们拿到一个复杂的程序或者任务,能快速找到切入点,写出程序,再在此基础上优化程序。当拿到一个单片机任务时,不要急于动手写程序,先仔细分析它的以下几个点:1、它要单片机整体实现什么功能2、功能细分(模块化),先干什么,再干什么,最后干什么3、画初步流程图,(把几个模块画出即可)4、模块之间的分析:一个模块到另一个模块之间,怎么变换,怎么连接(优化流程图)5、单个模块分析:每个模块要做什么(流程图细化)6、所有模块结合连接,细化所有流程图7、分析单个模块每步要用到的方法或者指令8、总流程图定型9、纸上写程序,对照流程图分析其可行性,若不可行则返回10、上机调试,加注释11、从小到大,一个功能一个功能地调试; 以上十一步,缺一不可(小程序例外)切记:流程图的确定很重要,需反复修改大忌:拿到任务,不仔细分析就写程序。即使是小程序,我们也要养成良好的编程习惯,不要一味的追求结果。写小程序可能比别人快,若是大程序,一旦出现思维混乱,或者出现程序调试不出结果,那么你花在调试上的时间,要比别人的多。!磨刀不误砍柴工!程序的优化:属于后期工作,只有调试出来后,才去优化,如果一开始优化和写程序同时进行,一是加重你的思考量,二是出现问题无从下手。无疑增加了写程序的难度。对于一个初学者,写一个程序,本身头脑就处于紧张的状态,思考的问题就很多,如果此时把优化程序也考虑进去,你脑袋的负荷无疑加重,若你头脑精明,你可以把优化的地方,先在纸上记下来,等到调试结果正常,再把你想到的,优化的地方加进去。7、如果在中断程序中改变了多字节类型的变量,那么中断程序以外的程序中(主程序,子函数)要使用该多字节类型变量的话,读写前要关中断,读写后再开中断。否则会导致偶尔读写错误。(实质为资源冲突)举一反三:其他的数据类型也可能有这种影响。例如:长整型、浮点型。例如:unsignedintms_counter;voidT0()/定时器程序每100毫秒中断一次,程序略if(ms_counter1000)ms_counter+;voidmain(void)/初始化定时器程序每100毫秒中断一次,程序略unsignedchartt;ms_counter=0;tt=0;/用tt控制只响一次while(1)if(ms_counter400)if(tt=0)tt=1;Sound_on();elseSound_off();/其他程序8、sbit变量不能使用extern关键字,使其在不同的文件中被使用,如要在led.c和main.c文件中使用同一个变量led0,有以下下两种办法: 1.在各种文件中重复定义变量,如在led.c中定义sbit led0=P10;同样在main.c中定义sbit led0=P10;这样,led0就变成了全局变量,可以在两个文件中使用。 2.将sbit led0=P10定义到led.h头文件中,均在led.c和main.c中包含led.h这个头文件。 9、在多文件的程序中声明外部变量(extern和) 如果一个程序包含两个文件,在两个文件中都要用到同一个外部变量Num,不能分别在两个文件中各自定义一个外部变量Num,否则在进行程序的连接时会出现“重复定义”的 错误。正确的做法是:在任一个文件中定义外部变量Num,而在另一个文件中用extern对Num作“外部变量声明”。即extern Num;注意若Num为uchar类型,应当写为“extern uchar Num”,否则会当为int,而导致出错。当使用static声明变量和函数时,需要在定义变量和函数的基础上加上此关键字,而不能单独使用。例如:static int a; /定义性声明,需要时,直接使用变量a即可a = 0x01;static int funA(int a, int b); /声明,且static不起作用int funA(int a ,int b) /定义,即使funA有static关键字修饰,但由于static不能单独使用,/故funA仍为外部函数。extern对变量进行声明时,如没有初始化,则为引用性声明,不含定义,如需使用此变量,需要进行定义。例如:extern int a; /引用性声明,不含定义extern int a = 0x01; /定义性声明,需要时,直接使用变量a即可int a; /定义extern对函数进行声明时,如没有函数体,则为引用性声明,不含定义。extern int funB(int a ,int b); /引用性声明,不含定义,且extern声明可以省略extern int funC(int a, int b) /定义性声明10、一般的,要尽量减少中断服务程序的内容和长度。因为在主程序中可以还需要随时响应其他的中断或事件。如果一个中断服务程序过程,很可能会影响到主程序对外部信号的检测和响应。通常,在中断程序中只是改变一些变量或标志位,在主程序中再根据变量或标志位的值进行判断,处理相应的事件。11、在A/D和D/A转换电路中,电源电压和基准电压的稳定性,对转换的精度影响很大。另外,A/D和D/A转换电路中要特别注意地线的正确连接,否则转换结果将是不正确的,干扰影响将很严重。12、根据C语言标准,左移“”运算要求操作数至少是int,如果不满int,自动转换成int(C语言整型提升)。因此uchar a=0x01;a8;实际运算,并不是8位数左移8位,而是int型左移8位。13、在中断里调用其他函数,且要进行参数传递时,必须保证被调用函数所使用的寄存器组与中断函数一样,否则会产生不正确的结果。为了保证被调用的函数与中断函数使用的寄存器一致,可对被调用函数使用using ,不过此函数只能被中断函数调用。14、函数不使用using 时,所使用寄存器组保持与此函数被调用前相同,不对RS0和RS1的值进行修改;当使用了using 关键字后,此函数所使用的寄存器组与using所定义的一样。15、当指定中断程序的工作寄存器组时,保护工作寄存器的工作就可以被省略。使用关键字using 后跟一个0 到3 的数对应着4 组工作寄存器当指定工作寄存器组的时候默认的工作寄存器组就不会被推入堆栈这将节省32 个处理周期,因为入栈和出栈都需要2个处理周期。为中断程序指定工作寄存器组的缺点是所有被中断调用的过程都必须使用同一个寄存器组否则参数传递会发生错误。16、如何使用pdata 类型的变量?当要使用到pdata 类型的变量,如下:void main(void)uchar pdata a;a=0x01; 则需要进行如下设置,否则pdata 的变量a则会无效。a、 修改STARTUP.A51的内容。默认时,PPAGEENALBE为0,表示不允许pdata类型的变量,须将其值改为1;PPAGE表示pdata类型的变量存储在哪一页,01H表示存放在外部存储器的第1页,地址范围100H至1FFH,此时P2经STARTUP.A51处理后的值为0x01;此项设置需和BL51连接器的设置一致。b、 修改BL51连接器。根据STARTUP.A51中PPAGE所设置的值来填写Pdata的值,如下图。图中Pdata的值可以填写100H至1FFH中任意一个,表示pdata类型的变量从所填写的值开始存储。例如,当Pdata填写的值为108H时,表示pdata类型的变量从108H开始存储,因此,存储范围变为了108H至1FFH。另外,存储模式Compact的作用是将没有指定存储类型的变量定义为pdata类型,对uchar pdata a;变量的定义没有影响,但对uchar a;则有影响。17、XBYTE的用法。XBYTE存在于#include 头文件中。XBYTE0x000F=data; / 此语句表示将data写到外部RAM中的0x000Fdata=XBYTE0x000F / 此语句表示读取外部RAM中0x000F的数据以下语句与上面的语句等效:#define EX_RAM XBYTE0x000F /将EX_RAM定义为外部RAM的地址0x000FEX_RAM=data; / 此语句表示将data写到外部RAM中的0x000Fdata=EX_RAM / 此语句表示读取外部RAM中0x000F的数据18、如何在keil中用汇编实现51中没有的指令部分MCU与8051兼容,但会增加8051中没有的指令,如华邦的W77E58和N79E352等芯片,具有8051中没有的指令DEC DPTR。如何才Keil中实现此指令呢?方法1:在需要执行该指令的地方放置相应的机器码MAIN: MOV DPTR,#02HDB 0A5H;由于从数据手册上得知,DEC DPTR的机器码为0A5H,故此处相当于执行了DEC DPTR指令。AJMP $END方法2:使用宏定义的方法/*宏定义,表示用DEC_DPTR代替MACRO与ENDM之间的内容*/DEC_DPTR MACRO DB 0A5H ;此处不能与MACRO同一行ENDMMAIN:MOV DPTR,#02HDEC_DPTR ;放置机器码0A5H,相当于执行DEC DPTRAJMP $END通过将以上两种方法生成的hex文件调入到编程器中,发现代码一样。经测试,同样可以用以上两种方法代替8051中已有的指令。例如,从数据手册可知,MOV A,#0FH的长度为2字节,机器码的值为74H,0FH。因此,经验证,以下三个程序等效,产生的HEX文件一样MAIN:MOV A,#55HDB 74HDB 0FHMOV P1,AAJMP $ENDMAIN:MOV A,#55HMOV A,#0FHMOV P1,AAJMP $ENDTEST MACRODB 74HDB 0FHENDMMAIN:MOV A,#55HTESTMOV P1,AAJMP $END18、汇编中包含头步骤:例如,T2CON为定时器2的特殊功能寄存器,地址为0C8H,要对此寄存器赋值01H,除了MOV 0C8H,#01H和T2CON EQU 0C8HMOV T2CON,#01H外,还有用包含头文件的方法#include MOV T2CON,#01H此时,需要将A51中的“Defines 8051 SFR Names”的勾去掉。 19、指针C51 提供一个3 字节的通用存储器指针。通用指针的头一个字节表明指针所指的存储区空间,另外两个字节存储16 位偏移量。对于DATA IDATA 和PDATA 段只需要8 位偏移量。Keil 允许使用者规定指针指向的存储段,这种指针叫具体指针。使用具体指针的好处是节省了存储空间编译器不用为存储器选择和决定正确的存储器操作指令产生代码这样就使代码更加简短但你必须保证指针不指向你所声明的存储区以外的地方否则会产生错误而且很难调试。由于使用具体指针能够节省不少时间所以我们一般都不使用通用指针。20、EEPROM存放开关机(复位)次数方法:每次开机(复位)读取EEPROM存放开关机的数据,并加1后重新写入EEPROM。21、C51中,将printf函数与串口输出结合注意事项:a、关串口中断;b、初始化串口,并使TI=1;c、KEIL里扩展出了b(8位),h(16位),l(32位)来对输入字节宽的设置 在Keil C51中用printf输出一个单字节变量时要使用%bd,若使用%d,则默认为双字节宽度,输出可能会出错。如unsigned char counter;printf(Current count: %bdn, counter);而在标准C语言中都是使用%d:printf(Current count: %dn, counter);d、输出数据类型的长度应与定义的数据类型长度一致,如:uint tem2=97; printf(%c,%bdn,tem2,tem2);第一个输出会出错。22、我一般不刻意的注意这个,都是从软件自身找问题的。我写程序时对于软件抗干扰都是在程序状态上考虑意外情况的,例如:if(a=1).elseif(a=2).else/这个else一定得加的,即使自己认为不可能出现的情况也要加上. /经过好多程序走飞的情况发现:大多情况都是缺少这个语句条件的,这 /语句可以写成重新初始化a 还有程序出现堆栈比较深的运算(例如浮点乘除法后)或中断比较深,我加2个_nop_();23、STC12C5410AD外部RAM使用方法:a.在Keil中设置外部RAM的起始地址和大小,如下图b.将变量定义为xdata即可。24、中断嵌套当有外部中断0时,中断标志位IE0由硬件自动置1,进入中断服务程序后,IE0被自动清0。若外部中断0触发信号在执行完中断服务程序后仍没有撤除,就会再次使已经变0的中断标志位IE0置1,再次进入中断服务程序;若在响应中断服务程序期间,再次产生外部中断0触发信号时,此中断不能被识别,因为CPU在响应中断时会自动关闭同一中断。如果外部中断0比外部中断1的优先级高,当在响应外部中断0期间产生外部中断1时,如果执行完外部中断0后,外部中断1的中断请求标志位IE1仍没有清除的话,将会响应外部中断1的请求;但是如果在响应外部中断0期间,外部中断1的触发信号产生后又撤除的话,IE1也会自动清除,也就是说,执行完外部中断0后,不会去响应外部中断1。当多个中断源同时向CPU请求中断时,CPU就可以通过中断优先权电路率先响应中断优先权高的中断请求,而把中断优先权低的中断请求暂时搁置起来,等到处理完优先权高的中断请求后再来响应优先权低的中断。如果某一中断源提出中断请求后,CPU不能立即响应,只要该中断请求标志位不被软件人为清除,中断请求的状态就将一直保持,直到CPU响应中断为止。但是对于串行口中断,即使CPU响应了中断,其中断标志位RI/TI也不会自动清零,而必须在中断服务程序中设置清除RI/TI的指令后,才会再一次地提出中断请求。25、在满足应用要求的前提下,选择配较低的单片机,较小的RAM/ROM、较低的ADC分辨率、较低的ADC速率,较少的IO管脚都可以降低单片机的整体功耗。当然了,这个得能满足你产品需求的前提下。26、对于一个数字系统而言,其功耗大致满足公式:P=CV2f。其中C为系统的负载电容,V为电源电压,f为系统工作频率2。功耗与电源电压的平方成正比,因此电源电压对系统的功耗影响最大,其次是工作频率,再次就是负载电容。负载电容对设计人员而言,一般是不可控的,因此设计一个低功耗系统,在不影响系统性能的前提下,尽可能地降低电源的电压和工作频率。对于大多数低功耗单片机来说,工作频率越低,意味着消耗的电流也越小,但是不能认为频率越低,系统整体功耗越小,因为工作频率降低,意味着需要更长的处理时间,其他外围电路消耗的电能就越多。目前有很多单片机都允许有两个或者两个以上的时钟源,低频时钟作为如UART、定时器等外围功能器件的时钟源,高频时钟作为系统的主时钟。在不需要高速运行的场合下,低频时钟也可以作为系统主时钟使用。对于需要在工作状态与空闲状态之间频繁切换的应用,在考虑单片机本身低功耗的同时,应该考虑切换时间和切换电流。考虑到有些场合单片机的工作特点,选择单片机不光要关注工作电流,更应该关注单片机休眠时的静态电流。单片机丰富的低功耗模式和极低的静态电流,在满足特定应用功能的同时,有效降低系统的功耗。尽量关闭MCU内部不用的资源,比如ATmega8内部的模拟比较器,默认是开着的,还有ATmega88内部的大多数资源都可以在不用的时候用软件关闭。27、定时/ 计数器的实时性定时/ 计数器启动计数后,当计满回0 溢出向主机请求中断处理,由内部硬件自动进行。但从回0 溢出请求中断到主机响应中断并作出处理存在时间延迟,且这种延时随中断请求时的现场环境的不同而不同,一般需延时3 个机器周期以上,这就给实时处理带来误差差。大多数应用场合可忽略不计,但对某些要求实时性苛刻的场合,可采用动态补偿措施。所谓动态补偿,即在中断服务程序中对THx、TLx 重新置计数初值时,应将THx、TLx 从回0 溢出又重新从0 开始继续计数的值读出,并补偿到原计数初值中去进行重新设置。可考虑如下补偿方法:CLR EA ;禁止中断MOV A ,T L x ;读TLx 中已计数值ADD A,#LOW ;LOW 为原低字节计数初值MOV T L x,A ;设置低字节计数初值MOV A,#HIGH ;原高字节计数初值送AADDC A ,T H x ;高字节计数初值补偿MOV T H x,A ;置高字节计数初值SETB EA ;开中断28、动态读取运行中的定时器/计数值在动态读取运行中的定时/ 计数器的计数值时,如果不加注意,就可能出错。这是因为不可能在同一时刻同时读取THx 和TLx 中的计数值。比如,先读TLx 后读THx,因为定时/ 计数器处于运行状态,在读TLx 时尚未产生向THx 进位,而在读THx 前已产生进位,这时读得的THx 就不对了;同样,先读THx 后读TLx 也可能出错。一种可避免读错的方法是:先读THx,后读TLx,将两次读得的THx 进行比较;若两次读得的值相等,则可确定读的值是正确的,否则重复上述过程,重复读得的值一般不会再错。此法的软件编程如下:RDTM: MOV A,THx ;读取THx 存A 中MOV R0,TLx ; 读取TLx 存R0 中CJNE A,THx,RDTM ;比较两次THx 值,若相等,则读得的值正确,否则重读MOV R1,A ;将THx 存于R1 中29、掉电及空闲模式 掉电方式 当PCON中的第二位PD为1时,进入掉电模式,不会执行任何指令,外部时钟停振,片内所有功能部件停止工作,如定时器,串行口,外部中断(部分增强型8051的外部中断可以工作),但片内RAM和SFR的内容保持不变。标准8051从掉电状态退出的惟一方法是硬件复位(部分增强型8051还可以通过外部中断来退出掉电状态),复位后,SFR被重新初始化,但RAM的内容不变。因此,若要使得8051在供电恢复正常后继续执行掉电前的程序,那就必须在掉电前预先把SFR中的内容保护到片内RAM,并在供电恢复正常后为SFR恢复到掉电前的状态。 当PCON的第一位IDEL为1时,进入空闲模式,CPU停止工作,不会执行任何指令,但中断、串行口和定时器可以继续工作。此时,CPU现场(即SP、PC、PSW和ACC等)、片内RAM和SFR中其他寄存器内容均维持不变。退出空闲模式有两种方法:一、被允许中断的中断源发出中断请求;二、硬件复位。30、看门狗应用 将喂狗操作(取反指令,如 CPL P1.0)分成两步,放在主程序和中断里执行。如将SETB P1.0放在主程序中,将CLR P1.0放在中断里,这样可以避免主程序跑飞,中断功能正常或者主程序正常,而中断跑飞的情况导致看门狗失效。31、volatile作用如果将将变量加上volatile修饰,则编译器保证对此变量的读写操作都不会被优化(肯定执行)。此例中i也应该如此说明。一般说来,volatile用在如下的几个地方:1、中断服务程序中修改的供其它程序检测的变量需要加volatile;2、多任务环境下各任务间共享的标志应该加volatile;3、存储器映射的硬件寄存器通常也要加volatile说明,因为每次对它的读写都可能由不同意义;另外,以上这几种情况经常还要同时考虑数据的完整性(相互关联的几个标志读了一半被打断了重写),在1中可以通过关中断来实现,2中可以禁止任务调度,3中则只能依靠硬件的良好设计了。32、51准双向口读取:只有1条指令:MOV A,P1为读端口寄存器有两条指令:MOV A,#0FFHMOV A,P1为读引脚33、采用C语言和汇编语言混合编程是最佳的选择;34、系统投入运行的最初时刻,应对系统进行自检和初始化。开机自检在初始化前执行,如果自检无误,则对系统进行正常初始化,通常包括硬件初始化和软件初始化两个部分。硬件初始化是指对系统中的各种硬件资源设定明确的初始状态,如对各种可编程芯片进行编程、对各I/O端口设定初始状态和为单片机的硬件资源分配任务等。软件初始化包括,对中断的安排、对堆栈的安排、状态变量的初始化、各种软件标志的初始化、系统时钟的初始化和各种变量储存单元的初始化等。35、 按键连击速度一般为3-4次/s。36、 复合键利用两个以上按键同时按下时产生的按键效果,但实际情况下,不可能做到真正的“同时按下”,它们的时间差可以长达50ms左右,故当检测到有KEY1按键按下时,还要等待超过50ms,再判断是否还有其他按键按下,再解析按键。37、 数码管显示闪烁效果时,一般闪烁速度为1-4次/s。38、 大电流和高电压设备的启动和关闭都是由软件指令来完成,这些指令执行后,必然引起强烈的干扰,这些干扰不能算随机干扰,它们与软件完全相关;可以在最后才执行这些可能引起强烈干扰的I/O操作,之后立即进入睡眠状态,这样就不会干扰到CPU,等CPU醒来后,干扰的高峰也基本过去。39、 用积分时间为20ms整数倍的双积分型A/D转换方式,能有效地抑制50Hz工频干扰。40、 掉电检测电路必须在电压下降到CPU最低工作电压之前就提出中断申请,提前时间为几百us到数ms,以便掉电中断程序进行掉电保护。41、用定时器作看门狗:当为专职看门狗时,在主程序中周期性清0定时器计数值,以使定时器中断不能产生,当产生定时器中断时,表明看门狗溢出,此时应执行出错处理程序或者进行复位。当为兼职看门狗时,可以在定时器中断程序对计数值进行加1,若计数值大于某值时,表明看门狗溢出,而主程序中应周期性地对计数值进行清0。42、中断中, 冲突发生的条件: 1)某一资源为中断程序和主程序所使用;该资源可以为1个变量,也可以为1个数组或者1个缓冲区。 2)中断程序或主程序对该资源进行了写操作; 3)主程序不能用一条指令对资源完成读或者写操作。(这条不对,参考深入浅出AVR单片机P100的例子)当这三个条件均满足时,即有可能发生资源冲突,导致程序偶然运行不正常。为了避免发生冲突,可以在主程序中先关中断,再对资源进行读或写,结束后再开中断。当主程序对资源的访问比较费时,长期关中断可能影响系统的实时性;解决的办法是尽可能缩短关中断的时间,将一边访问,一边处理的工作方式改为集中访问,分批处理。如果是读该资源,则关中断迅速将该资源的内容转移到缓冲区,再开中断,然后再对缓冲区中的信息进行处理;如果是写该资源,则先边运算边写缓冲区,全部写好后再关中断,然后迅速将缓冲区中的内容复制到该资源中,边可以开中断了。43、A/B*C的运算方案不如(A*C)/B的运算方案精度高。因此,应尽可能将出现偏差的运算往后排,先进行无偏差或偏差小的运算。在定点运算系统中,加减法只要不超限,是没有偏差的,乘法运算的结果会使字长增加,如双字节乘双字节,积为四字节,如果保留全部结果,则没有偏差的。乘法运算的结果会使字长增加,如双字节乘双字节,积为四字节,如果保留全部结果,则没有偏差;如果受字长限制,则要舍去低位字节,从而产生舍入偏差。除法几乎都是有偏差的能够刚好整除的情况是很少的。在浮点运算系统中,加减法由于要进行对阶操作,当两操作数的阶码相差较大时,绝对值大的数有可能将绝对值小的数淹没,使运算的结果仍为绝对值大的数,一点儿也看不出绝对值小的数对结果的影响。相比之下,浮点乘法和浮点除法引起的偏差就比较小,它们能够保持一个比较稳定的运算精度。另外,不管在定点系统中还是在浮点系统中,都要尽可能避免两个数值相近的数过早相减,因为他们都可能是近似值,相减以后,差的有效数值大大减少,必然带来更大的相对误差。经过后续运算之后,结果可能离真实值相差甚远。再有,尽可能不要用绝对值小的数作分母,否则引起的误差也是很大的。44、要对软件标志位的使用进行说明;对于全局定义的软件标志,它有惟一的定义;对于局部定义的软件标志,必须注明其有效范围。45、软件理论已经证明:任何一个程序(除某些短小的子程序外)都存在错误(缺陷),人们可以通过合理的测试来证明它仍然存在错误,却无法证明它已经没有错误。软件测试应该把发现错误作为目的,而不能把“程序调通”作为目的。1.P0口能驱动8个TTL电路意思: 8051单片机P0口驱动8个TTL电路的意思,TTL电路输入悬浮时相当于输入高电平,因此P0口输出高电平驱动TTL电路几乎不需输出电流。TTL电路输入为低电平时最少要释放1mA电流,因此P0口输出低电平时吸收的电流大于8mA。TTL输出高电平最大1.6mA,输出低电平时吸收的最大电流 16mA。51输出最好用低电平有效,推动PNP管,因为51复位后IO为高电平,如果用高电平有效推N管的话上电复位后会先让外部电路动做。2. 在51里,有一条指令没有写进书本,机器码为A5,执行操作:将下一个字节跳过而不管它是单字节指令还是双字节或三字节指令的一部分.如果反汇编工具不识别A5指令的话,你在A5以后的程序反汇编后就错乱无章.当成个数据,用db a5 即可3. 有些51系统容易复位,一般是电路设计上的问题。很多电路介绍的复位电路都是10u和8.2k,但是在实践过程中我们发现该电路在电源不稳时很容易复位,特别是附近有大干扰时,如继电器动作等。我建议使用22u和1k的复位电路,有许多电路改为该数值后就工作稳定了。当然,最好的办法还是使用专用复位电路或三极管电路,但是那样要增加成本和体积。4. 电路中的滤波电容一定要注意加上,最好每个芯片都再加一个约0.1uf的电容,这样对电路的稳定性很有好处。如果使用了看门狗电路,就有可能是软件问题,程序工作到某些环节时忘记了复位看门狗,结果计数满了就复位了。5. 如果在中断程序中改变了多字节类型的变量,那么中断程序以外的程序中(主程序,子函数),读写前要关中断,读写后再开中断。举一反三:其他的数据类型也可能有这种影响。例如:长整型、浮点型。上面的例子是中断里写,主程序中读。相反主程序写,中断里读也可能出错。6. 教你一招,别说我损。写一个测试代码,反复向EEPROM中的某几个不用的空位字节写入0x55,直到把它干到寿命终结不能写为止,如果按照10MS写一个字节计算的话,大约只需要20分钟就能干掉它。然后向这个芯片中烧入你的正常代码,当然了,这个代码中应该有一段上电检测EEPROM这几个字节的代码,先尝试向它写入0Xaa,然后再读出来看看是否写入成功,如果没写入则再来两次,如果始终不能写入,这当作检查通过,如果就判断为检查失败,这个时候代码要装着不知情继续执行正确代码,下面的破坏行为应该如何做就不要我讲了把?破坏行为要装的掩蔽一点,例如调一段代码檫除FLASH的代码,嘿嘿,那对方肯定以为CHIP质量不好容易出现FLASH数据丢失,如果对方使用了AD什么的,可以偶尔人为让它波动大一点,这样对方一般只会怀疑PCB和硬件电路弄的不好,而不会想到是代码动手脚了,长久以后他的用户肯定也会认为他们的产品质量不好,你这个时候就可以向他的客户推广你的产品了。 上电写EEPROM的次数要在你自己的产品质量承诺的寿命时间之内,否则你自己的产品也可能增加维修。 这个方法特别适合在外接单挣钱的工程师,你可能给了对方几个CHIP做测试,对方测试通过偏说不行,就是不给你余款,然后把CHIP拿去CRACK,妄想省掉这个钱,NND,让他们见鬼去把,俺这招已经对付了不少不良分子。 7. AD键盘8.防解密高招高招, 解密使用一些带内部晶振和内部EEPROM的单片机,如PIC16F913和ATMEGA8等,带内部晶振的单片机有一个寄存器OSCTUNE(或OSCCAL),这个是芯片厂家用来校准内部晶振的,范围从0-31,出厂时同型号的单片机这个寄存器的值是不一样.我们可以利用一些隐藏功能,将OSCTUNE寄存器的值存入内部的EEPROM中,开机时读取EEPROM的值,再与OSCTUNE的值相比较,若二者相同系统正常工作,若不相同则不正常工作.解密者将解密的程序烧写进单片机中后,会发大部分的芯片不能正常工作,因为他们不知道这个隐藏的功能.举例说明:芯片为PIC16F913,这个厂品有4个按键(KEY0、KEY1、KEY2、KEY3),内部我们可以设定这样子一个隐藏的功能,如果KEY0与KEY1同时按下3秒钟以上,会将OSCTUNE寄存器存入单片机的EEPROM中。开机复位后,读取EEPROM中的数据,与OSCTUNE寄存器相比较,若二者相同系统正常工作,若不相同则不正常工作。以上有三个重点:1、对于OSCTUNE寄存器不要进行写的操作,只进行读的操作,因为写了一次以后,就一直是你写的这个数据的。2、刚才介绍的KEY0、KEY1同时按下3秒钟这个功能,可不能让解密者(包括产品的用户)知道,当然大家可以用别的隐藏的功能。3、单片机中的OSCTUNE寄存器(或OSCCAL)的值,同一种型号的单片机不是每一个都是一样的,有32个数据,也就是说32个芯片中有一个是与解密的单片机是一样的。这样子造成的后果是:解密者解密了你的程序以后,却发现有些单片机可以正常工作,可有些单片机不能正常工作,可以说是大部分的单片机不能正常工作。不过需要注意一下:要是遇到强干扰把EEPROM中的数据改变了看客户怎么收拾你!9.PIC16F887A中,要求SLEEP指令后的下一条指令为NOP;不知51和AVR的芯片是否需要注意这一点。经查,AVR的datasheet无此要求,可能是其唤醒时,存在启动延时。10.中断随时随刻都有可能产生,故编写程序时,需要时刻注意中断的影响。11. 注意以下语句在某些编译器下,结果可能出错: unsigned char a,b; unsigned int sum; a=0x80; b=0x80; sum=a+b; 12.编程序最重要是好维护。几个执行时间和程序的可读性比,和开发时间比,我认为是不用考虑的。为了几个机器周期而把程序搞得很复杂,是非常愚蠢的行为。可是很多人多乐此不疲啊。总体系统的算法是要考虑优化的问题的, 这点我是赞同的。天天在技术上对着几行程序去优化,而导致开发速度减慢,是非常愚蠢的行为。13.串口通信协议:引导码/识别码+长度+命令字+data+校验 通过引导码/识别码、长度、校验三步检测 每当出错则丢弃当前数据并还原接收状态和空间14. 当准备调试一块板的时候,一定要先认真的做好目视检查,检查在焊接的过程中是否有可见的短路和管脚搭锡等故障,检查是否有元器件型号放置错误,第一脚放置错误,漏装配等问题,然后用万用表测量各个电源到地的电阻,以检查是否有短路,这个好习惯可以避免贸然上电后损坏单板。调试的过程中要有平和的心态,遇见问题是非常正常的,要做的就是多做比较和分析,逐步的排除可能的原因,要坚信“凡事都是有办法解决的”和“问题出现一定有它的原因”,这样最后一定能调试成功。 做一个硬件设计人员要锻炼出良好的沟通能力,面对压力的调节能力,同一时间处理多个事务的协调和决断能力和良好平和的心态,还有细心和认真等等。 对初学者来说,在新的领域面前一穷二白,要学的东西太多太多,一味机械地试图学习这些“高深的语法”和“看不懂的技巧”,胡乱模仿些别人的“优秀风格”,不能说完全无所得,只能说会学得很累,而且往往事倍功半-久而久之,信心丧失殆尽,半途而废。学习编程,做自己便好。学习眼前能够看懂的内容,多写自己会写的程序。对于已经学到的东西,仔细地体会、思考,发掘其中的发展;学会用一种研究的心态去考察你的每一个疑问;不可以轻易地人云亦云,在网上看了别人无责任的经验,甚至是写错了,丢在一边,懒得改的东西之后,就放弃了自己的探索;要学会坚持自我,遇到别人先进的做法,在自己还没有体会到自己当前这种做法的劣势之前,不要轻易盲从;同样,使用了先进的方法,在没有同时理解这种做法的优点和缺点之前,请不要轻易地感叹“这种方法好,跟帖顶一下!”“大师高手宁有种乎?”坚持“体会到了才是学到了”的态度,最终形成自己的风格,形成自己的技巧。
展开阅读全文