单片机应用小技巧.docx
- 文档编号:12389277
- 上传时间:2023-04-18
- 格式:DOCX
- 页数:48
- 大小:45.33KB
单片机应用小技巧.docx
《单片机应用小技巧.docx》由会员分享,可在线阅读,更多相关《单片机应用小技巧.docx(48页珍藏版)》请在冰豆网上搜索。
单片机应用小技巧
第二章单片机应用小技巧
进入本章,我想你已经具备了基本的单片机功底,最基本的要求是指可以用某种单片机进行一
些简单程序开发。
通过本章内容的学习,一定会让你在产品开发方面的思维得到一些启迪,当你看
完本章后不妨回过头去看看自己以前的产品或程序,如果你很容易就从以前的程序或产品中找出自
己之前存在的不足,那恭喜你,再做两个项目你就可以向老板要求加薪。
本章内容大都是以实际工作经验为基础总结而得,内容多少不一,有的章节可能颇费纸墨,有
的却可能只是寥寥数语,存在这种差异的原因是有些例子技巧性主要体现在实现的细节方面,而有
的却只要找到方法就算成功。
2.1.用IO模拟接口
有时选用的单片机并不提供外围器件所需的接口,这时可以用IO来模拟所需接口,只要IO口
能满足接口规定的时序,就能用IO模拟的接口来和外围器件进行通讯。
用IO口模拟接口的方法对于大家我相信是一点就明,但要使IO口模拟的接口工作更加可靠稳
定并不简单,往往需要在一些细节上多加处理才能做好,接下来我会通过用IO模拟UART和I2C来
告诉大家,应该通过哪些细节展现你的技术功底。
IO模拟UART
模拟UART非常简单,一条IO模拟发送的TX,一条IO模拟接收的RX,另外将地GND引出就可
以实现UART功能。
在硬件上基本不用考虑太多,只需要注意IO口上下拉电阻的选择,如果IO口
内部可以选择设置上下拉电阻,必须设为上拉电阻,如果IO口不提供内部上下拉电阻控制最好在
外部连上10k~51k的上拉电阻。
有了上拉电阻,就可以确保TX能可靠输出高低电平,RX即使没有
和另外的设备相连也能保证读到的状态是1,这样是为了和UART通讯时序中用1来表示空闲的要求
一致。
删繁就简-单片机入门到精通
图2.1.-1IO模拟UART示意图
要用IO软件模拟UART,就需要用软件在IO_TX脚输出满足UART通讯时序的波形,还能检测出
IO_RX脚上的波形是否与UART通讯时序一致并将数据正确读回。
UART可以设置成多种工作状态,
限于篇幅这里只选用最常见的“9600/8/N/1”设置进行讲述。
“9600/8/N/1”表示波特率为9600,这个速率收发一个位大约耗时104us,8位数据位,无校
验位,1位停止位。
IO_TX的控制比较简单,先将对应IO设置成输出,然后输出1表示当前没有数据发送。
当需要
发送数据的时候,先输出一个104us宽的低电平做为起始位0,然后按104us/位的宽度按照先低位
后高位的顺序依次输出所发数据的各个位,最后将输出104us宽的高电平做为停止位1。
这样一个
字节的发送过程就全部完成,如果还有数据需要发送,按同样的方法操作即可。
IO_TX发送过程最关键的地方是保证每个位宽为104us。
最简单的方法是用代码实现延时,在
发送过程中最好关闭所有中断以保证延时准确。
如果不想去数代码有多少周期也可以用定时中断来
实现,让单片机产生一个104us的定时中断,然后在中断程序被调用后的同一时刻依次输出所有位,
这个定时中断需要最高的优先级,否则其它中断会导致时间不准。
IO_RX的控制要复杂一些,将对应IO设置成输入,然后需要让程序不停的检测IO_RX上有没有
收到0,一旦检测到0则表示一个数据开始传送,需要启动接收过程。
接收程序最好是IO_RX刚从
1变为0就能立刻检测到,这样才能保证接收过程104us间隔的时间基点准确。
检测数据开始传送的方法基本上为这三种:
①IO_RX支持负跳变触发中断用中断检测。
,
②程序用不超过52us的定时中断程序定时检测。
③程序在主程序中循环检测。
这三种方法中负跳变中断的方法最好,时间基点可以控制得非常准,后两种方法时间基点误差
相对都比较大。
检测到IO_RX从1变为0后就需要严格按照通讯时序来读取数据的各个位,我个人认为最好的
方法如下:
①在检测到数据开始传送后26us/52us/78us三个点读IO_RX状态,要求这三点必须全为0,否
则错误退出。
②然后在52+104*Nus位置读得8个数据位。
③再在104*9+26us/52us/78us三个点读IO_RX状态,要求这三点必须全为1,否则错误退出。
●注:
计算时间需要将中断响应时间考虑进去
不管是IO_TX还是IO_RX,实际上都很难准确无误的做到104us的延时间隔,如果延时和绝对
时间两者间误差达到一定限度时,就会出错,这里示意了IO_RX延时不够大的情况。
MCUEDU第59页作者:
DaiShangJu
删繁就简-单片机入门到精通
图2.1.-2UART读数据位置示意图
那为什么①和③需要做一个26us/52us/78us的特殊处理呢?
来看看我们发送0xFF时候的波
形,这个波形很简单,就是一个宽度为104us的负脉冲。
以104us间隔去读数据,我们可以正确读
回0xFF,但果以52us的间隔去读,我们会读到一个0xFE。
所以认为能读到数据就万事大吉,所读
到的结果并不一定正确。
反过来也一样,如果发送方改为以4800波特率(208us间隔)发送0xFF,
接收方以9600波特率接收会误读到0xFE。
图2.1.-3UART读数据出错示意图
①和③的特殊处理可以避免刚才所说的错误发生,这个处理是对起始位和结束位的宽度进行检
MCUEDU第60页作者:
DaiShangJu
删繁就简-单片机入门到精通
测,可以避免收发方波特率不一致产生的误接收。
那为什么只在26us/52us/78us三点处理而不是
在整个宽度内尽可能多次的判断呢?
是因为我们日常应用中波特率不随意定的,前人已经选择了一
些常用的波特率做为标准,这些波特率是300/600/1200/2400/4800/…,它们间大多数呈现两倍的
关系,26us/52us/78us三点已经能将相邻的波特率检测出来。
读的次数过多的话还会带来另外一个
麻烦,那就是每个设备和绝对波特率时间间隔之间或多或少都会存在一定误差,也就是波特率9600
的基准间隔大约为104us,实际中的设备和这个间隔都存在一定误差,误差大的甚至103us和105us
都有可能出现,如果读太多的话会让这个误差允许范围变得非常小,所以不要去读太多次,留足够
的间隔来容纳误差。
那到底可以接受多大的误差呢?
10个位总宽度为1040us,如果不做26us/52us/78us的特殊处
理,最后读停止位的时间应该是1040-52=988us,当延时间隔偏小时我们只要保证到这个点大于
104*9=936us就行,也就是负偏差最大可以到(936/988-1)*100%=5.2%,考虑到收发双方都会存在误
差,所以能接受的误差还要除以2为2.6%,实际应用中一般认为3%以内都可以被接受。
用IO模拟UART会有一些限制:
首先是对高波特率的模拟难以实现;其次是在收发数据的时候
为了保证时间间隔的精准会影响其它中断的使用;另外如果想能收发同时进行(全双工)需要比较
高的程序技巧。
如果编程语言不是汇编而是C,去数指令周期数会比较麻烦,如果想偷懒的话就是
关掉中断用示波器将延时调准。
IO模拟I2C
模拟I2C接口也只需要两条IO,分别模拟SDA和SCL,和UART不同的是模拟SCL的IO根据时
序图在不同时刻所设的输入输出状态会不同。
还是以EEPROM芯片AT24Cxx为例,单片机为主设备,用IO模拟出IO_SDA和IO_SCL来读写
AT24Cxx。
硬件连接上也没有特别要注意的地方,许多提供I2C接口的芯片都明确指出在这两条信
号线上建议加4.7k上拉电阻,以保证信号线在空闲状态下保持高电平。
这两个上拉电阻作用非常
重要,用IO模拟必须加上。
I2C接口有两个重要的时序状态,就是规定SDA/SCL从高变低和从低变高的特殊顺序产生START
和STOP信号:
SDA和SCL都为高然后SDA先变低接着SCL变低为START,SDA和SCL都为低然后SCL
先变高接着SDA变高为STOP。
图2.1.-4I2C接口START和STOP信号示意图
MCUEDU第61页作者:
DaiShangJu
删繁就简-单片机入门到精通
主设备向从设备传输一个位时先将SDA输出正确状态,然后SCL变高再变回低,SCL的这个正
脉冲使得双方完成一位的传输。
用IO模拟I2C接口来读写AT24Cxx时,首先要让单片机的IO_SDA和IO_SCL输出与通讯时序
相同的波型,然后IO_SDA根据实际情况在输入输出两种状态中相互切换。
来看一下对AT24Cxx进
行读操作的时序图,IO_SDA在整个流程中需要来回在输入和输出中切换。
图2.1.-5I2C时序图
当AT24Cxx给出ACK信号时,单片机来判断这个ACK信号,在蓝色框标识的ACK位置IO_SDA
先要从输出转成输入,然后IO_SCL变高,接下来单片机去看IO_SDA是否与ACK状态一致,最后IO_SCL
变回低IO_SDA转回输出。
下面是单片机对AT24Cxx返回的ACK信号进行处理的详细步骤:
①IO_SDA从输出转成输入,此时AT24Cxx的SDA还是输入,如果没有上拉电阻,就会形成一个
未知状态,有可能被识别成STOP信号而出错(必须加上拉电阻的原因)。
②IO_SCL从低变高,AT24Cxx的SDA输出ACK,因为IO_SDA上一步已经转为输入,两者不会产
生冲突。
③IO_SDA判断AT24Cxx的SDA输出的ACK是否正确,IO_SCL从高变回低,AT24Cxx的SDA随即
变回输入。
④IO_SDA从输入转回输出,因为AT24Cxx的SDA上一步已经转为输入,不会产生冲突,接下来
MCUEDU第62页作者:
DaiShangJu
删繁就简-单片机入门到精通
进行下一位传输。
从前面流程可以看出IO_SDA输入输出转换需要严格遵循通讯时序,否则就有可能出错或者形
成两边都是输出相互打架的局面,这是和UART接口最大的不同之处。
如果对时序不能完全肯定,
可以在主从设备的SDA间串联一个100Ω左右的电阻以起到保护作用。
(其它接口也可以根据实际情
况添加这样的保护电阻)
图2.1.-6IO模拟I2C示意图
在以往的工作经历当中,我发现不少人在写IO模拟I2C程序的时候会出现一些疏漏,他们所
写的程序正常运行都不会发生任何问题,但在长时间运行当中有时候会出现I2C设备突然不再响应
主机命令的情况。
检查他们写的程序发现大都是在ACK判断的地方存在问题,当他们检查到ACK不
对时,会错误退出,这时他们只记得返回错误信息,而忘记IO给出STOP信号,导致从设备没有终
止当前操作以释放I2C总线。
当他们所写的程序进行下一步操作时,从设备会因无法解析时序而不
知道如何响应命令,这样主机的新操作还会失败。
即便是程序完全正确,也有可能出现ACK不对的情况,比如外界的干扰信号扰乱了原本正确的
通讯时序就会导致ACK不对,如何让IO模拟I2C工作更稳定可靠,两点建议。
①在IO对I2C进行操作函数一开始先让IO_SDA和IO_SCL产生一个STOP信号,因为I2C设备
一般都默认任何时刻只要有STOP信号产生就会立刻退出并释放掉I2C总线,如果之前I2C设备出
错,这个操作会让其释放I2C总线。
②别忘记在ACK不对这类错误退出之前产生出一个STOP信号。
2.2.交流特性显神通
触摸屏还不便宜的时候,人们为了实现手指触摸功能想了许多方法,红外线就是其中一种。
我
所在的公司也尝试用红外线来实现手指触摸,我们公司的产品并不需要太高的精度,当时好象是只
要做到每个区域大约手指头大小的8*8矩阵就可以满足应用要求,这里以3*4矩阵为例。
MCUEDU第63页作者:
DaiShangJu
删繁就简-单片机入门到精通
图2.2.-1红外矩阵示意图
光敏三极管的电流和所感应到光强度成正比,红外LED发光照在光敏三极管上,流过光敏三极
管的电流大,如果有手指等物体挡在红外LED和光敏三极管之间,红外线就会被阻挡,流过光敏三
极管的电流小,电流大小的变化通过电阻以电压形式表现出来,这样就可以用光敏三极管输出电压
的高低来判定是否有手指。
采用类似扫描键盘的方法图示矩阵可以检测3*4=12点。
硬件电路和程序很快完成,开发阶段功能也都正常,好象已经满足设计要求,然而就在产品准
备生产的时候,意外情况发生,有人发现晴天在窗户附近手指怎么点都没反映。
那时候我就在烧钱
的R&D部门,所以被叫过去救急。
原因很快就分析出来,晴天室外的光照强度太大,虽然产品有用深色的塑料片来过滤可见光,
但晴天室外的可见光和红外线强度实在是太大,就是加了深色的塑料片也还能让光敏三极管饱和,
无法再体现由程序控制的红外LED的亮灭引起的变化,从而失效。
用示波器测量也验证这一分析结
果,接下来是要想解决方法,总不能说取消产品吧。
MCUEDU第64页作者:
DaiShangJu
删繁就简-单片机入门到精通
图2.2.-2红外信号处理示意图
要解决此问题就要找出一个可以将程序控制的红外LED信号和阳光分离的方法,阳光我们可以
看成一个幅度很大的直流信号,如果红外LED信号是交流信号,那就很容易从阳光中分离出来。
改
变硬件电路,将光敏三极管的输出串接一个电容,这样阳光产生的直流信号就被电容阻隔住,当程
序控制红外LED点亮时,光敏三极管会因红外LED从灭到亮的变化而产生一个跳变的交流信号,这
个信号可以传过串接的电容,然后由程序对这个交流信号进行判别。
单做这一点改动还不能完全解决问题,在阳光下光敏三极管已经工作在饱和状态,电气特性就
已经不能体现红外LED产生的变化。
光敏三极管是电流性器件,导通电流和感应的光强度成正比,做这样的简化假定:
光敏三极管电流I,光照强度γ,I=K*γ
光敏三极管外接电阻R,电源电压U,光敏三极管压降Uce,U=R*I+Uce=R*K*γ+Uce
当γ大到一定程度后R*K*γ≈U,Uce接近为0,光敏三极管饱和,其电流I不能继续随光照强
度γ变大而变大。
我们需要避免阳光下出现饱和状态,只能是减小R或K,简单起见选择减小电阻
R。
当红外LED被点亮时,光强度会产生一个△γ,对应有电压变化△U=R*K*△γ,△U可以通过
电容,但为了防止阳光下产生饱和这个电阻变得非常小,所以△U也相当小,不能直接被单片机处
理,所以我们用一个放大电路来放大△U,到这里已经可以输出单片机程序想要的YI了。
验证电路效果不错,即便是中午在室外测试也能稳定工作。
既然做了改动,就要看看有没有其
它方面需要进行完善。
放大电路相对成本比较高,如果每一路都用独立的放大电路显然不合算,可
以将不同光敏三极管的输出用电容并联在放大器输入端,这样所用通道就可以共用一个放大器,所
增加的成本就会变小。
因为是对交流信号进行放大处理,其它灯光的闪烁可能会造成干扰,所以程
序需要增加一些抗干扰措施。
2.3.电阻网络低成本高速AD
不少人都有这样一个观点,就是在学校书本上的东西基本上都没什么用,我不大赞同这种说法,
学校里面学的大都是理论基础,要直接用到实际工作中确实比较难,但许多时候将这些理论基础做
MCUEDU第65页作者:
DaiShangJu
删繁就简-单片机入门到精通
一定延伸往往能找出解决问题的方法,前面用交直流的原理解决了红外线在室外饱和的问题,这里
给一个基于电路理论实现低成本AD的例子。
……(详见完整版)
2.4.利用电容充放电测电阻
电容充放电符合下面公式:
图2.4.-1电阻电容充放电示意图
如果Ut和U1恒定,对于初始电压为0的情况有:
t=RC*Ln(U1/(U1-Ut))
也就是当Ut和U1选用恒定的值,对于相同的电容C,充电时间t和电阻R大小成线性正比关
系t=K*R,比例系数K=C*Ln(U1/(U1-Ut))。
图2.4.-2电容效应测电阻示意图
测量流程如下:
①IO_IN设为输入,IO_10k、IO_100k和IO_Rx设为输出并输出0,等待一段时间后将IO_IN也
改为输出0一段时间,确保电容C放电充分。
②IO_IN、IO_100k和IO_Rx设为输入,IO_10k输出1,单片机开始计时,当IO_IN检测到1时
MCUEDU第66页作者:
DaiShangJu
删繁就简-单片机入门到精通
候计时停止,这个时间T10为10k大小参考电阻充电时间。
③IO_IN设为输入,IO_10k、IO_100k和IO_Rx设为输出并输出0,等待一段时间后将IO_IN也
改为输出0一段时间,确保电容C放电充分。
④IO_IN、IO_10k和IO_Rx设为输入,IO_100k输出1,单片机开始计时,当IO_IN检测到1时
候计时停止,这个时间T100为100k大小参考电阻充电时间。
⑤IO_IN设为输入,IO_10k、IO_100k和IO_Rx设为输出并输出0,等待一段时间后将IO_IN也
改为输出0一段时间,确保电容C放电充分。
⑥IO_IN、IO_10k和IO_100k设为输入,IO_Rx输出1,单片机开始计时,当IO_IN检测到1时
候计时停止,这个时间Tx为电阻Rx充电时间。
虽然我们并不清楚IO_IN检测到1的具体电压(也就是Ut)是多少,电容C也不容易控制误差,
但是通过前面的公式我们可以将这个电压Ut和电容C约掉。
基本公式t=RC*Ln(U1/(U1-Ut))
T10=(10k)*C*Ln(U1/(U1-Ut))式①
T100=(100k)*C*Ln(U1/(U1-Ut))式②
Tx=Rx*C*Ln(U1/(U1-Ut))式③
式③与式①相除得Tx/T10=Rx/10k..Rx=(10k)*Tx/T10
式③与式②相除得Tx/T100=Rx/100k..Rx=(100k)*Tx/T100
已经可以测量出电阻Rx的大小,这种测试方法虽然可以通过比较来消除IO_IN检测到1的具
体电压和电容C大小不一带来的误差,但还是存在一些局限,IO输出1的时候电压并不完全相同,
会带来一定的误差。
通过10k/100k两种电阻做参照档可以使测量范围加大,但单片机IO在输入状态下会有一个比
较大的电阻,所以测量需要选用100k档的大电阻误差会高一些。
因为接触电阻、IO驱动能力等原
因需要以1k为参照档的小电阻不太适合本方法。
另外软件需要对Rx进行是否有接电阻的特殊检测,不然当IO_Rx输出1时可能永远无法充到
IO_IN检测到1。
2.5.晶振也能控制电源
曾经遇到这样一个产品,要求单片机在工作时能对一个元件供电,单片机停止工作(软关机)
时关断这个电源。
这个要求其实非常简单,正常情况随便用一个IO来控制这个电源就可以实现。
问题出在当时所用的单片机身上,这个单片机非常简单便宜,能提供的IO口有限,当完成其它功
能需求后已经没有多余的IO可以使用。
不用怀疑是不是真的没有多余的IO,或者是有没有可以共用的IO,这些问题当时已经把单片
机的IO资源翻了许多遍都没找到。
也不是没解决方法,用74HC373之类的锁存器进行IO扩展就可
MCUEDU第67页作者:
DaiShangJu
删繁就简-单片机入门到精通
以实现,但这么做会显著增加成本,是属于没有办法时才会用的办法。
望着电路图,好象还真的是没有什么办法了,忽然看到晶振,一个想法产生:
能不能利用晶振
来控制这个电源呢?
晶振在单片机工作时可以输出一个稳定的正弦波,单片机工作时晶振停振停止
输出,如果我们利用到这个特性来控制电源不就达到了目的吗?
图2.5.-1晶振控制电源示意图
晶振输出脚在单片机工作时输出为稳定正弦波,通过电阻R1加在三极管Q1基极上,这样三极
管Q1随着晶振正弦波周期性的通断,并在导通期间将正弦波反相放大,电容C1隔直流通交流的特
性可以通过交流分量(又用到了交直流通断这样最基本的原理),三极管Q2也会周期性通断并对二
极管D1和电容C2组成的充电电路充电,只要充电频率足够快,电容C2的电容值够大,就能向负
载Rx提供工作电流。
(和当时实际电路有差异,只供示意用)
当单片机停止工作,晶振停振,三极管Q1关断其集电极为高,没有交流信号电容C1停止导通,
三极管Q也随之停止导通,电容C2上的电荷被负载Rx消耗完,输出电压降为0。
2.6.如何降低功耗
但凡提到飞利浦手机,人们第一反应那就是待机时间长,曾几何时,飞利浦几乎就是超长待机
的代名词。
有人说飞利浦待机时间长是其电池容量大,这只是一其待机时间长的一个因素,它以牺
牲体积、重量等方面的性能来保证电池容量足够大,但这一点并不能使其待机时间比其它品牌的两
倍都要长,更重要的一点是它在如何降低功耗方面下了大量功夫。
……(详见完整版)
2.7.开机请用NOP
单片机和自然界的其它事物会具备一些共性,一辆汽车,发动到匀速前进需要一个加速稳定的
MCUEDU第68页作者:
DaiShangJu
删繁就简-单片机入门到精通
过程,单片机也一样,上电后到它正常稳定工作也需要一段时间来稳定,只是这个时间非常之短。
单片机上电时,系统内部并不是即刻到达理想状态,晶振起振到稳定需要稳定时间,系统内部
的各种逻辑电路高低电平的形成需要稳定时间,外部接口充放电过程到稳定状态需要时间等等,在
这些操作没完成之前如果就让单片机执行实际工作代码,就难保证执行结果准确可靠。
一般NOP指令是空操作,就是MCU没有做什么实质性的操作,好比做了一个小小的延时等待,
在所有的指令中,这条指令需要使用到的系统资源是最少的,也就是如果MCU真有一个不稳定的状
态,执行这条指令的安全性最高。
我们是无法知道MCU到底什么时候会稳定下来,设计MCU的工程师不会让这个时间太长,一般
来说等到MCU复位完开始执行代码基本已经稳定下来,如果还没有稳定也只会持续一个非常段的时
间。
如果我们程序启动一开始用上十几个NOP,可以说MCU执行完这些NOP肯定已经稳定下来,如
果还没稳定那只能说这个MCU设计得太烂。
有的MCU会在复位后自动等待一段时间,这个时间就是
让整个芯片稳定下来,然后才开始执行程序。
用NOP并没有严格的理论依据来支持,只是从经验方面做出这样的预估,我工作的的时间已经
不算短,还没有遇到用不用NOP运行结果会不相同的实际经历,但从经验方面看这么做好处不一定
能体现出来,坏处肯定是没有,所以我还是建议新人在写程序的时候在启动的位置多加几个NOP。
2.8.查表与乘除法
查表法是单片机程序提升执行速度的一个有效方法,尤其在进行一些运算的时候,可以显著提
高速度。
网上有不少如何算法加速的资料和文章供大家参考,但单片机应用程序大多数时候都只用
到加减乘除这样的基本运算,这里只是用查表来实现乘法来介绍查表法的优点,并利用乘法来告诉
大家如何实现除法操作。
……(详见完整版)
2.9.RAM动态装载程序
简单的单片机,程序都是存放在ROM里面,现在这些单片机一般都自带有内部ROM来存放程序,
但这部分ROM空间有限,空间大小可能会不能满足用户的需要,所以会支持用户在外部对存储空间
进行扩展,如果单片机器支
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- 单片机 应用 技巧