当前位置:首页 > 单片机 > 单片机
[导读]1、C51串口的弊端。C51的串口收发程序相信大家都很熟悉了,在hello.c里面有很简单的例程,不知 道大家有没有注意到hello.c里面有一句很不显眼的语句"TI = 1;" 当你在初始化串口的时候如果你不让TI = 1的话,相信你看到

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


本站声明: 本文章由作者或相关机构授权发布,目的在于传递更多信息,并不代表本站赞同其观点,本站亦不保证或承诺内容真实性等。需要转载请联系该专栏作者,如若文章内容侵犯您的权益,请及时联系本站删除。
换一批
延伸阅读

在电动汽车(EV)领域,牵引逆变器作为关键组件之一,扮演着至关重要的角色。它不仅影响电动汽车的驱动性能和效率,还直接关联到整车的安全性和耐用性。本文将详细介绍在选择电动汽车牵引逆变器时需要考量的多个方面,包括其保护机制与...

关键字: 逆变器 电动汽车 驱动

7月3日消息,AMD显卡战未来,再添实锤证据!

关键字: 驱动 英伟达

在当今高速发展的电子系统领域,信号完整性已然成为确保系统性能与可靠性的关键要素。从驱动到连接器的信号传输路径宛如一条信息高速公路,而接收端则如同这条公路的终点收费站,其设置的合理性直接关乎信号能否准确无误地抵达目的地。若...

关键字: 信号 连接器 驱动

串口全称是串行接口(Serial Interface),串口通讯指仅用一对传输线就能将数据以比特位进行传输的一种通讯方式。尽管串口通讯必按字节传输的并行通信慢,但是串口可以在仅用两根线的情况下完成数据传输,大大降低了成本...

关键字: 串口 UART

随着电脑技术的发展,一些老的设备在新电脑上不能被使用,主要原因是不管是台式电脑,还是笔记本电脑,都很少有串口接口,也就是我们常说的COM口。好在这些设备都有USB接口,不妨通过接口转换的方式,使我们的设备在新电脑上重新被...

关键字: 串口 USB

串口:串口是一个泛称,UART、TTL、RS232、RS485都遵循类似的通信时序协议,因此都被通称为串口。串口通讯应用是工控人必须掌握的一个技能,几乎在每一个项目中都会用到,今天我们就来详细比较一下它们究竟有何区别。

关键字: 串口 协议

在复杂的嵌入式系统或高性能计算环境中,以太网驱动的稳定性与可靠性至关重要。然而,有时开发者会遇到一些难以解释的现象,比如拔掉一个网口后,另一个原本工作正常的网口突然无法接收数据。这种看似不合逻辑的问题,往往隐藏着深层次的...

关键字: 以太网 驱动

在嵌入式开发过程中,许多系统通常使用串口驱动来满足通信要求,但在实际应用中,使用SPI通信方式会更加高效和快捷。

关键字: 串口 驱动

串口WiFi模块作为新一代嵌入式WiFi模块,因其体积小、功耗低的特点,广泛应用于物联网、智能家居等领域。

关键字: 串口 WiFi模块 嵌入式

在现代嵌入式系统设计中,FPGA(现场可编程门阵列)的灵活性和可重构性使其成为许多应用的理想选择。而在FPGA的开发和部署过程中,如何实现远程升级和故障恢复成为了一个重要议题。本文将详细探讨如何通过BPI FLASH实现...

关键字: FPGA 串口 MultiBoot 嵌入式系统
关闭