快捷搜索:  as  test

C51单片机串口驱动设计的常见问题解析

1、C51串口的弊端。

C51的串口收发法度榜样信托大年夜家都很认识了,在hello.c里面有很简单的例程,不知 道大年夜家有没有留意到hello.c里面有一句很不显眼的语句“TI = 1;” 当你在初始化串口的时刻假如你不让TI = 1的话,信托你看到你的数据永世都发不出去,debug里运行stop会看到法度榜样实际上是进行到了while(!TI);的语句处进入逝世轮回了。

深 入一点的看,可以在keil/c51/lib下发明putchar函数的原文件,和许多软件串口驱动一样printf()都是反复调用putchar() 来实现的,以是putchar函数是我们进入逝世轮回的要害。putchar函数很简单,在此中有一个最小实现要领,我就以这个简单的例子来解释。

char putchar(char c){

while(!TI);

TI = 0;

return(SBUF = c);

}

很 显然,C51中缺省的putchar函数是靠查询并等待TI这个标志位来实现串口发送的,也便是说,在putchar函数中确凿发送了所有的数据,然则每 发送一个BYTE前都等待了一段光阴。这就不难理解为什么在初始化串口的时刻必须把TI置位了,无非是想让发第一个数据的时刻让putchar函数能顺利 履行。

留意,这里有一个问题呈现了,我们可以把UART理解为一个自力的外设,在一次数据装订后就应该交给UART自动完成数据收发,也 便是说宝贵的CPU光阴应该不在这里挥霍掉落,以是我们可以做出这样一个结论C51的putchar函数着实是有弊真个,它在等待TI置位时大年夜大年夜占用了 CPU光阴。

2、刨根问底

为什么C51这么醉翁之意的设计这样一个根基的函数来实现收发呢,为什么必须用TI来支持这个判断,我在写法度榜样的时刻发清楚明了一点,着实便是51中UART的一点特点。

51 的UART可以理解为一个自动的串行输出外设,每对SBUF写一个数据就会触发UART的一次串行输出操作,即在准时器分频的根基上慢慢移初所稀有据位 (包括启始为和停止位等等),移出速率是靠准时器溢出光阴来来度量的,以是对付MCU来说这个光阴一样平常都对照长。是以假如在准时器还没有溢出的时刻再对 SBUF写数据的话会从新引起这个新数据的发送。这样假如你写

while(1)

SBUF = ‘a’;

着实是没有任何意义的,发出的肯定是乱码。

因为以上的缘故原由我们就可以看出TI确凿是上次发送的停止和下次发送的开始的结点。C51也是使用了这样一个特点来实现自己的函数。

3 改进的PUTCHAR函数

缓 冲区是连接奉告设备和低速设备的接口,我们的串口收发着实便是MCU的高速和UART的低速的协同事情,以是我们应该设计一个缓冲区作为数据的暂存位置, 当设备发送数据的时刻假如UART正在忙于发上一个数据那么就应该把数据存在BUFF里面,而假如UART不忙了就应该把数据从BUFF里面顺序读出并发 送。

这个恰恰相符行列步队的观点,我就设计了一个轮回行列步队来实现这个功能。而在

putchar函数就应该设计成

void putchar(char c)

{

if(UART不忙)

直接发送数据到SBUF

else

把数据写到BUFF里

}

而中断函数则应该写为

ISR(){

……

if(TI){

TI = 0;

if(BUFF里面还稀有据)

取下一个数据并发送;

}

新的问题又呈现了,什么是UART忙,他与TI的关系若何,是不是TI = 0便是UART忙?

前两个问题先不说,着末一个问题的谜底很显然是“no!”,从最极度的角度来看,上电后UART便是余暇,TI也应该即是0!

上面的几个问题从另一个角度也可以获得谜底,这里有一点点哲理的问题,一个物品一样平常只能完成一件工作,既然TI已经作为上次发送的停止和下次发送的开始的结点那么它应该不是作为UART忙的美丽。

4、着末的设计概要

从OS的角度来看UART是一种资本,对付我们的法度榜样我们把SBUF看做它的载体,以是对付高速和低速设备的同步问题我们应该引入互斥量来实现对这个资本占用环境的标志。以是我设计的串口驱动里写了一个mutex_sbuf来实现这个功能。

后面的工作就简单了

void putchar(char c)

{

if(mutex_sbuf == 0){

EA = 0

mutex_sbuf = 1;

SBUF = c;

EA = 1

}

else

把数据写到SBUF里面

}

ISR()

{

……

if(TI){

TI = 0;

mutex_sbuf = 0;

if(BUFF里面还稀有据){

mutex_sbuf = 1;

取下一个数据并送;

}

}

}

写到这里说的差不多了,没兴致了:( 今后我把法度榜样贴上来供大年夜家参考

盼望大年夜家能把我的法度榜样优化一下,我现在的这个版本的driver是用纯C写的,对ROM的占用太大年夜了。今后我会用ASM来改写部分代码。

而且还有一个问题便是C51对付指针的应用很麻烦,法度榜样很轻易跑飞,我的代码还不是足够的清晰,由于便是指针的乱跑,以是我在需要的函数里面加了指针类型限制,然则我发明假如都加限制的话反而也会飞。

过两天我会放上来盼望大年夜家能一路把这个写好。

写得不错,但我不倾向采纳中断发送。由于假如采 用中断发送的话,必要一个发送缓冲区,缓冲区设多大年夜?只设一个字节的话,那么调用putchar的时刻是不是先得判断缓冲区非空,假如不空则printf 一类的函数仍旧必要等待。缓冲区设很大年夜的话,有两个问题,一是51原先内存就小,二仍旧是必要判断缓冲区空不空。斟酌再三,照样用的查询发送。到底采纳查 询或中断发送,可能要根据自己的需求来选择。

别的,KeilC写的不见得就比asm写的占空间大年夜;c指针跑飞的缘故原由,大年夜部分是指针越界,比如申请了5字节内存,应用了第6个,把其余变量冲掉落了等等,不在于你是否加cast,也便是说与强制类型转换无关,要加强对查表等索引指针的反省,确保指针不越界。

确凿是这样的,根据项目必要吧,我现在只是想写一个模块出来给大年夜家参考:)

代码量大年夜是由于我用了轮回行列步队,对付buff的操作险些是透明的,以是险些不用斟酌,还有一点由于我用的是RD2的芯片,以是有ERAM,我对内存的斟酌就轻细少了一些。

假如不用中断的话就要写一个scheduler来实现,我想今后要写一个调整器的实现要领,不过我现在不知道怎么模拟串口收数据,好象这个问题对照麻烦,斑竹有没有什么好的设法主见?

于这个问题我想照样有需要评论争论一下的,你可能觉得我对buff的校验不到位,以是孕育发生了越界,然则我想问为什么有了casting今后就可以不越界了呢

我对C51不是很认识,然则我感觉要害可能在于函数嵌套过多,虽然布局了了了,然则客栈不敷用了。

别的一个必要留意的是为什么老是跑到IDATA里面,这个我也不解:)

我所有的buff都是在XDATA中的,而且我用了MALLOC函数,但我又用的是compact mode

您可能还会对下面的文章感兴趣: