单片机

单片机(Microcontroller, MCU)是一种集成了计算机功能的微型计算机,通常由一个微处理器(CPU)、存储器(ROM、RAM)、输入/输出接口、定时器/计数器等功能模块集成在同一芯片上。单片机是一种常用于嵌入式系统中的控制器,它被广泛应用于家电、汽车、工业自动化、医疗设备、消费电子、物联网(IoT)设备等多个领域。

本文主要总结一些比较实用的单片机编程经验:

经验之一:用“软件陷阱+程序口令”对付PC指针的弹飞

当CPU受到外界干扰,有时PC指针会飞到另一段程序中,或跳到空白段去。其实,如果PC指针飞到空白段去,倒也好处理。只要在空白段设立软件陷阱(拦截指令),将程序拦截到初始化段或程序错误处理段。但是,如果PC指针飞到另一段程序中去了,系统如何办?小匠在这里推荐一种方法——程序口令,思路如下:

1、首先,程序必须模块化。每个模块(子程序)执行一个功能。每个模块只有一个出口(RET)。

2、设立一个模块(子程序)ID寄存器。

3、为每个子程序配置一个唯一的ID号码。

4、每当子程序执行完毕,要返回(RET)之前,先将本子程序的ID号送入 ID寄存器。

5、返回到上级程序后,先判断ID寄存器中的ID号。

如果正确,则继续执行;如果不正确,则表示PC指针有可能已经跳错了,子程序没有按预计的出口返回,这时将程序拦截到初始化段或程序错误处理段。

这种方法,如同在程序中设立了若干个岗哨,每次调用子程序返回后,都要对口令(ID号),验明正身后再放行。再配合软件陷阱,基本上可以将大多数PC指针弹飞的现象检测到。到了程序错误处理段,要杀要剐(冷启动还是热启动)就由您了。

仅以一条代码来揭示程序飞跑的本质!750102H ;MOV 01H,#02H ,如当前PC不是指向75H,而是指向01H或02H,那么51内的指令译码器将把她们忠实地翻译成AJMP X01H 或 LJMP XXXXH 而XX01H XXXXH又是什么呢?天知道!这样恶性飞跑下去那还不死定!改革一下:

CLR A ;0C4H

INC A ;04H

MOV R1,A ;0F9H

INC A ;04H

MOV @R1,A ;86H

每一字节代码都不能在生成跳转和循环,且都是单字节指令!往那跑去?跑出去了都要自己回来!“在家”千日好!“跳出”事事难嘛!这样只要平时习惯了用累加器和寄存器把数倒一倒,把那些危险代码都给倒掉,这样虽说给PC的“足”上多加了两字节的“包”可它不好“跑”啊!“足包”====跑!有朋友会问:要是PC抓做02H--LJMP 又有抓做了老鼻子远的XXH,再抓做隔壁的YYH不就没用了吗?提这样的问题只有ZENYIN这种钻牛角得才会提!PC那一位最活跃啊?PC0啊!要“扯拐”显然发生在她身上,至于那PC15同志啊,睡得更死猪一样,雷爆(强干扰)来了都打不醒?此外如果干扰都强到了PC高位都出错的地步!关电!关电!不干了!“不是我们不行而是敌人太强大”!反过来要是敌人在你的专政下,只是偶尔出来捣捣乱,但一出来就冲到屁西(PC)高层,就要问问是不是你的王国根基(硬件)有问题了?而非出在意识形态(软件)上!硬件为本!软件为标!标本兼治铸就坚强体魄,方能百毒不侵!

经验之二:不要轻信软件狗

关于软件狗的讨论,论坛上多矣。匠人也曾经查阅过许多关于软件狗的文章。有些大师确实提出了一些比较有技巧性的方法。但是,匠人的忠告是:不要轻信软件狗!其实,软件狗相当于软件的一种自律行为。一般的思路都是通过设立一个计数器,在计时中断中对其+1,在主程序的适当地方对其清零。如果程序失控了,清零指令未被执行,但中断造常发生,则计数器溢出(狗狗叫了)。但是这里有个问题:万一干扰导致中断被屏蔽了,那软件狗就永远不会叫了!——针对这种可能,有人提出在主程序中反复刷新中断使能标志,保证不让中断被屏蔽。——但万一程序飞到某个死循环中去了,不再执行“刷新中断使能标志”这一功能了,还是有可能把狗狗活活饿死。

所以,匠人的观点是:看门狗必须拥有独立的计数器。(即硬件看门狗)好在现在好多芯片都提供了内部WDT。这种狗都是自带计数器的。即使干扰导致程序失控,WDT还是会造常计数直到溢出。当然,匠人也没有要将软件狗一棍子全部打死的意思。毕竟不管是软狗还是硬狗,逮到耗子就是好狗嘛(狗拿耗子——多管闲事?)。如果哪位训狗专家确实养过一条能看门的好软件狗,请牵出来让大伙瞧瞧。

经验之三:话说RAM冗余技术

