单片机

单片机解密是什么?

单片机解密又叫单片机破解,芯片解密,IC解密,但是这严格说来这几种称呼都不科学,但已经成 了习惯叫法,我们把CPLD解密,DSP解密都习惯称为单片机解密。单片机只是能装载程序芯片的其中一个类。

单片机(MCU)一般都有内部程序区和数据区(或者其一)供用户存放程序和工作数据(或者其一)。为了防止未经授访问或拷贝单片机的机内程序,大部分单片机都带有加密锁定位或者加密字节,以保护片内程序。

如果在编程时加密锁定位被使能(锁定),就无法用普通编程器直接读取单片机内的程序,这就叫单片机加密。

单片机程序基本上都存在于Flash中,大部分能够读取或者识别Flash上的数据就能够获得Firmware文件,从而给复制产品带来了机会。

单片机攻击者借助专用设备或者自制设备,利用单片机芯片设计上的漏洞或软件缺陷,通过多种技术手段,就可以从芯片中提取关键信息,获取单片机内程序这就叫单片机解密。

能烧录程序并能加密的芯片还有 DSP,CPLD,PLD,AVR,ARM等。

当然具存储功能的存储器芯片也能加密,比如DS2401、DS2501、AT88S0104、DM2602、AT88SC0104D等,当中也有专门设计有加密算法用于专业加密的芯片或设计验证厂家代码工作等功能芯片,该类芯片业能实现防止电子产品复制的目的。

单片机解密方法

软件攻击

该技术通常使用处理器通信接口并利用协议、加密算法或这些算法中的安全漏洞来进行攻击。比如一个典型事例是对早期XXX系列单片机的攻击。攻击者利用了该系列单片机擦除操作时序设计上的漏洞,使用自编程序在擦除加密锁定位后,停止下一步擦除片内程序存储器数据的操作,从而使加过密的单片机变成没加密的单片机,然后利用编程器读出片内程序。

目前在其他加密方法的基础上,可以研究出一些设备,配合一定的软件,来做软件解密。

还有比如利用某些编程器定位插字节,通过一定的方法查找芯片中是否有连续空位,也就是说查找芯片中连续的FF FF字节,插入的字节能够执行把片内的程序送到片外的指令,然后用解密的设备进行截获,这样芯片内部的程序就被解密完成了。

电子探测攻击

该技术通常以高时间分辨率来监控处理器在正常操作时所有电源和接口连接的模拟特性,并通过监控它的电磁辐射特性来实施攻击。

因为单片机是一个活动的电子器件,当它执行不同的指令时,对应的电源功率消耗也相应变化。这样通过使用特殊的电子测量仪器和数学统计方法分析和检测这些变化,即可获取单片机中的特定关键信息。

过错产生技术

该办法就是使得单片机异常运行从而使得单片机处于非保护状态。

该技术使用异常工作条件来使处理器出错,然后提供额外的访问来进行攻击。使用最广泛的过错产生攻击手段包括电压冲击和时钟冲击。

低电压和高电压攻击可用来禁止保护电路工作或强制处理器执行错误操作。时钟瞬态跳变也许会复位保护电路而不会破坏受保护信息。电源和时钟瞬态跳变可以在某些处理器中影响单条指令的解码和执行。

探针技术

通过该技术使芯片内部都完全暴露!直接暴露芯片内部连线,然后观察、操控、干扰单片机以达到攻击目的。

为了方便起见,人们将以上四种攻击技术分成两类:

一类是侵入型物理攻击,这类攻击需要 破坏封装,然后借助半导体测试设备、显微镜和微定位器,在专门的实验室花上几小时甚至几周时间才能完成。所有的微探针技术都属于侵入型攻击。

另外一类属于非侵入型攻击,被攻击的单片机不会被物理损坏。在某些场合非侵入型攻击是特别危险的,这是因为非侵入型攻击所需设备通常可以自制和升级,因此非常廉价。大部分非侵入型攻击需要攻击者具备良好的处理器知识和软件知识。与之相反,侵入型的探针攻击则不需要太多的初始知识,而且通常可用一整套相似的技术对付宽范围的产品。

因此,对单片机的攻击往往从侵入型的反向工程开始,积累的经验有助于开发更加廉价 和快速的非侵入型攻击技术。

侵入式解密过程

侵入型攻击的第一步是揭去芯片封装(简称“开盖”有时候称“开封”,英文为 “DECAP”,decapsulation)。有两种方法可以达到这一目的。

第一种是完全溶解掉芯片封装,暴露金属连线。
第二种是只移掉硅核上面的塑料封装。

第一种方法需要将芯片绑定到测试夹具上,借助绑定台来操作。第二种方法除了需要具备攻击者一定的知识和必要的技能外,还需要个人的智慧和耐心,但操作起来相对比较方便,完全实验室中操作。

芯片上面的塑料可以用小刀揭开,芯片周围的环氧树脂可以用浓硝酸腐蚀掉。热的浓硝酸会溶解掉芯片封装而不会影响芯片及连线。该过程一般在非常干燥的条件下进行,因为水的存在可能会侵蚀已暴露的铝线连接,这就可能造成解密失败。接着在超声池里先用丙酮清洗该芯片以除去残余硝酸,并浸泡。
最后一步是寻找保护熔丝的位置并将保护熔丝暴露在紫外光下。一般用一台放大倍数至少100倍的显微镜,从编程电压输入脚的连线跟踪进去,来寻找保护熔丝。若没有显微镜,则采用将芯片的不同部分暴露到紫外光下并观察结果的方式进行简单的搜索。

操作时应用不透明的物体覆盖芯片以保护程序存储器不被紫外光擦除。将保护熔丝暴露在紫外光下5~10分钟就能破坏掉保护位的保护作用,之后,使用简单的编程器就 可直接读出程序存储器的内容。

对于使用了防护层来保护EEPROM单元的单片机来说,使用紫外光复位保护电路是不可行的。对于这种类型的单片机,一般使用微探针技术来读取存储器内容。在芯片封装打开后,将芯片置于显微镜下就能够很容易的找到从存储器连到电路其它部分的数据总线。

由于某种原因,芯片锁定位在编程模式下并不锁定对存储器的访问。利用这一缺陷将探针放在数据线的上面就能读到所有想要的数据。在编程模式下,重启读过程并连接探针到另外的数据线上就可以读出程序和数据存储器中的所有信息。

还有一种可能的攻击手段是借助显微镜和激光切割机等设备来寻找保护熔丝,从而寻查和这部分电路相联系的所有信号线。

由于设计有缺陷,因此,只要切断从保护熔丝到其它电路的某一根信号线,或者切割掉整个加密电路。又或者连接1~3根金线,通常称为FIB(focused ion beam),就能禁止整个保护功能。这样使用简单的编程器就能直接读出程序存储器的内容。

虽然大多数普通单片机都具有熔丝烧断保护单片机内代码的功能,但由于通用低档的单片机并非定位于制作安全类产品,因此,它们往往没有提供有针对性的防范措施且安全级别较低。

加上单片机应用场合广泛,销售量大,厂商间委托加工与技术转让频繁,大量技术资料外泻,使得利用该类芯片的设计漏洞和厂商的测试接口,并通过修改熔丝保护位等侵入型攻击或非侵入型攻击手段来读取单片机的内部程序变得比较容易。

防止单片机被解密的几点建议

作为电子产品的设计工程师非常有必要了解当前单片机攻击的最新技术。因为任何一款单片机从理论上讲,攻击者均可利用足够的投资和时间使用以上方法来解密!为避免辛苦劳作的成果被窃取,提出以下建议:

在选定加密芯片前,要充分调研,了解单片机破解技术的新进展,包括哪些单片机是已经确认可以破解的。尽量不选用已可破解或同系列、同型号的芯片选择采用新工艺、新结构、上市时间较短的单片机。

对于安全性要求高的项目,尽量不要使用普及程度最高,被研究得也最透的芯片。

产品的原创者,一般具有产量大的特点,所以可选用比较生僻、偏冷门的单片机来加大仿冒者采购的难度,选用一些生僻的单片机。

在设计成本许可的条件下,应选用具有硬件自毁功能的智能卡芯片,以有效对付物理攻击;另外程序设计的时候,加入时间到计时功能,比如使用到1年,自动停止所有功能的运行,这样会增加破解者的成本。

如果条件许可,可采用两片不同型号单片机互为备份,相互验证,从而增加破解成本。

打磨掉芯片型号等信息或者重新印上其它的型号,以假乱真。

可以利用单片机未公开,未被利用的标志位或单元,作为软件标志位。

你应在程序区写上版权信息,以备获得法律保护。

采用高档的编程器,烧断内部的部分管脚,还可以采用自制的设备烧断金线,这个目前国内几乎不能解密,即使解密,也需要上万的费用,需要多个母片。

采用保密硅胶,比如环氧树脂灌封胶,封住整个电路板,PCB上多一些没有用途的焊盘,在硅胶中还可以掺杂一些没有用途的元件,同时把MCU周围电路的电子元件尽量抹掉型号。

可以用编程器把空白区域中的FF改成00,也就是把一些未使用的空间都填充好,这样一般解密器也就找不到芯片中的空位,也就无法执行以后的解密操作。

总结

当然,要想从根本上防止单片机被解密,那是不可能的,加密技术不断发展,解密技术也不断发展,现在不管哪个单片机,只要有人肯出钱去做,基本都可以做出来,只不过代价高低和周期长短的问题,编程者还可以从法律的途径对自己的开发作出保护,比如写相关专利。

本文转载自:嵌入式Linux
免责声明:本文为转载文章,转载此文目的在于传递更多信息,版权归原作者所有。本文所用视频、图片、文字如涉及作品版权问题,请联系小编进行处理(联系邮箱:
cathy@eetrend.com)。

围观 112


下面先讲串口通信的一些基本概念,术语。如果对串口通信比较熟悉的,就当复习,如果哪里讲的不到位,欢迎及时指出。

这里并不对串口的编程作讲解,主要是从应用的角度去讲一讲。因为更多的时候,都是产品做好了,比如触摸屏需要和控制器,PLC通信。理想的情况下,一般只要一上电,不需要太多的操作和配置,就可以通信上。

文章后半部分罗列了一些相关问题,在解答前还需要先了解一下什么是串口通信,232,485,422等。

什么是串口通信

常见的串口通信一般是指异步串行通信。

这里就要说一下同步和异步的区别了。

先讲一下串行通信的概念。那么,与串行通信相对的是什么呢?

与串行通信相对的是并行通信。数据传输一般都是以字节传输的,一个字节8个位。拿一个并行通信举例来说,也就是会有8根线,每一根线代表一个位。一次传输就可以传一个字节,而串口通信,就是传数据只有一根线传输,一次只能传一个位,要传一个字节就需要传8次。就像小虎队那首歌一样,把你的心,我的心,串一串,再烤一烤。。串口通信就是把数据串在一根线上传输,所以就叫串口吧。


与异步通信相对的就是同步通信了。同步通信一般是指有一个时钟信号进行数据信号同步。同步通信对接收方来说就相对简单一些。因为有时钟信号在,每一个高低电平变化一下,就去取一下数据就行了。通信速率可以由发送方或者说是主站设备进行控制。通信速度也相对比串口通信快很多。但是为什么很多设备,屏和plc,控制器不采用这种方式,都使用串口呢。

那么,在很多设备上,不方便接太多线,比如接8根数据线,也不方便接同步时钟信号(这个后面再说),于是一种异步串行通信就诞生了。

相对来说,异步串口通信,就只需要一根线就可以发送数据了。在对速率要求不高的情况,使用一根线发送数据是带来大大的方便和实用价值的。

那么问题来了,怎么样才能保证一根线就能发送正常的数据呢。也就说发送方发送的数据,接收方是怎么知道是什么数据呢。

为了能正常发送数据和接收正确的数据,那异步串口通信就需要满足以下几个条件:


也就是双方必要约定一种暗号。

也许当时发送这个通信的小组是这样讨论的。

经理:我要用一根线就能传输数据,你来给我定个标准。

研发:好。

经理:只有一根线,我怎么知道数据什么时候开始呢。

研发:就一根线,默认是高电平,那就有一个起始位吧。当检测到有低电平的时候,就是开始有一个字节的数据发送了,起始位之后,先是字节的最低位,传送一个字节。

经理:可是,就一根线,过来的数据会不会有干扰,容易出错呀。

研发:行呀,那就在字节数据后再加一个校验位。可以作奇校验,偶校验,1校验,0校验,无校验。

经理:嗯 ,很不错。有起始位就应该有停止位,那我们就再加个停止位在后面吧。

研发:。。。。。。。。。

经理:传输一串数据,对方要怎么知道数据的拆分呢,怎么按时间或频率去解读数据位,校验位呢。

研发:这样吧,双方约定一个波特率吧,定义一个每个位占用多长的时间,这样双方按这个波特率就可以处理了。

经理:这个比特率呀。。。

研发:老板,是波特率。

经理:我知道,是比特率嘛。

研发:这个波特率呢,是指1S钟可以传输多个位,也就知道一个位占用多长时间。这样就解决传输的问题了。

经理:那万一传输过程,数据快太,判断失误停不下来怎么办。

研发:那就把停止位可以调节为1个停止位或者2个停止位。这样就可以停下来了。

经理:嗯,听着不错。就这样办吧。

于是,串口通信就这样出来了。

在串口的通信参数上,就有了波特率,数据位,停止位,校验位这几个参数来确保串口通信的正确性和稳定上。当然,这只是某个方面保证串口通信的正确性和稳定性,不代表设备间通信的正确性和稳定性。

串口通信主要为分232,485,422 通信三种方式。

这三种有什么区别呢。

232:


232 通信主要是由RX,TX,GND三根线组成。

RX与TX,TX接RX,GND接GND。这样还是比较好理解吧。因为发送和接收分别是由不同的线处理的,也就是能同时发送数据和接收数据,这就是所谓的全双工。

在这里扩展一下,串口通信还有一个功能叫做全功能串口通信,也叫标准串口。因为在两个设备间进行数据传输,有些设备处理速度比较快,有些数据比较慢。为了保证数据能正常传输,在RX,TX的基础上,还增加了几个控制引脚,本来好端端就R,T,G,三根线,凑着就凑齐了9个引脚,召唤出了DB9这个东西。


这要怪就怪当时使用电脑的时候,还没有互联网这个概念,但是又想在两台电脑间进行通信。所以才有这样一个东西。

在后来的设备,很多控制器,人机界面,PLC等使用串口通信中,基本上就不使用标准串口,而是就直接使用RX,TX,GND三根线来通信了。

但是这里为什么要提到这个呢。因为只是很多设备这样用,也就是还存在少数设备还保留了标准串口的功能。这就是为什么会遇到明明电脑通信是好的,换成触摸屏通信就不行了。因为很多触摸屏只使用了RX,TX,GND通信,遇到一些还保留标准串口功能的就比较讨厌了。

485:

485是为了解决232通信距离的问题。原理什么之类的就不多讲了。反正232通信距离就是不长。485主要是以一种差分信号进行传输,只需要两根线,+,-两根线,或者也叫A,B两根线。A,B两根线的差分电平信号就是作为数据信号传输。

那么问题来了,那是不是就没有RX和TX的概念了。是的,发送和接收就不能分开了。发送和接收都是靠这两根的来传输,也就是每次只能作发送或者只能作接收,这就是半双工的概念了,这在效率上就比232弱很多了。就像对讲机一样,经常是某个人讲完之后,都要说一个over,确保当前说完了,等待对方回复。


485就是这样牺牲了232全双工的效率来达到自己传输距离远的代价。那有没有即保留了232的全双工,又可以像485这样提高传输距离呢,于是,422出来了。

422:

422呢,有些标注为485-4。而485就标注为485-2。有什么区别呢。就是为了好记呢。485-2就是2根线。485-4就是4根线。


422就是把232的RX分成两根线,RX+,RX-,把TX分成TX+,TX-。这样就可以同时发送和同时接收了,还可以像485这样,有较远的传输距离。可是这样一种很有优势的通信方式,为什么用的不多呢。我个人的答案和理解就是:线太多了。特别是像我这样懒得接线的人,超过3根线就头晕的。搞个通信还需要接这么多线,什么TX,RX,正啊负啊。交换来交换去。

因为在很多设备通信中,基本上是属于一问一答式的,因此,232的全双工通信优势其实也并没有发挥出来。就像现在打电话,虽然两个人可以同时说话,但是两个人同时说话,叽叽歪歪的,谁知道说什么呀。特别是一个主站与多个从站通信的时候,485的接线就就方便多了,反正大家就两根线,把+都接一块,把-都接一块。如果是422作一主多从,接线上还要理半天呢,而且通信异常了也不好解决。

好了,串口通信基本就普及到这里吧。下面就对刚上提到的问题进行讲一讲。

1、电脑使用USB转串口可以和设备通信上,换成屏与设备就通信不上了?

1)有可能电脑USB转串口接到设备上,使用的是标准串口功能,也就是除了RX,TX,GDN外,还使用了其它引脚。比如像欧姆龙PLC,三菱PLC,在实际与屏的通信中,就需要接某些引脚短接的情况。

2)电脑与控制器或PLC通信时,是扫描波特率参数,自适应的,屏通信可能参数没有设备好。在三菱,基恩士等PLC,就存在变化波特率进行通信交互的过程。

3)也有可能是接线方式不对。因为有些DB9,还需要公头,母头。如果不注意的话,也会存在把TX接到TX上,把RX接到RX上,这样需要注意的地方。

4) 在这里补充一下,有时候可能会使用一些串口助手发送测试数据与控制器通信,有些串口助手的奇偶校验是不起作用,这个要提醒一下。

2、 这A家的屏可以和设备通信,换成B家的屏就通信不上了?

1) 首先确认一下接线是否正确了,RX和TX是否兼容。
2) 地线是否没有接。
3) 除了RX,TX,GND,是否还有其它引脚需要短接的。
4) 通信协议是否一致或不完善,波特率是否一样。

3、 以前不接地线可以通信,换个设备为什么需要接地线了?

这个问题和上一个有类似的。因为有些设备使用了隔离电源。以前不接地可以通信,有可能是地线已经在另外一个环路已经共地了,实际地线已经接了,所以才可以通信。可能换了个带隔离电源的,两个设备的地是隔离的,就需要在串口上把地线接起来。这个我是自身经历过的,有个客户老说他的设备通信不上,后来拍个照我给我,他地线没有接,他说以前不接地线可以通信的。于是我就给他科普了一下。

4 、一个设备是232,另一个设备是422,没有转换设备,怎么办?(232与422互转的简单方法)

这个情况我遇到过,客户的设备是422通信的,但是我手上并没有422设备,只有232通信可以测试。因此就需要把422转成232进行通信。

刚才也讲了422和232的接线,因为这两个都是全双工的,接收和发送都是分到的,而422只是以一种差分信号进行传输。

把422的Rx+与232的TX接,422的RX-与232的GND接。

把422的TX+与232的RX接,422的TX-与232的GDN接。

这样,422设备要发送数据的,就可以发送到了232的RX上。232的TX发数据后,由于TX和GND也形成了差分信号给422,422就可以接收到数据了。

5、 用232通信没问题,用485通信没问题,使用232转485之后就通信不稳定?

232和485从通信原理上,最大一个差别是全双工和半双工的区别。可是应用层发送数据和接收数据才不管底下是全双工还是半双工。

但是485就得管了。因为既然是半双工,就得严格保证通路上只能有发送或只能有接收的数据,一旦同时有发送和接收,数据就会冲突了。所以解决的办法就是主站设备,也就是主动命令的一方就需要严格控制好发送数据命令的节奏了。

当然有些232转485的设备做的比较好了,可以优化这个,但是主站还是要控制,比较把通信速率调节慢一些(不是调节波特率)。

6、 485单独接每个设备都通信正常,多个从站接一块通信就不稳定?

这个是属于485通信的不稳定因素了。

7 、要想实现两个屏或两个主站通过485访问modbus设备,有什么好的办法?

在485通信中,基本上是一主多从。但是遇到一些客户实际使用中,有客户想用两个屏来访问一个modbus设备的。目前暂时还没有好的办法。

8、 针对串口通信的弱点,在使用上应该要注意哪些地方?

说来串口通信的弱点,那就说来话长了。不过还是长话短说吧。

1)信号干扰的问题

建议使用带屏蔽线,接线要严格,比如要接地。有些485通信上,还考虑接上终端电阻来匹配。如果是232,尽量不要让线太长。通信协议上尽量避免长报文的数据通信。

2)波特率匹配的问题

因为有些设备的计算的波特率是存在误差的,特别是一些控制器,由于使用的晶振不一样。因此在一些波特率比如9600波特率就存在误差。存在误差带来的影响是什么呢。因为接收方是通过时间来计算一个位的。那么如果一个报文过长,就会存在误差积累的问题,算着算着就偏了。所以,这也是串口通信不稳定的一些地方,在使用上应注意避免发送太长数据的包。

3)在一些可能会存在干扰的情况,在有的选的情况,可以考虑使用奇校验或者偶校验。因为虽说出现错误的可能性不大,但既然存在干扰,如果加了校验,至少可以把错误的报文过滤掉。总好比没有校验然后通信数据错了不知道。或者尽量使用一些带校验的协议,防止数据出错。

4)串口通信本来就比较慢,请降低对数据响应的要求。

因为串口通信本身就比以太网慢。而且,串口通信并不是能像CPU那样多线程处理。因为就一个口一个线数据出去,即便你应用到程序再怎么用多线程处理数据,但是最底下也只有一个口出去,一次也只能传一个位,一个字节过去。因为有客户在使用9600的波特率通信,但是又希望多少的数据可以在多少毫秒内得到响应。