所谓的RAM冗余,就是:

1、将重要的数据信息备份2份(或以上)并存放在RAM中不同的区域(指地址不相连)。

2、当平时对这些数据进行修改时,同时也更新备份。

3、当干扰发生并被拦截到“程序错误处理段”中时,将数据与备份做比较,采用表决方式(少数服从多数)选出正确(或可能正确?)的那个。

4、备份越多,效果越好。(当然,你得有足够的存储空间)。

5、只备份最最原始的数据。中间变量(指那些可以从原始数据重新推导出来的数据)不必备份,

注:1、这种思路的理论依据,据说是源于一种“概率论”,即一个人被老婆打肿脸的概率是很大的,但如果他捂着脸去上班却发现全公司每个已婚男人的脸都青了,这种概率是很小的。同理,一个RAM寄存器数据被冲毁的概率是很大的,但地址不相连的多个RAM同时被冲毁的概率是很小的。

2、前两年,小匠学徒时,用过一次这种方法,但效果不太理想。当时感觉可能是概率论在我这失效了?现在回想起来,可能是备份的时机选的不好。结果将已经冲毁的数据又备份进去了。这样以来,恢复出来的数据自然也就不对了。

经验之四:话说指令冗余技术

前面有个朋友问到指令冗余,按匠人的理解,指令冗余,就是动作冗余。举个例子,你要在某个输出口上输出一个高电平去驱动一个外部器件,你如果只送一次“1”,那么,当干扰来临时,这个“1”就有可能变成“0”了。正确的处理方式是,你定期刷新这个“1”。那么,即使偶然受了干扰,它也能恢复回来。除了I/O口动作的冗余,匠人强烈建议大家在下面各方面也采用这种方法:

1、LCD的显示。有时,也许你会用一些LCD的专用驱动芯片(如HT1621),这种芯片有个好处,即你只要将显示数据传送给它,它就会不断的自动扫描LCD。但是,你千万不要以为这样就没你啥事了。正确的处理方式是,要记得定期刷新送显数据(即使显示内容没有改变)。对于CPU中自带LCD DRIVER 的,也要定期刷新LCD RAM。

2、中断使能标志的设置。不要以为你在程序初始化段将中断设置好就OK了。应该在主程序中适当的地方定期刷新一下,以免你的中断被挂起来。

3、其它一些标志字和参数寄存器(包括你自己定义的),也要记得常常刷新。

4、其它一些你认为有必要反复刷新的地方。

经验之五:10种软件滤波方法

下面奉献——匠人呕心沥血搜肠刮肚冥思苦想东拼西凑整理出来的10种软件滤波方法:

1、限幅滤波法(又称程序判断滤波法)

A、方法:根据经验判断,确定两次采样允许的最大偏差值(设为A),每次检测到新值时判断:如果本次值与上次值之差<=A,则本次值有效。如果本次值与上次值之差>A,则本次值无效,放弃本次值,用上次值代替本次值

B、优点:能有效克服因偶然因素引起的脉冲干扰。

C、缺点:无法抑制那种周期性的干扰,平滑度差。

2、中位值滤波法

A、方法:连续采样N次(N取奇数),把N次采样值按大小排列,取中间值为本次有效值.

B、优点:能有效克服因偶然因素引起的波动干扰,对温度、液位的变化缓慢的被测参数有良好的滤波效果。

C、缺点:对流量、速度等快速变化的参数不宜。

3、算术平均滤波法

A、方法:连续取N个采样值进行算术平均运算。N值较大时:信号平滑度较高,但灵敏度较低;N值较小时:信号平滑度较低,但灵敏度较高。N值的选取:一般流量,N=12;压力:N=4

B、优点:适用于对一般具有随机干扰的信号进行滤波,这样信号的特点是有一个平均值,信号在某一数值范围附近上下波动。

C、缺点:对于测量速度较慢或要求数据计算速度较快的实时控制不适用,比较浪费RAM。

4、递推平均滤波法(又称滑动平均滤波法)

A、方法:把连续取N个采样值看成一个队列,队列的长度固定为N,每次采样到一个新数据放入队尾,并扔掉原来队首的一次数据.(先进先出原则),把队列中的N个数据进行算术平均运算,就可获得新的滤波结果。N值的选取:流量,N=12;压力:N=4;液面,N=4~12;温度,N=1~4

B、优点:对周期性干扰有良好的抑制作用,平滑度高,适用于高频振荡的系统。

C、缺点:灵敏度低 ,对偶然出现的脉冲性干扰的抑制作用较差,不易消除由于脉冲干扰所引起的采样值偏差,不适用于脉冲干扰比较严重的场合,比较浪费RAM

5、中位值平均滤波法(又称防脉冲干扰平均滤波法)

A、方法:相当于“中位值滤波法”+“算术平均滤波法”。连续采样N个数据,去掉一个最大值和一个最小值,然后计算N-2个数据的算术平均值。N值的选取:3~14

B、优点:融合了两种滤波法的优点,对于偶然出现的脉冲性干扰,可消除由于脉冲干扰所引起的采样值偏差。

C、缺点:测量速度较慢,和算术平均滤波法一样,比较浪费RAM。

6、限幅平均滤波法

A、方法:相当于“限幅滤波法”+“递推平均滤波法”,每次采样到的新数据先进行限幅处理,再送入队列进行递推平均滤波处理 。

B、优点:融合了两种滤波法的优点,对于偶然出现的脉冲性干扰,可消除由于脉冲干扰所引起的采样值偏差。

C、缺点:比较浪费RAM。

7、一阶滞后滤波法

A、方法:取a=0~1,本次滤波结果=(1-a)*本次采样值+a*上次滤波结果。

B、优点:对周期性干扰具有良好的抑制作用,适用于波动频率较高的场合。

C、缺点: 相位滞后,灵敏度低,滞后程度取决于a值大小,不能消除滤波频率高于采样频率的1/2的干扰信号。

8、加权递推平均滤波法

A、方法:是对递推平均滤波法的改进,即不同时刻的数据加以不同的权。通常是,越接近现时刻的数据,权取得越大。给予新采样值的权系数越大,则灵敏度越高,但信号平滑度越低。

B、优点:适用于有较大纯滞后时间常数的对象和采样周期较短的系统。

C、缺点:对于纯滞后时间常数较小,采样周期较长,变化缓慢的信号不能迅速反应系统当前所受干扰的严重程度,滤波效果差。

9、消抖滤波法

A、方法:设置一个滤波计数器将每次采样值与当前有效值比较:如果采样值=当前有效值,则计数器清零如果采样值<>当前有效值,则计数器+1,并判断计数器是否>=上限N(溢出),如果计数器溢出,则将本次值替换当前有效值,并清计数器 。

B、优点:对于变化缓慢的被测参数有较好的滤波效果,可避免在临界值附近控制器的反复开/关跳动或显示器上数值抖动。

C、缺点:对于快速变化的参数不宜,如果在计数器溢出的那一次采样到的值恰好是干扰值,则会将干扰值当作有效值导入系统。

10、限幅消抖滤波法

A、方法:相当于“限幅滤波法”+“消抖滤波法” 先限幅,后消抖。

B、优点: 继承了“限幅”和“消抖”的优点改进了“消抖滤波法”中的某些缺陷,避免将干扰值导入系统。

C、缺点:对于快速变化的参数不宜。

IIR 数字滤波器

A. 方法:确定信号带宽, 滤之。 Y(n) = a1*Y(n-1) + a2*Y(n-2) + . + ak*Y(n-k) + b0*X(n) + b1*X(n-1) + b2*X(n-2) + . + bk*X(n-k)。

B. 优点:高通,低通,带通,带阻任意。设计简单(用matlab)

C. 缺点:运算量大。

文章来源:互联网(版权归原著作者所有)

围观 290

寻找操作数可以通过直接给的方式(立即寻址)和直接给出数所在单元地址的方式(直接寻址),这就够了吗?

看这个问题,要求从30H单元开始,取20个数,分别送入A累加器。

就我们目前掌握的办法而言,要从30H单元取数,就用MOV A,30H,那么下一个数呢?是31H单元的,怎么取呢?还是只能用MOV A,31H,那么20个数,不是得20条指令才能写完吗?这里只有20个数,如果要送200个或2000个数,那岂不要写上200条或2000条命令?这未免太笨了吧。

为什么会出现这样的状况?

是因为我们只会把地址写在指令中,所以就没办法了,如果我们不是把地址直接写在指令中,而是把地址放在另外一个寄存器单元中,根据这个寄存器单元中的数值决定该到哪个单元中取数据,比如,当前这个寄存器中的值是30H,那么就到30H单元中去取,如果是31H就到31H单元中去取,就可以解决这个问题了。

怎么个解决法呢?

既然是看的寄存器中的值,那么我们就可以通过一定的方法让这里面的值发生变化,比如取完一个数后,将这个寄存器单元中的值加1,还是执行同一条指令,可是取数的对象却不一样了,不是吗。通过例子来说明吧。

这个例子中大部份指令我们是能看懂的,第一句,是将立即数20送到R7中,执行完后R7中的值应当是20。第二句是将立即数30H送入R0工作寄存器中,所以执行完后,R0单元中的值是30H,第三句,这是看一下R0单元中是什么值,把这个值作为地址,取这个地址单元的内容送入A中,此时,执行这条指令的结果就相当于MOV A,30H。第四句,没学过,就是把R0中的值加1,因此执行完后,R0中的值就是31H,第五句,学过,将R7中的值减1,看是否等于0,不等于0,则转到标号LOOP处继续执行,因此,执行完这句后,将转去执行MOV A,@R0这句话,此时相当于执行了MOV A,31H(因为此时的R0中的值已是31H了),如此,直到R7中的值逐次相减等于0,也就是循环20次为止,就实现了我们的要求:从30H单元开始将20个数据送入A中。这也是一种寻找数据的方法,由于数据是间接地被找到的,所以就称之为间址寻址。

为什么我们需要这么多的寻址方式呢?这是为了在效率和方便性上找一个平衡。立即数寻址和寄存器寻址在效率上是最快的,但寄存器仅有几个非常宝贵不可能将操作数都存入其中等待使用,立即数的使用场合也非常有限,这样就需要将数据保存在内存中,然后使用直接寻址、寄存器间接寻址、寄存器相对寻址、基址加变址寻址、相对基址加变址寻址这些寻址方式将内存中的数据移入寄存器中。

围观 399

单片机是一种集成电路芯片,是采用超大规模集成电路技术把具有数据处理能力的中央处理器CPU随机存储器RAM、只读存储器ROM、多种I/O口和中断系统、定时器/计时器等功能(可能还包括显示驱动电路、脉宽调制电路、模拟多路转换器、A/D转换器等电路)集成到一块硅片上构成的一个小而完善的微型计算机系统,在工业控制领域的广泛应用。从上世纪80年代,由当时的4位、8位单片机,发展到现在的32位300M的高速单片机。

1、万事开头难、要勇敢迈出第一步。
  
开始的时候,不要老是给自己找借口,说KEIL不会建项目啦、没有实验板啦之类的。遇到困难要一件件攻克,不会建项目,就先学它,这方面网上教程很多,随便找找看一下,做几次就懂了。然后可以参考别的人程序,抄过来也无所谓,写一个最简单的,让它运行起来,先培养一下自己的感觉,知道写程序是怎么一回事,无论写大程序还是小程序,要做的工序不会差多少,总得建个项目,再配置一下项目,然后建个程序,加入项目中,再写代码、编译、生成HEX,刷进单片机中、运行。必须熟悉这一套工序。个人认为,一块学习板还是必要的,写好程序在上面运行一下看结果,学习效果会好很多,仿真器就看个人需要了。单片机是注重理论和实践的,光看书不动手,是学不会的。
  
2、知识点用到才学,不用的暂时丢一边。
  
厚厚的一本书,看着人头都晕了,学了后面的,前面的估计也快忘光了,所以,最好结合实际程序,用到的时候才去看,不必说非要把书从第一页看起,看完它才来写程序。比如你写流水灯,完全就没必要看中断的知识,专心把流水灯学好就是了,这是把整本书化整为零,一小点一小点的啃。
  
3、程序不要光看不写,一定要自己写一次。
  
最开始的时候,啥都不懂,可以抄人家的程序过来,看看每一句是干什么用的,达到什么目的,运行后有什么后果,看明白了之后,就要自己写一次,你会发现,原来看明白别人的程序很容易,但到自己写的时候却一句也写不出来,这就是差距。。。当你自己能写出来的时候,说明你就真的懂了。 

4、必须学会掌握调试程序的方法。
  
不少人写程序,把代码写好了,然后一运行,不是自己想要的结果,就晕了,然后跑到论坛上发个帖子,把程序一贴,问:为什么我的程序不能正常运行?然后就等别人来给自己分析。这是一种很不好的行为,应该自己学会发现问题和学会如何解决问题。这就需要学习调试程序的方法,比如KEIL里,可以下断点啦,查看寄存器内容等等,这些都是调试程序的手段,当你发现你写的程序运行结果和你想象中不一样的时候,你可以单步,也可以下断点,然后跟踪,查看各相关寄存器内容,看看程序运行过中是不是有什么偏差,找出影响结果的地方,改正过来。这一个过程非常重要,通过程序的排错,你可以学到的知识是书上得不到的。  

5、找到解决问题思路比找到代码更重要。
  