但是串口通信还是要事实求是,所以正确认识串口通信对应用,对开发,对沟通都有着很大的帮助的。

为什么不用同步通信呢?

刚才提到,同步通信需要依赖于时钟信号。这就存在一个问题,这个时钟信号是谁来发起呢。在同步通信中,往往需要一个主设备发起时钟信号读从模块的数据。在实际中,有屏读PLC,有屏读屏的数据。而单纯地从异步串口通信来说,是没有主从之说,双方都是平等的角色,都可以互发信息,互收信息。而同步通信一般是应用于CPU读一些模块,由CPU发起时钟信号,比如读SD卡模块,就可以通过SPI方式,还有一些传感器模块。

本文是转载文章,直接来源:技成培训,转载此文目的在于传递更多信息,版权归原作者所有。

围观 64

一、 前言

1、利用所学知识设计一个单片机数字电子钟

2、数字电子钟的功能要求:
(1)有自动计时功能;
(2)能显示计时时间,显示效果良好;
(3)有校时功能,能对时间进行校准

3、设计要求:
(1)主电路由秒信号发生器、“时、分、秒”计数器、译码器及显示器、校准电路等构成。
(2)秒信号发生器一般用石英晶体振荡器加分频器实现。
(3)译码电路将时、分、秒计数器的输出状态送七段译码器译码,经过六位LED七段显示器显示出来。
(4)校时电路用来对时、分、秒显示数字进行校对。

二、 硬件原理分析

1、时钟信号部分

单片机XTAL1,XTAL2端接外部时钟电路(时钟电路参考课本),EA端接5V电源,使得单片机读取片内程序。


2、按键开关部分

开始仿真,按下开关K1,时钟暂停,然后按下开关K2一次,切到调时位,再按K3,K4实现时位加减;按K1两次切到调分位,按K3,K4实现分位加减;按K1三次切到调秒位,按K3,K4实现秒位加减;按下K5,确定当前操作,然后再按K1,重新启动时钟。


3、数码管显示器部分(共阴极)

所用的是一个六位七段共阴极数码管


4、共阴极数码管编码

三、 程序设计


四、 程序代码

#include <reg51.h>
#define uchar unsigned char
#define uint unsigned int
sbit set=P1^0;
sbit save=P1^5;
sbit rselect=P1^1;
sbit lselect=P1^2;
sbit add=P1^3;
sbit reduce=P1^4;
uchar code tab[]={0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f};
uchar disp_buf[6];
uchar disp_bit=0;
uint hour,min,sec;
uint select_num=0;
uint hour_adj,min_adj,sec_adj;
uchar count;
uchar key_num=0;
void key_scan()
{
if(!set)
{
hour_adj=hour;
min_adj=min;
sec_adj=sec;
key_num++;
}
while(!set);
if(key_num%2==1)
{
if(rselect==0)
{
select_num++;
if(select_num==4)
select_num=1;
}
while(!rselect);
if(lselect==0)
{
select_num--;
if(select_num<=0)
select_num=3;
}
while(!lselect);
}
if(!add&&(key_num%2))
{
switch(select_num)
{
case 1 :
{
hour_adj++;
if(hour_adj==24)
hour_adj=0;
break;
}
case 2 :
{
min_adj++;
if(min_adj==60)
min_adj=0;
break;
}
case 3 :
{
sec_adj++;
if(sec_adj==60)
sec_adj=0;
break;
}
default:break;
}
while(!add);
}
if(!reduce&&(key_num%2))
{
switch(select_num)
{
case 1 :
{
hour_adj--;
if(hour_adj<=0)
hour_adj=23;
break;
}
case 2 :
{
min_adj--;
if(min_adj<=0)
min_adj=59;
break;
}
case 3 :
{
sec_adj--;
if(sec_adj<=0)
sec_adj=59;
break;
}
default:break;
}
while(!reduce);
}
if(!save&&(key_num%2))
{
select_num=0;
hour=hour_adj;
min=min_adj;
sec=sec_adj;
while(!save);
}
}

void main()
{
TMOD=0x11;
TH0=0xf7;
TL0=0x00;
TH1=0x4c;
TH0=0x00;
ET0=1;
ET1=1;
EA=1;
TR0=1;
TR1=1;
PT1=1;
hour=23;
min=59;
sec=59;
count=0;
while(1)
{
key_scan();
if(key_num%2)
{
switch(select_num)
{
case 1 :
{
if(count<=10)
{
disp_buf[0]=hour_adj/10;
disp_buf[1]=hour_adj%10;
}
else
{
disp_buf[0]=0x40;
disp_buf[1]=0x40;
}
disp_buf[2]=min_adj/10;
disp_buf[3]=min_adj%10;
disp_buf[4]=sec_adj/10;
disp_buf[5]=sec_adj%10;
break;
}
case 2 :
{
if(count<=10)
{
disp_buf[2]=min_adj/10;
disp_buf[3]=min_adj%10;
}
else
{
disp_buf[2]=0x40;
disp_buf[3]=0x40;
}
disp_buf[0]=hour_adj/10;
disp_buf[1]=hour_adj%10;
disp_buf[4]=sec_adj/10;
disp_buf[5]=sec_adj%10;
break;
}
case 3 :
{
if(count<=10)
{
disp_buf[4]=sec_adj/10;
disp_buf[5]=sec_adj%10;
}
else
{
disp_buf[4]=0x40;
disp_buf[5]=0x40;
}
disp_buf[0]=hour_adj/10;
disp_buf[1]=hour_adj%10;
disp_buf[2]=min_adj/10;
disp_buf[3]=min_adj%10;
break;
}
default : break;
}
}
if(key_num%2==0)
{
disp_buf[0]=hour/10;
disp_buf[1]=hour%10;
disp_buf[2]=min/10;
disp_buf[3]=min%10;
disp_buf[4]=sec/10;
disp_buf[5]=sec%10;

}
}
}
void timer0() interrupt 1
{
TH0=0xf7;
TL0=0x00;
P2=~(0x01<<disp_bit);
P0=tab[disp_buf[disp_bit]];
disp_bit++;
if(disp_bit==6) disp_bit=0;
}
void timer1() interrupt 3
{
TH1=0x4c;
TL1=0x00;
if(++count==20)
{
count=0;
if(++sec==60)
{
sec=0;
if(++min==60)
{
min=0;
if(++hour==24)
{
hour=0;
}
}
}
}
} 

五、 仿真效果图


六、 参考文献

1. 网上搜索
2. 单片机教材
3. 网上论坛

七、学习体会

通过本次单片机课程设计,不仅加深了我对单片机理论课程知识的认识,而且通过将理论与实践相结合,使我真正地全面理解单片机的功能。 在刚开始编程时,没有一点思路,通过书上所提供的例程,慢慢理清了思路、基本了解了程序大致需要那几部分,基本确定编程思想。在课程设计的整个过程中也遇到了很多问题,但本着遇到问题解决问题的原则,通过查找资料和询问老师、同学的办法,基本解决了所遇到问题。整个课程设计过程学到了不少通过理论学习没法学到的东西,真正增强了自己的能力。
课程设计的这天时间,虽然辛苦但是收获巨大。

作者:小西贝
出处:
https://www.cnblogs.com/jtd666/p/12499243.html
转载此文目的在于传递更多信息,版权归原作者所有。

围观 92

因为单片机有CPU、存储器、IO等等,使他(人性化一点以配合下文)看起来就像一个比较小的计算机,所以,在理解单片机的时候如果能把你之前有的那些也许仅仅是直觉上的对计算机的理解融入进来的话,可能会对你学习单片机的概念有极大的帮助,至少对于我是这样的。

我想在关于单片机的众多让你头晕脑胀、摸不着头脑甚至想撞墙的概念里面,“堆栈”可能是其中最可恶的一个,因为即使单单是从汉语的角度来理解这个词就已经让你很晕了,其实我最初也想不通这是哪位大侠的创意,不过不用担心,这里我们完全不去讨论关于这个词的问题(这个词用得其实很好“堆”和“栈”都有他们各自的意思,准确的概括了这个区域的功能,有兴趣可以Baidu一下),这里我会打一个比较有趣的比方,以此来绕过那些令你想撞墙的概念,并使你在直觉上对“堆栈”这个概念有一个深刻的理解。

你基本上应该清楚,单片机里面是有存储区和CPU的,如果你不清楚,那么我刚刚告诉你了,请记住。现在,请你把单片中的CPU想成一个人(你完全可以把他想成是你宿舍的那个天天和你吵嘴的同学,一会你就会发现这会非常有趣),在这里就叫他C哥吧,不过这个人不同于常人,有一些特点,一会我们会慢慢说清楚,现在要告诉你的关于这个人的第一个特点是:他的记忆能力很差。下面,请你把存储区想象成一个一个排好的小盒子,这些盒子的作用大致可以分成两类:1、保存写有你命令的纸条,比如你在某个盒子里面的纸条上写着:去洗我的袜子!;2、保存你的一些东西,比如你那双正在污染宿舍空气的臭袜子。因为C哥是一个记忆力不怎么好的人,所以,这些盒子都有自己的编号,以方便他查找。

那么,现在,我们可以来说明一下单片机是如何工作的了。首先,你要把所有的命令还有需要处理的东西放进那些小盒子,比如刚才提到的你那双待洗的袜子还有那张纸条,这时你应该发现C哥另一个特点:笨——他只会做你明确告诉他的事情,也就是说,如果你没有在纸条上写“去洗我的袜子!”,那么C哥极有可能会无动于衷地看着你的袜子直到他被熏晕倒,当然,更可能的情况是他根本找不到你的袜子…好了,当你把要做的事情和该怎么做写到盒子里之后,下面的任务就交给C哥了。C哥做事真的很讲原则,他会按照你给定的顺序或者——如果你没有给定的话,根据盒子上面的编号按照从小到大的顺序——一个一个地打开盒子,读取里面的命令、处理相应的事件,直到所有的事情都执行完毕,他就会休息。请你牢记这个简单而有趣的过程,因为其实单片机就是这样工作的,当然,这里忽略了许多细节,但是这对你从直觉上理解单片机的概念以及足够了。

下面,就要开始说明堆栈这个概念了,思来想去,还是觉得如果直接把“堆栈”这个词用到文中来,实在不符合本文的风格,考虑到其实“堆栈”也是存贮区(这一点你要记住,堆栈并不是一个像专用寄存器那样专门的一个区域,它是由你在通用RAM区指定的。),按照本文的说法也就是一些盒子,所以,现在我们把“堆栈”改名叫“记忆盒子”,你可以感觉到,“堆栈”的作用和记忆有极大的关系,不过你也不用在这里纠结这个名字的由来,下面我会说的。

现在,请注意,我要开始解释“记忆盒子”了,也就是“堆栈”。大致上说,“记忆盒子”的作用是当C哥执行某任务到一半的时候突然有了更紧急的是事情要执行的时候用来保存当前任务的(包括盒子的编号和盒子里面的东西)。这么说你肯定晕了,其实,通俗一点,就是当C哥洗袜子洗到一半的时候突然接到你的命令要去打开另一个盒子(那个盒子里的纸条上可能写着“给我换尿布”)并执行里面的命令,因为C哥记忆力很差,以至于他做完那件紧急的事情后记不起要回到哪个盒子来继续执行“洗袜子”这个命令,这时候,他要把现在手头的东西保存到“记忆盒子”里,要保存的东西有:1、放着纸条和袜子的盒子的编号(注意这里其实是两项内容);2、那双袜子。这样,当他执行完紧急任务后会去记忆盒子里,从里面找到两张纸条,和一双袜子(这个时候C哥还是没有想起来他要洗袜子,他必须要到那张写着洗袜子命令的纸条),他按照两张纸条的信息知道自己要去哪个盒子去洗袜子,并在那里继续完成洗袜子的任务。你可能会发现,在这一段的解释里面有一个重要的漏洞,那就是在C哥执行完紧急任务后他是如何知道储存着原来的任务信息的盒子的编号是存储在哪个“记忆盒子”里呢?别着急,下面我会解释的。

从本质来说,“记忆盒子”与普通的盒子是没有区别的,他们都是单片机里面的存储单元,证明这一点的最好证据就是堆栈是需要你来指定的,也就是说,你要预先把一些盒子指定为“记忆盒子”。下面,说明一下是如何指定“记忆盒子”的。其实这个过程很简单,在单片机的专用寄存器里面有一个SP指针(81H),这个指针里面记录着堆栈的开始处的地址。用符合本文的话来解释就是,C哥的衣服上有一个口袋(也就是SP指针),这个口袋里面的“神奇纸条”上记录着第一个“记忆盒子”的编号,而指定“记忆盒子”的过程就是你在这张“神奇纸条”上写上一个盒子的编号(作为第一个“记忆盒子”的编号),这个纸条会自动地将纸条上的编号加1或者减1,所以,某个目前并不确定的区域内盒子具备了成为“记忆盒子”的可能,注意,堆栈的大小是不能规定的,这就是为什么用“生长”这个词来形容堆栈。

现在,关于堆栈的概念基本上都介绍完了,但是,我知道,你可能还是很晕,甚至比看之前还晕,那是因为刚才叙述的这个过程是分开的,而且逻辑上并不是顺序的,下面,顺序的说一下,相信你马上就明白了。

主角仍然是傻傻笨笨但任劳任怨的C哥,他一个一个的打开盒子按照里面的纸条上的说明执行你规定的任务。而你,为了防止他在执行复杂任务时犯傻,把一个盒子指定为“记忆盒子”,并把这个“记忆盒子”的位置写在了一张 “神奇纸条”上放在了C哥的口袋里。现在,C哥正在洗你的袜子,这个时候,他突然接到你的命令要去给你换尿布,而C哥知道自己很笨,所以他自动地掏出了口袋里的纸条,找到了第一个“记忆盒子”,然后拿出一张空白纸条,把装着“给我洗袜子”那张纸条的盒子的编号写在了上面并放进“记忆盒子”。然后,他把“神奇纸条”放回了口袋里。当这个任务完成后“神奇纸条”会自动将写在它上面的编号加1,也就是将一个新的、空的“记忆盒子”的编号写在上面。之后,他会按照刚才的过程把装着袜子的那个盒子的编号以及袜子本身分别放进不同的记忆盒子(现在已经有三个盒子成为“记忆盒子”,堆栈已经长大了,红色下划线的字体就是这三个盒子里的内容,注意是有先后顺序的)。再然后,他就去给你换尿布了…