我们用单片机来控制周边器件,达到我们想到的目的,这是一个题目,而如何写出一个程序,来控制器件按你想要的结果去运作,这个就是解题的思路。要写程序,就得先找到解决问题的思路,你学会找出这个解题思路,比你找到代码更为重要。不少人很喜欢找人家的代码,有的人甚至有了代码就直接复制到自己的程序中,可以说,这不是一种学习的态度,无助于你编程水平的提高。我几乎不怎么看人家的代码,多数时候是看别人的思路,有方框图最好,没有的话文字说明也可以,要从代码中看出别人处理问题的思路,是相当困难的,特别是大型的程序,看起来是非常的累人,所以现在我也明白了,以前读书时说的程序流程图很重要,现在算是知道了。当你知道一个问题怎么去解决了,那么剩下的只是你安排代码去完成,这就已经不是什么问题了。(
http://www.diangon.com版权所有)举个例子:数码管动态扫描,没写过的初学者可能搞不清是怎么回事,其实,就是分时让每一时间段时只控制一只数码管显示数字,几只数码管轮流显示,由于速度很快,人眼的看起来是全部数码管都亮的。明白是这么一回事,事情就好办了,剩下的事情,无非是你安排让一只只数码管轮流显示出相应的数值。显示数字,然后延时一下,再下一只显示数字,延时,知道是这样,我们实际程序上只要做到这样就可以:往段口送段码,然后打开位选显示一只,延时一下,再关闭位选,再送出段码,再打开另一只位选..仅此而已。有了解决问题的思路,我们就能问题拆分开来,然后逐一的解决,如果动态扫描的原理都没懂,不知道如何做,那么这个程序是怎么也写不出来的。
  
6、开动脑筋,运用多种方法,不断优化自己的程序。   

想想用各种不同方法来实现同一功能。这是一个练习和提高的过程,一个问题,你解决了,那么你再想想,能不能换种写法,也可以实现同一功能,或者说,你写出来的代码,能不能再精简一点,让程序执行效率更高,这个过程,就是一个进步的过程。很多知识和经验的获得,并不是直接写在书让你看就可以得到的,需要自己去实践,开动脑筋,经验才能得到积累,编程水平才能有所提高。
  
7、看别人的代码,学习人家的思路。
  
这个在学习初期是很有用,通过看别人的代码,特别是有多年编程经验的人写出的具有一定水平的代码,可以使自己编程水平得到迅速的提高,同时,也可以结合别人的编程手法,与自己的想法融合在一起,写出更高水平的代码,从中得到进步。但要注意,切忌将学习变成抄袭,更不是抄袭完了就认为自己学会了,这样做只会使你退步。
  
8、尝试编写一下综合应用的程序。
  
从流水灯学起,到动态扫描,再到中断,那么,你可以试试写一下时钟这种综合性应用的程序,不要小看时钟,要写好它不是一件容易的事情,它包括了单片机大部分的知识,比如有按键(IO读取)、动态扫描(IO输出)、中断等,如何协调好各功能模块正常工作,才是编程者需要学习的地方,当你单独写一个功能的时候,比如按键读取,你可能感觉很容易,因为你的程序啥也不做,只是读按键。但把它和其它功能混合在一起,如何在整个程序运行中使每一部分都正常工作,这就不是写一个按键读取这么容易的事情,功能模块之间有可能会互相影响,比如你需要让数码管既能显示,又要去处理按键读取,怎么使这两部分都正常工作,这就是一个协调过程。当你有了这个处理协调能力,你就算是入门了。 

9、着重于培养解决问题的能力,而不是具体看自己编写了多少代码或者做过什么。
  
“学单片机重点在于学习解决问题的思路,而不是局限于具体的芯片类型和语言”这一直是我的座右铭,是我学单片机多年来感悟出来的。经常看到有人说“你会驱动XX芯片,真牛啊”“你搞过XX项目,真厉害”之类的话,其实这是非常片面的,搞过XX芯片,搞过XX项目,只能说明你做过这一项目,它只是你的业绩,并不是代表能力就一定高。真正的能力应该是:“遇到没有解决过的问题或器件,能利用自己已学的知识,迅速找到解决问题的方法。”这个才是能力。写程序的过程就是一个创造的过程,几乎没有完全一样的项目,每次你遇上的几乎都不相同,所以你拥有的必须是你面对新项目时的创造能力,而不是标榜着你以往做过多少项目。当然,业绩也能从另一侧面反映你的经验和水平。
 
10、如果有可能,多学习计算机专业的知识,比如数据结构等。
  
这些是你解决问题的基础知识,你把这些知识应用得越好,就会发现越容易找到解决问题的方法,这就是为什么一个学计算机专业的人编的程序和一个非计算机专业的人编的程序有差异的原因。也是一个菜鸟进军到高手所要配备的知识。如果我们把编程分为宏观编程和微观编程,那么微观编程就是写具体的代码,比如控制某某器件的语句;而宏观编程就是如何对整个程序进行布局、安排,使功能模块以你想要的方式去运行,得出你想要的结果。如前所说“会控制XX器件”这些只能算是微观编程,能做到这一步还只能算是菜鸟级别,如果面对一个新的器件,你心里没底,没把握去写这个控制程序,那说明你还是一个初级的菜鸟。当你有了一定的编程经验,控制过相当数量的器件之后,你就会发现,控制器件这些工作都是相似的、重复的工作,体现不出编程的水平,最多也是写得好与不好的差别,只能算是一些小技巧的应用。而对整个程序进行布局、安排这些才是最头痛的事情,能达到宏观编程和微观编程都做好才是真正的高手。对于规模越大的程序,越能体现出这一点。
  
11、面对一个新项目时,多自己开动脑筋,不要急于找别人的程序。  

有不少人面对一个新项目时,第一步想到的就是网上找别人写过的代码,然后抄一段,自己再写几句,凑在一起就完成任务,这虽然可能是省时间,但绝对不利你的学习。当你接到一个新项目时,应该先自己构思一下整个程序的架构,想想如何来完成,有可能的话,画一个流程图,简单的可以画在脑子里,对程序中用到的数据、变量有一个初步的安排,然后自己动手去写,遇到实在没办法解决的地方,再去请教别人,或看别人是怎么处理的,这样首先起码你自己动过脑想过,自己有自己的思路,如果你一开始就看别人的程序,你的思维就会受限在别人的思维里,自己想再创新就更难了,这样你自己永远也没办法提高,因为你是走在别人的影子里。
  
12、多利用网络的搜索,学会提问题。  

一般来说,学习过程中,你遇上的问题,前人们多数也有遇上的,所以如果有什么不懂,在自己解决不了的时候,最好先到网上搜索一下,看能不能找到答案,找不到再到论坛里发问,发问也要有目的性,尽量简单明了的描述问题,让帮助你的人可以用最少的时间就看懂你说什么,毕竟人家帮助你是免费的,时间也是有限的。

围观 369

在单片机的BCD增量算式、线性化处理等过程中,都会遇到一个共同的问题,那就是小数的运算。在单片机当中,对于小数的表现方法一般只有两种,一种是浮点数,一种是定点数。本文就将对单片机中的浮点数进行概述并对其汇编程序设计进行介绍。

浮点数结构有其自身的优点,其能够以固定的字节长度保持相对精度不变,用较少的字节表示很大的数的范围,便于存储和运算,在处理的数据范围较大和要求精度较高时,采用浮点数。

浮点数概念

常用科学计数法来表示一个十进制数如:

l234.75=1.23475E3=1.23475×103(10的3次方)

在数据很大或很小时,采用科学计数法避免了在有效数字前加0来确定小数点的位置,突出了数据的有效数字的位数,简化了数据的表示,可以认为科学计数法就是十进制数的浮点数表示方法。

在二进制中,也可以用类似的方法来表示一个数,如:

1234.75=10011010010.11(二进制)=0.1001101001011×211(2的11次方)

一般表达式为:

N=S×2p

在这种表示方法中,数值由4个部分组成,即尾数S及符号,阶码P及符号。

在二进制中,通过定义相应字节或位来表示这4部分,就形成了二进制浮点数。二进制浮点数可以有多种不同的表示方法,下面是一种常见的三字节浮点数的格式:

其中尾数占16位,阶码占6位,阶符占1位,数符占1位。阶码通常用补码来表示。

在这种表示方法中,小数点的实际位置要由阶码来确定,而阶码又是可变的,因此称为浮点数。

1234.75用这种格式的浮点数表示就是:

000010111001101001011000

用十六进制表示为:

1234.75=0B9A58H

-1234.75=4B9A58H

0.171875=043B00H

-0.171875=443B00H

三字节浮点数所能表示的最大值为:

1×263=9.22×1018

能表示的最小数的绝对值为:

0.5×2-63=5.42×10-20

其所表示的数的绝对值范围=(5.42×10-20~9.22×1018),由此可以看到,比三字节定点数表示的数的范围大得多。

按同样方法可以定义一个4字节的浮点数,以满足更高精度的需要。

规格化浮点数

同一个数用浮点数表示可以是不同的,如:

1234.75=0B9A58H=0C4D2CH=0D2696H

虽然这几种表示其数值是相同的,但其尾数的有效数字的位数不同,分别为16位、15位和14位。在运算过程中,为了最大限度地保持运算精度,应尽量增加尾数的有效位数,这就需要对浮点数进行规格化处理。

在只考虑用二进制原码表示尾数时,尾数的最高位为l,则该浮点数为规格化浮点数。在规格化浮点数中,用尾数为0和最小阶码表示0,三字节规格化浮点数的0表示为410000H。

浮点数在运算之前和运算之后都要进行规格化,规格化过程包括以下步骤:

首先判断尾数是否为0,如果为0,规格化结果为410000H;(如果尾数不为0,判断层数的最高位是否为1,如果不为1,尾数左移,阶码减1。)

再判断层数的最高位是否为1,如果不为1,继续进行规格化操作,如果为1,则规格化结束。

通过以上的文章可以看到,浮点数结构有着较为明显的应用场景。在需要处理的数据范围较大或者对于数据的请求范围较高时,适合使用单片机浮点数来进行运算,浮点数能够利用自身固定的字节长度来保持相对精度。设计者可根据自己不同的需要来进行选择,希望大家在阅读过本文之后能够有所收获。

文章来源:网络(版权归原著作者所有)

围观 1169

在进行单片机开发时,经常都会出现一些很不起眼的问题,这些问题其实都是很基础的C语言知识点,是一些小细节。

但是正是因为很基础,又都是小细节,所以我们往往容易忽视它们。结果有时候我们会花很长的时间纠结一个问题,迟迟找不到问题的所在。

当发现原因竟然是这么的简单和不起眼时,大家都会感到痛不欲生。这些问题要记录下来,时刻提醒自己!!

1、! 和 ~ 不一样

! 是逻辑非符号,~ 是位取反符号。

对IO口某个引脚赋值时不要错用 ! 如

2、<< 和 >> 的优先级低于+、-

比如要实现c=x*2+1,没有加括号会出错

3、移位要防止溢出

其实用移位代替乘除法是个不错的方法,笔者很喜欢拿到一段代码后用移位代替乘除法来进行优化。不过有时候却会出现问题,比如溢出问题。当很明显可能溢出的话我们是会注意的,比如

但是有时候这个问题是不明显的,比如当移位出现在数组索引或函数参数时,有段用液晶显示字符的代码如下

我们可以用左移运算来代替乘法进行优化,如

这本是一个好方法,但是事实上上面的代码是错的。当执行c<<4时,因为没有明显的赋值过程,我们可能认为没问题,而事实上c的高位已经丢失了,所以得到错误的结果。一个可行的做法是先进行强制转换,如

4、无符号数和有符号数混合运算都会被强制转换为无符号数运算

当一个有符号数和一个无符号数进行算术运算时,系统会自动将有符号数强制转换为无符号数再进行运算(即使你使用有符号数强制类型转换),如下面两种写法的运输结果是一样的

5、局部变量要初始化

局部变量没有初始化的话,因为单片机每次为他分配的是同一个内存区域,当你在函数中是这么使用局部变量时,就可能出问题:

如果第一次调用fun时,a传递的值为0,那么flag = 0x01;执行if(flag&0x01)后面的代码。以后再调用fun时,即使a不为0,但flag依然使用之前的内存区域,所以其值一直为0x01,一直执行的是if后面的代码,而不是else后面的。

如果要避免这个错误,平时要养成对局部变量初始化的习惯。

围观 306

单片机的选型是一件重要而费心的事,如果选型得当,则做出来的产品就会性价比较高,且工作稳定;反之,则可能会造成产品成本过高或影响产品正常运行,甚至可能根本就达不到预先设计要求。一般来说,总的选型原则是:(1)“芯片含有(功能或数量)略大于设计需求”,“设计需求尽可能(用)芯片完成(少用外围器件)”;(2)“选大(大厂)不选小,选多(供应量多)不选少,选名(名牌)不选渺(飘渺,不知详情的厂子),选廉(廉价)但要好(质量保证)”。具体要从单片机应用的技术性、实用性和开可发性等方面来考虑:

1、内存

单片机FLASH的容量根据程序的大小确定,FLASH容量必须大于代码量。举例来说,如果你的代码量大约50 KB,那么建议你选择FLASH容量为64 KB或128 KB的单片机。

2、速度

单片机的运行速度首先看时钟频率,一般情况对于同一种结构的单片机,时钟频率越高速度越快。如果你的设计对速度要求很高,那么要选择一个运行速度较快的单片机。例如,一般情况下,电机控制应用大多采用100ksps或更高的采样速率,因此当单片机用于电机控制时,时钟频率要足够高。总之,在选用单片机时要根据产品需要选择时钟频率,不要片面追求高速度,时钟频率越高功耗也就越大。此外,单片机的稳定性、抗干扰性等参数基本上跟单片机的运行速度成反比。因此,要尽量寻找可以在很高的时钟频率下运行而功耗又不高的单片机。

3、外设需求

如果你的设计需要ADC、SPI、GPIO、USB等之类的外设,那么你需要寻找一款集成所有这些外设的单片机。因为,使用一个具有上述外设的单片机显然比使用一个普通的单片机及外围加一个单独的ADC更为经济。此外,外设集成于单片机同时也意味着更低的功耗,因为没有可以产生功耗的外围电路,也没有用于连接外围电路的能产生功耗的敷铜,只有单片机本身产生功耗。

4、方便的开发工具

这是个非常重要的方面,因为开发工具可以极大地影响你所设计的产品的功耗。很多公司都已经开发出了具有代码优化功能的编译器,所以当你编译代码的时候,编译器会告知具体编译信息,你可以根据编译信息优化代码以降低功耗。举例来说,如果你的设计需要用到ADC、UART和GPIO等外设,你就需要初始化这些器件,但是设计中使用UART是有条件的(仅用于调试时显示结果),此时编译器会提示你禁用这个外设以降低功耗。必须得说这种智能化的开发工具对开发者来说是一种福音。

5、未来需求和兼容性

设计者在设计产品时需要考虑产品未来可能需要升级等之类的问题。例如,若需要给设计增加某些功能,那么可能需要增加内存、外设等,还可能需要加提高单片机的运行速度。因此,在单片机的选型上需要在当前设计需求以及未来设计上寻找平衡,以满足不同程度的要求。

6、成本

一个好的设计不仅要功能完善,而且要满足成本要求,如果无法控制成本,再好的设计也是枉然。因此,需要尽可能地降低单片机甚至整个产品的成本。

7、工作电压(VCC)

单片机的工作电压是指可以让其正常工作所需要提供的电压。工作电压越高,单片机的功耗也就越大。因此,为了降低产品功耗,必须要尽可能地降低工作电压。

除此之外,我还要建议设计者根据具体产品需求选择合适芯片架构。若仅是个简单的控制应用(如照明系统、电子玩具等),那么并不需要一个像ARM那样具有复杂架构的芯片。此外,对于低功耗设计,单片机必须具有睡眠模式,基于中断操作的睡眠模式/低功耗模式的使用是降低功耗的一个标准的行业惯例。

最后再来一句老生常谈:不要拘泥与芯片是否先进,单片机只是一个工具,真正的功夫在于你的专业知识,要用最合适的芯片做出最合适的产品。

围观 380

学习使用单片机就是理解单片机硬件结构,以及内部资源的应用,在汇编或C语言中学会各种功能的初始化设置,以及实现各种功能的程序编制。

第一步:数字I/O的使用

使用按钮输入信号,发光二极管显示输出电平,就可以学习引脚的数字I/O功能,在按下某个按钮后,某发光二极管发亮,这就是数字电路中组合逻辑的功能,虽然很简单,但是可以学习一般的单片机编程思想,例如,必须设置很多寄存器对引脚进行初始化处理,才能使引脚具备有数字输入和输出输出功能。每使用单片机的一个功能,就要对控制该功能的寄存器进行设置,这就是单片机编程的特点,千万不要怕麻烦,所有的单片机都是这样。要注意的是两个功能使用同一组I/O口,比如LCD和LED例程众都是使用PB这一组的,如果两者结合,会有冲突,达不到预期的效果,建议不同的模块使用不同的IO口。

第二步:定时器的使用

学会定时器的使用,就可以用单片机实现时序电路,时序电路的功能是强大的,在工业、家用电气设备的控制中有很多应用,例如,可以用单片机实现一个具有一个按钮的楼道灯开关,该开关在按钮按下一次后,灯亮3分钟后自动灭,当按钮连续按下两次后,灯常亮不灭,当按钮按下时间超过2s,则灯灭。数字集成电路可以实现时序电路,可编程逻辑器件(PLD)可以实现时序电路,可编程控制器(PLC)也可以实现时序电路,但是只有单片机实现起来最简单,成本最低。

定时器的使用是非常重要的,逻辑加时间控制是单片机使用的基础。

第三步:中断

单片机的特点是一段程序反复执行,程序中的每个指令的执行都需要一定的执行时间,如果程序没有执行到某指令,则该指令的动作就不会发生,这样就会耽误很多快速发生的事情,例如,按钮按下时的下降沿。要使单片机在程序正常运行过程中,对快速动作做出反应,就必须使用单片机的中断功能,该功能就是在快速动作发生后,单片机中断正常运行的程序,处理快速发生的动作,处理完成后,在返回执行正常的程序。

中断功能使用中的困难是需要精确地知道什么时候不允许中断发生(屏蔽中断)、什么时候允许中断发生(开中断),需要设置哪些寄存器才能使某种中断起作用,中断开始时,程序应该干什么,中断完成后,程序应该干什么等等。中断学会后,就可以编制更复杂结构的程序,这样的程序可以干着一件事,监视着一件事,一旦监视的事情发生,就中断正在干的事情,处理监视的事情,当然也可以监视多个事情,形象的比喻,中断功能使单片机具有吃着碗里的,看着锅里的功能。以上三步学会,就相当于降龙十八掌武功,会了三掌了,可以勉强护身。

第四步:与PC机进行RS232通信

单片机都有USART接口,其中很多型号都具有两个USART接口。USART接口不能直接与PC机的RS232接口连接,它们之间的逻辑电平不同,需要使用一个芯片进行电平转换。USART接口的使用是非常重要的,通过该接口,可以使单片机与PC机之间交换信息,虽然RS232通信并不先进,但是对于接口的学习是非常重要的。正确使用USART接口,需要学习通信协议,PC机的RS232接口编程等等知识。试想,单片机实验板上的数据显示在PC机监视器上,而PC机的键盘信号可以在单片机实验板上得到显示,将是多么有意思的事情啊!

第五步:学会A/D转换

单片机通常带有A/D转换器,通过这些A/D转换器可以使单片机操作模拟量,显示和检测电压、电流等信号。学习时注意模拟地与数字地、参考电压、采样时间,转换速率,转换误差等概念。使用A/D转换功能的简单的例子是设计一个电压表。

第六步:学会PCI、I2C接口和液晶显示器接口

这些接口的使用可以使单片机更容易连接外部设备,在扩展单片机功能方面非常重要。

第七步:学会比较、捕捉、PWM功能

这些功能可以使单片机能够控制电机,检测转速信号,实现电机调速器等控制起功能。如果以上七步都学会,就可以设计一般的应用系统,相当于学会十招降龙十八掌,可以出手攻击了。

第八步:学习USB接口、TCP/IP接口、各种工业总线的硬件与软件设计

学习USB接口、TCP/IP接口、各种工业总线的硬件与软件设计是非常重要的,因为这是当前产品开发的发展方向。

到此为止,相当于学会15招降龙十八掌,但还不到打遍天下无敌手的境界。即使如此,也算是单片机大侠了。

围观 303

页面

订阅 RSS - 单片机