现在,尿布换完了,不过,果不其然,C哥完全忘记了他要给你洗袜子这件事情了,不过,他记得一件事,那就是看口袋里的纸条。于是,他摸出了口袋里的纸条,上面当然是一个“记忆盒子”的编号,他按照编号找到了第一个“记忆盒子”(按照上一段的顺序应该是第三个“记忆盒子”),里面应该是一双你的袜子,于是他拿到了你的袜子。但是,他还是不知道该干什么,于是他再次摸出了“神奇纸条”,这时,纸条上的编号已经自动减1了,于是,他找到了新的“记忆盒子”,里面的纸条上记录着袜子本来放置的盒子的编号,于是,他把袜子放到了那个盒子里。恩,你可以想到,现在C哥还是不知道要对袜子做些什么,他耐心的又一次摸出了那张“神奇纸条”,这次按照上面的编号,他找到了一张纸条,上面写着的仍然是一个盒子的编号。C哥按照编号找到了那个盒子,发现那个盒子里的纸条上写着“给我洗袜子!”…至此,C哥又回到了原来的任务——洗袜子。

现在,我希望你已经明白了,堆栈其实就是你指定的一个些存储单元,这些存储单元被指定只用来保存一些特殊信息,比如地址(保护断点)或者一些数据(保护现场),如果你一定要说这个存储区有什么特别的话,那就是:1、这些存储单元内的内容都是CPU在执行某任务中途被打断时的一些相关参数;2、这些存储单元的地址被记在了一个叫堆栈指针的地方,也就是C哥口袋里的那张纸条上!

来源:单片机与嵌入式

围观 32

对于初学者而言,对单片机的内存分配往往最让人头疼,很多人学了单片机几年 都不知道单片机内部的内存使用情况是如何分配的。要了解 ROM(flash)、RAM(sram)启动,首先需要对链接器 Linker 如何分配内存有一定的了解。

通常,对于栈生长方向向下的单片机,其内存一般模型是:



一个进程运行时,所占用的内存,可以分为如下几个部分:
1、栈区(stack):由编译器自动分配释放,存放函数的参数值,局部变量的值等。
2、堆区(heap):一般由程序员分配释放,若程序员不释放,程序结束时可能由OS释放。
3、全局变量、静态变量:初始化的全局变量和静态变量放在一块区域,未初始化的全局变量和和未初始化的静态变量在相邻的的另一块区域。程序结束后由系统自动释放。
4、文字常量:常量字符串就是放在这里的。这些数据是只读的,分配在RO-data(只读数据存储区),则被包含在flash中,程序结束后由系统自动释放

5、程序代码(code):存放函数体的二进制代码。

同时,单片机内存被总分为flash(rom)和sram(ram),flash里面的数据掉电可保存,sram中的数据掉电就丢失,sram的执行速度要快于flash,flash容量大于sram

上方的最低内存地址,最高地址,都是在flash和sram中

我们正常下载程序都是下载存储进flash里面,这也是为什么断电可保存的原因

单片机的程序存储分为code(代码存储区)、RO-data(只读数据存储区)、RW-data(读写数据存储区) 和 ZI-data(零初始化数据区)

  • Flash 存储 code和RO-data
  • Sram 存储 RW-data 和ZI-data


在使用MDK编译时可以看到


Code为程序代码部分 = 程序代码区(code)

RO-data 表示 程序定义的常量 = 文字常量区

RW-data 表示 已初始化的全局变量 = 栈区(stack)堆区(heap)全局区(静态区)(static)

ZI-data 表示 未初始化的全局变量

部分参考自:http://blog.chinaunix.net/uid-15473693-id-388637.html

版权声明:
本文为CSDN博主「Z小旋」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。

原文链接:https://blog.csdn.net/as480133937/article/details/87608816

围观 170

1. MCU有串口外设的话,在加上电平转换芯片,如MAX232、SP3485就是RS232和RS485接口了。

2. RS485采用差分信号负逻辑,+2~+6V表示0,-6~-2表示1。有两线制和四线制两种接线,四线制是全双工通讯方式,两线制是半双工通讯方式。在RS485一般采用主从通讯方式,即一个主机带多个从机。

3. Modbus是一种协议标准,可以支持多种电气接口,如RS232,RS485,也可以在各种介质上传输,如双绞线,光纤,无线。

4. 很多MCU的串口都开始自带FIFO,收发FIFO主要是为了解决串口收发中断过于频繁而导致CPU的效率不高的问题。

如果没有FIFO,则没收发一个数据都要中断处理一次,有了FIFO,可以在连续收发若干个数据(根据FIFO的深度而定)后才产生一次中断去处理数据,大大提高效率。

5. 有些工程师在调试自己的系统时一出现系统跑飞,就马上引入看门狗来解决问题,而没有思想程序为什么会跑飞?

程序跑飞可能是程序本身的bug,也可能是硬件电路的问题(本身就是易受干扰或自己就是干扰源)。通常建议在调试自己的系统时,先不加看门狗,等完全调试稳定了,在补上(危机产品安全,人身安全的除外)。

6. 如何区分有源蜂鸣器和无源蜂鸣器?

从外观上看,如将两种蜂鸣器的引脚都朝上放置时,可以看出绿色电路板的一种是源蜂鸣器,没有电路板而用黑胶密封的一种是有源蜂鸣器。

有源蜂鸣器直接接上额定电源就可以连续发声,而无源蜂鸣器则和电磁扬声器一样,需要接在音频输出电路上才能发声。

7. 电压比较器的用途主要是波形的产生和变换,模拟电路到数字电路的接口。

8. 低功耗唤醒的常用方式:处理器进入低功耗后就停止了很多活动,当出现一个中断时,可以唤醒处理器,使其从低功耗模式返回到正常运行模式。

因此在进入低功耗模式之前,必须配置莫个片内外设的中断,并允许其在低功耗模式下继续工作。如果不这样,只有复位和重新上电才能结束低功耗模式。处理器唤醒后首先执行中断服务程序,退出后接着执行主程序中的代码。

9. 注册中断服务函数:中断服务函数已经编写好,但当中断事件发生时,CPU还是无法找到它,因为我们还缺少最后一步:注册中断服务函数。

注册有两种方法:一是直接利用中断注册函数,优点是操作简单,可移植性好,缺点是由于把中断向量表重新映射到SRAM中而导致执行效率下降;还有一种是需要修改启动文件,优点效率很高,确定可移植性不高。

10. 很多的MCU提供数字电源VDD/GND和模拟电源VDDA/GNDA。通常建议是采用两路不同的3.3V电源供电。但为了节省成本,也可以采用单路3.3V电源,但VDDA/GNDA要通过电感从VDD/GND分离出来。

一般GNDA和GND最终还是要连接在一起的,建议用一个绕线电感连接并且接点尽可能靠近芯片(电感最好放置在PCB背面)。

本文转自网络,版权归原作者。

围观 35

随机数在单片机的应用中也是很多的,当然产生随机数的方法有很多,当中有一个就是利用单片机定时器,取出未知的定时器THX和TLX的值,再加以运算得到一个规定范围内的随机数值。这做法也是可行的。或者预先写好一个随机数表,然后进行取数据。也是可以的。

KEIL里面产生随机数的函数确实是rand(),但头文件是stdlib.h,不是time.h。C语言提供了一些库函数来实现随机数的产生。

C语言中有三个通用的随机数发生器,分别为 rand函数、random函数、randomize 函数,但是rand函数产生的并不是真意正义上的随机数,是一个伪随机数,是根据一个数,称之为种子,为基准以某个递推公式推算出来的一系数,当这系列数很大的时候,就符合正态公布,从而相当于产生了随机数。

但这不是真正的随机数,当计算机正常开机后,这个种子的值是定了的,除非破坏了系统,为了改变这个种子的值,C提供了srand()函数,它的原形是void srand( int a)。在调用rand函数产生随机数前,必须先利用srand()设好随机数种子,如果未设随机数种子,rand()在调用时会自动设随机数种子为1。一般用for语句来设置种子的个数。

单片机产生随机数的两种方法:

方法一:定时器直接随机取值

每按一次按键生成一个随机数,这个随机数实际是把定时器的值给取出来了,并不能算绝对的随机、方法二才是真正意义上的随机。
  
方法二:用定时器加rand()随机函数来实现

单片机上电之后通过按键去启动取随机数,若是单片机上电就立即取随机数的话,那每次上电随机的结果都是一样的。然后是0 到9不重复的随机数,程序中用了循环来判断是否和前面取的随机数相同,相同则进入,下次取随机数,不同则存入数组。

来源:EDN电子技术设计

围观 593

单片机应用中,常常会遇到这种情况~~在用单片机制作电子钟或要求根据时钟启控的控制系统时,会突然发现当初校准了的电子时钟的时间竟然变快或是变慢了。于是,尝试用各种方法来调整它的走时精度,但是最终的效果还是不尽人意,只好每过一段时间手动调整一次。那么,是否可使时钟走时更精确些呢?

现探讨如下:

一、误差原因分析

1. 单片机电子时钟的计时脉冲基准,是由外部晶振的频率经过12分频后提供的,采用内部的定时,计数器来实现计时功能。所以,外接晶振频率的精确度直接影响电子钟计时的准确性。

2. 单片机电子时钟利用内部定时,计数器溢出产生中断(12MHz晶振一般为50ms)再乘以相应的倍率,来实现秒、分、时的转换。大家都知道,从定时,计数器产生中断请求到响应中断,需要3_8个机器周期。定时中断子程序中的数据人栈和重装定时,计数器的初值还需要占用数个机器周期。此外。从中断人口转到中断子程序也要占用一定的机器周期。例如:

从上述程序可以看出,从中断人口到定时/计数器初值的低8位装入需要占用2+2+2=6个机器周期。所以,在编程时一般会把这6个机器周期加入定时/计数器的初值中。但是,从定时,计数器溢出中断请求到执行中断需要几个机器周期(3~8个机器周期)。就很难确定准确值,正是这一原因导致了电子时钟计时的不准。

二、解决方法

1、采用高精度晶振方案

虽然采用高精度的晶振可以稍微提高电子钟计时的精确度,但是晶振并不是导致电子钟计时不准的主要因素,而且高精度的晶振价格较高,所以不必采用此方案。

2、动态同步修正方案

从程序人手,采用动态同步修正方法给定时,计数器赋初值。动态同步修正方法如下:由于定时,计数器溢出后,又会从O开始自动加数,故在给定时/计数器再次赋值前,先将定时,计数器低位(TLO)中的值和初始值相加,然后送人定时,计数器中,此时定时,计数器中的值即为动态同步修正后的准确值。

具体程序如下:

采用此种方法后,相信制作的电子时钟的精度已有提高了。

3、自动调整方案

采用同步修正方案后,电子时钟的精度虽然提高了很多,但是由于晶振频率的偏差和一些其他未知因素的影响(同一块电路板、同样的程序换了一片单片机后,走时误差不一样,不知是何原因),时间长了仍然会有积累误差。为此,可采用自动调整方案。实际上是一种容错技术。其自动调整原理为:实测出误差Is所需的时间,然后每隔这样一段时间后就对秒进行加“1”或减“1”调整。例如:电子钟每过50小时就慢1秒,其自动调整程序如下:

以下是一个完整实例:

来源:畅学电子,转载此文目的在于传递更多信息,版权归原作者所有。

围观 111

介绍UART

最早的串行通讯设备可以追溯到电报机,它使用长度可变的脉冲信号进行数据传输。要说早期的芯片级UART,不得不提一下DEC,该公司的PDP系列计算机用上了第一个UART。当时的UART的线路占据了整个电路板,体积巨大!可以联想一下早期计算机的样子,如下图。


如今PC机上的串口早已被USB取代,对RS-232(也称标准串口)有需求的用户通常使用USB转串口线,这里常见的有CH340串口驱动程序。在UART通信中,两个UART直接通信。

发送端的UART将来自控制设备(如CPU)的并行数据转换为串行数据,以串行方式将其发送到接收端的UART,然后由接收端的UART将串行数据转换为并行数据以用于接收设备的正常处理。这里只需要两条线RX/TX即可在两个UART之间传输数据,如下图所示。


UART传输的数据被封装成数据包。每个数据包包含1个起始位,5~9个数据位(取决于UART的具体设置),一个可选的奇偶校验位以及1个或2个停止位,如下图所示。


起始位

UART数据传输线通常在不传输数据时保持在高电平。为了开始数据传输,发送端UART在一个时钟周期内将传输线从高电平拉低到低电平。当接收端UART检测到高电压到低电压转换时,它开始以波特率的频率读取数据位中的每一位数据。

数据

数据位包含正在传输的实际数据。如果使用奇偶校验位,则可以是5位,最多8位。如果不使用奇偶校验位,则数据帧的长度可以为9位。在大多数情况下,数据首先以低有效位发送。

校验位

在串口通信中一种简单的检错方式。有四种检错方式:
偶校验
奇校验
高校验
低校验

对于偶和奇校验的情况,串口会设置校验位(数据位后面的一位),用一个值确保传输的数据有偶个或者奇个逻辑高位。

停止位

发送端UART将数据传输线从低电压驱动到高电压至少持续两位数据的时间宽度来表示整个数据包的传输已经结束。由于数据是在传输线上定时的,并且每一个设备有其自己的时钟,很可能在通信中两台设备间出现了小小的不同步。因此停止位不仅仅是表示传输的结束,并且提供计算机校正时钟同步的机会。适用于停止位的位数越多,不同时钟同步的容错性越好,但是数据传输率同时也越慢。

波特率

波特率是串口数据的传输速度,即Bit/s,常见的波特率比如:9600,19200,38400,57600,115200等。假设目前UART的配置为,1个起始位,8个数据位,0个校验位,1个停止位,那么9600的波特率,可以计算出每一位数据的时间宽度为:


那么传输一个字节(也就是10 bit 数据)需要的时间为 1.04 毫秒。

UART传输过程

①发送端UART从数据总线转换并行数据。

②发送端UART将起始位,奇偶校验位和停止位添加到数据包中,示意图如下。


③整个数据包从发送端UART串行发送到接收端UART,接收端UART按照预先配置好的波特率对数据线进行采样,示意图如下。


④接收端UART解析接收的数据,丢弃数据包中的起始位,奇偶校验位和停止位。

⑤接收UART将串行数据转换回并行数据,并将其传输到接收端的数据总线。

本文转自: STM32嵌入式开发(微信号:c-stm32),作者:acket,转载此文目的在于传递更多信息,版权归原作者所有。

围观 59

对于搞单片机的特别用8051系列工程师来说,谈到单片机的RTOS,很多时候会问一句:“为什么要用RTOS?单片机就这一点资源,使用RTOS能保证效率吗?”

对于这个问题,我会反问:“你用单片机的目的是什么?是为了用单片机的C编程,单片机的汇编编程甚至于用单片机的二进制指令编程?”上个世纪80年代,工程师用二进制指令给Z80编程,现在还有谁在用?现在还有人死抱着汇编不放,但越来越多的人工程师使用C编程(我起初也是使用汇编的),为什么?因为我们的目的是在有限的时间甚至是不充足的时间内把项目保质保量的完成!使用什么工具和方法是次要的(如果你的项目以成本放在第一位,则另当别论,这时,也是要考虑开发时间的)。时间就是金钱啊,一个产品在单片机上增加些许成本是可以接受的。况且,使用8051系列单片机时,单片机资源也常有富余,CPU一般情况也只是空转,这就为它使用RTOS创造了条件。

那么,使用RTOS的好处呢?我举一个例子吧。假设我们编一个串行通讯程序,通讯协议如下:

数据包长度为NBYTE,起始字节为STARTBYTE1,STARTBYTE2,最后一个字节为检验和,中间字节不可能出现连续出现STARTBYTE1,STARTBYTE2。

第一种方法,在中断中处理协议:

unsigned char Buf[NBYTE-2];bit GetRight=0;
void comm(void) interrupt 4//"串行口中断"{
static unsigned char Sum,Flag=0,i;
unsigned char temp;
if(RI==1)
{
RI=0;
temp=SBUF;
switch(Flag)
{
case 0:
if(temp==STARTBYTE1)
{
Flag=1;
}
break;
case 1:
if(temp==STARTBYTE2)
{
Sum=STARTBYTE1+STARTBYTE2;
i=0;
Flag=2;
break;
}
if(temp==STARTBYTE1) break;
Flag=0;
break;
case 2:
if(temp==STARTBYTE1)
{
Flag=3;
break;
}
Sum+=temp;
if((i>=(NBYTE-3))&&Sum==0)
{
GetRight=1;
Flag=0;
break;
}
Buf[i++]=temp;
break;
case 3:
if(temp==STARTBYTE2)
{
Sum=STARTBYTE1+STARTBYTE2;
Flag=2;
i=0;
break;
}
Sum+=STARTBYTE1;
if((i>=(NBYTE-3))&&Sum==0)
{
GetRight=1;
Flag=0;
break;
}
Buf[i++]=STARTBYTE1;
if(temp==STARTBYTE1)
{
break;
}
Sum+=temp;
if((i>=(NBYTE-3))&&Sum==0)
{
GetRight=1;
Flag=0;
break;
}
Buf[i++]=temp;
Flag=2;
break;
}
}}

第二种方法,使用队列中断函数:

void comm(void) interrupt 4//"串行口中断"{
if(RI==1)
{
RI=0;
SBUF 入队;
}}

主程序不断调用的函数:

unsigned char Buf[NBYTE-2];
unsigned char ReadSerial(unsigned char *cp){
unsigned char i;
unsigned char temp,Sum;
temp=队列中数据个数;
if(temp<(NBYTE)) return 0;
出队 temp;
if(temp!=STARTBYTE1) return 0;
temp=队列首字节;
if(temp!=STARTBYTE2) return 0;
出队 temp;
sum=STARTBYTE1+STARTBYTE2;
for(i=0;i
{
temp=队列首字节;
if(temp==STARTBYTE1)
{
temp=队列次首字节;
if(temp==STARTBYTE2) return 0;
}
出队 temp;
*cp++=temp;
Sum+=temp;
}
temp=队列首字节;
Sum+=temp;
if(Sum!=0) return 0;
出队 temp;
return 1;}

第三种方法,使用RTOS中断函数:

void comm(void) interrupt 4//"串行口中断"{
OS_INT_ENTER();
if(RI==1)
{
RI=0;
OSIntSendSignal(RECIVE_TASK_ID);
}
OSIntExit();}
ID为RECIVE_TASK_ID的任务
void Recuve(void){
unsigned char temp,temp1,Sum,i;
OSWait(K_SIG,0);
temp=SBUF;
while(1)
{
while(1)
{
OSWait(K_SIG,0);
temp1=SBUF;
if((temp==STARTBYTE1)&&(temp1==STARTBYTE2)) break;
temp=temp1;
}
Sum=STARTBYTE1+STARTBYTE2;
OSWait(K_SIG,0);
temp=SBUF;
for(i=0;i
{
OSWait(K_SIG,0);
temp1=SBUF;
if((temp==STARTBYTE1)&&(temp1==STARTBYTE2))
{
OSWait(K_SIG,0);
temp=SBUF;
i=-1;
Sum=STARTBYTE1+STARTBYTE2;
continue;
}
Buf[i]=temp;
Sum+=temp;
temp=temp1;
}
Sum+=temp1;
if(Sum==0) OSSendSignal(命令解释任务 ID);
}}

以下为这几种方法的比较:

可读性和编程容易性方面,第三钟方法最好(如果允许使用goto语句,程序更加简单易读),第二种次之(因为要编队列程序),第一种最差。如果协议更加复杂,这方面更加明显。程序简单易读,自然出错机会小了。

RAM占用方面,第三种方法较少,第二种最多(因为队列占用大量空间),第一种最少。

中断执行时间方面,第三种方法最长,第二种最短,第一种较长。

从功能方面,第三种方法最强,它还可以进行超时处理(虽然例子程序没有),其它方法均不行。

如果数据来的太快,命令处理程序来不及处理,三种方法处理方式不太一样,第一种和第三种方法类似:丢弃以前数据,第二种则是丢弃后到的数据。而且,第二种方法必须等命令处理程序完成后才处理下一个数据包,而第一种和第三种方只需命令处理程序将数据收取后就可处理下一个数据包。也就是说,第一种和第三种与命令处理程序并行处理,第二种方法为串行处理。

现在,一般情况下,开发的效率第一,执行的效率(包括执行时间和资源占用)第二。在这种情况下,降低些许效率换取开发的效率的较大提高,何乐而不为?何况,单个模块的执行的效率高不等于整个程序执行效率高。例如,如果程序需要等待一段时间,一般用程序延时或定时器延时。无论何种方法,CPU不再处理其它工作,效率很低。而用RTOS,等待的时候CPU可以处理其它工作,效率得到提高。

以下摘自《uC/OS-II--源码公开的实时嵌入式操作系统》

“实时内核也称为实时操作系统或RTOS。使用它使得实时应用程序的设计和扩展变得容易。不需要大的改动就可以增加新的功能。通过应用程序分割为若干独立的任务,RTOS使得应用程序的设计过程大为简化。使用可剥夺性的内核时,所有时间要求苛刻的事件都得到了尽可能快捷、有效的处理。通过有效的服务;如信号量、邮箱、队列、延时、超时等;RTOS使得资源得到更好的利用。

“如果应用项目对额外的需求可以承受,应该考虑使用实时内核。这些额外的需求是:内核的价格,额外ROM/RAM开销,2至4百分点的CPU额外负担。

“还有没提到的一个因素是使用实时内核增加的价格成本。在一些应用中,价格就是一切,以至于对使用RTOS连想都不敢想。”

总而言之,适用的就是最好的,不要拒绝RTOS,在它适用的情况下,它工作得很好

本文转自:单片机与嵌入式

围观 22

页面

订阅 RSS - 单片机