我做的第14章思考与练习3、4的答案,请马老师点评
第3题:题上说假定主程序循环了1000次,发送1000个0x55,请判断串口能否发出1000个0x55,有没有产生字符丢失或溢出现象?为了让主程序循环了1000次我的代码如下:
#include <mega8515.h>
#define BAUD 9600 //波特率采用9600bps
#define CRYSTAL 11059200 //系统时钟11.0592MHz
//计算和定义波特率设置参数
#define BAUD_SETTING (unsigned int)((unsigned long)CRYSTAL/(16*(unsigned long)BAUD)-1)
#define BAUD_H (unsigned char)(BAUD_SETTING>>8)
#define BAUD_L (unsigned char)(BAUD_SETTING)
// USART控制和状态寄存器的标志位定义
#define FRAMING_ERROR (1<<FE)
#define PARITY_ERROR (1<<PE)
#define DATA_OVERRUN (1<<DOR)
#define DATA_REGISTER_EMPTY (1<<UDRE)
// USART Transmitter buffer
#define TX_BUFFER_SIZE 5
unsigned char tx_buffer;
unsigned char tx_wr_index,tx_rd_index,tx_counter;//队列写指针,读指针,待发送字符个数
// USART Transmitter interrupt service routine
interrupt void usart_tx_isr(void)
{
if(tx_counter)//如果队列中还有未发送的数据
{
--tx_counter;//未发送字符个数减1
UDR=tx_buffer;//发送一个字符
if(++tx_rd_index==TX_BUFFER_SIZE)tx_rd_index=0;//读指针指向下一个未发送的字符,如果指到了队尾,则回到队首
}
}
void putchar(unsigned char c)//向发送缓冲区写一个字符
{
while(tx_counter==TX_BUFFER_SIZE);//如果发送队列满,则等待
#asm("cli")//关总中断,防止发送中断程序改变相关的变量
if(tx_counter||((UCSRA&DATA_REGISTER_EMPTY)==0))//如果前面还有未发送的或者未发完的数据
{
tx_buffer=c;//将现在的数据放在队列后部
if(++tx_wr_index==TX_BUFFER_SIZE)tx_wr_index=0;//写指针指向下一个要存放的位置,如果指到了队尾,则回到队首
++tx_counter;//发送个数加1
}
else
UDR=c;//无待发送数据且发送寄存器空,直接发送
#asm("sei")//开总中断
}
void main(void)
{
unsigned int i=0;
PORTD=0x03;
DDRD=0x02;//TXD(PD1)输出,RXD(PD0)输入,上拉有效,TXD输出高电平
UCSRA=0X00;
UCSRB=(1<<TXCIE)|(1<<TXEN);//使能TXC中断,接收不允许,发送允许
UCSRC=(1<<URSEL)|(1<<UCSZ1)|(1<<UCSZ0);//8位数据位,1位停止位,无奇偶位
UBRRH=BAUD_H;
UBRRL=BAUD_L;//设置波特率
#asm("sei")//使能总中断
while(1)
{
putchar(0x55);
if(++i==1000)UCSRB=(0<<TXCIE)|(0<<TXEN);
}
}
用变量i计数,当i=1000时,关闭发送使能和发送中断使能(我不知道这样测试行不行)
实验结果,每次单片机复位后,串口调试助手接收到的数如下
http://cache.amobbs.com/bbs_upload782111/files_45/ourdev_677180OCSK3T.jpg
串口调试助手接收情况 (原文件名:2.jpg)
本来我以为不会丢失,但每次按下复位按钮后,都只能收到995个数,所以我认为会产生丢失字符。后来我又把i改成2000或者别的数,但每次都是收到的数都少5个,我的发送中断缓冲区大小是5,后来又改了发送缓冲区的大小,发现发送缓冲区是多大,就会丢多少个数据,请马老师解释一下吧
第4题:当本机没有收到任何数据时在主程序中调用getchar(),我认为会发生死等现象,就是程序会一直停在while(rx_counter==0);上导致系统不能干其他的事,我的解决办法是将while(rx_counter==0)改成
if(rx_counter==0)return
else...后面的程序不变,不知道行不行? 我教程中的许多练习和思考都是为向高手进阶准备的,不同层次的人,给出的解答也不同。
对于你的解答,还是入门级,而且还木有真正入门。
书中这个题目中有个括号,里面的问题你思考过吗?
先说明你的测试为什么会少接受字符,错误在于你的主程序的发送部分:
当你把最后一个0x55用PUTCHAR发送后,这个0x55在哪里?应该在发送缓冲中,而且是最后的一个,在等待发送。
而你在后面马上把UART的发送禁止了,那么实际上发送缓冲区中所有的数据都还没有送出,当然接受就少了,少的个数就是你发送缓冲区的个数。
你实际根本没有理解这段结构的作用。发送缓冲区中的数据是没有发送掉的,只是在排队。
正确简单的测试应该如下:
while(i<1000)
{
putchar(0x55);
++i;
}
while(1){};
==============================================================
现在再解释第3题的正确解答和深入的问题。
首先是肯定可以发送1000个字符,不会产生丢失和溢出的现象。如果连这个基本的要求都做不到,那么这段代码就不能使用了。
既然这样,那么问这个问题的出发点是什么?
MCU执行的时间和效率!
如果按9600bps计算,1000个字符需要1.04s的时间!注意这里是关键点。
那么下面主程序中的第1个循环结构while执行完成需要多少时间?
while(i<1000)
{
putchar(0x55);
++i;
}
while(1){};
缓冲区为5个和缓冲区为1000个时,第1个循环结构while执行完成需要的时间一样吗?差别是多少?
只有明白上面的差别,你才能真正理解和掌握如何正确使用这段代码,如何设计和定义自己的通信包,以及定义缓冲区的大小。
==============================================
正确的方法是,定义合适长度(尽量小点)的数据包,缓冲区的大小与数据包相同,这样你的第1个循环结构while执行时间非常快,然后可以去做其他的与UART无关的事情,比如送显示,读按键,计算等。等待后台UART自己把数据发送完成,再回来处理UART的工作。
===============================================
对于第4题的回答,你只对了最基本的:“当本机没有收到任何数据时在主程序中调用getchar(),我认为会发生死等现象,就是程序会一直停在while(rx_counter==0);上导致系统不能干其他的事”
但处理的方法是不对的,你return个0,是什么意思?是没有数据?那么如果接受到的数据为0,又怎么办?
===============================================
14.1.3节中的代码结构是CVAVR产生的,思路非常好,是PC上通用的一种结构,但在实际应用中还是有一点问题,需要修改和调整。
我在14.2.3节中的实际例子,就是采用了这个思想,但处理的方法是不同的,更加适合嵌入式的应用。
14.2.3这个例子中已经回答了3、4的问题。
在14.2.3基础上,经过变化,就能实现对通信不正常的判断。比如规定上位机每200ms发送一次数据包,那么下位机就可以通过判断接收标志,如果超过500ms,仍然没有任何接收的话,那么就是通信线路出现问题,没接或断了。
通信的最基本和低层是正确发送一个字节,这个主要依赖硬件,而完整的、实用可靠的一个通信过程,是需要程序员设计的。 缓冲区为5个和缓冲区为1000个时,第1个循环结构while执行完成需要的时间一样吗?差别是多少?
===============================================
答:不一样,5个时慢,1000个时快,差别是995个执行一次putchar()的时间,因为缓冲区为5时,当tx_counter=5时就会等待发送中断,不会再执行下一次putchar(),而缓冲区1000时,直到tx_counter=1000时,才会等待发送中断,之后发送数据的时间都是一样的,都是发送一个数后tx_counter减1,然后进发送中断发一个数,如此下去直到发送完毕,时间的差别来在第一次进入发送中断函数前,不知道这样解释对不对? 差别不是955次PUTCHAR的时间!(程序执行所需要的时间可以忽略掉)而是955个数据从UART串出数据的时间!
发送1000个字节的数据,如果发送缓冲区为1000的话,第1个循环结构while执行时间非常快,它只要将1000个数据写入缓冲就可以了。
而发送缓冲区为5个的话,第1个循环结构while执行就增加了等待时间,要等的时间是UART串出了954个字节,正在串出第955个数据,此时,主程序把最后一个字节写入缓冲区中最后的空位,第1个循环结构while执行才能完成。
那么UART串出955个字节需要多少时间?按9600bps/1/8/1格式计算,9600/10 = 960个/秒。因此,如果缓冲区为5的话,第1个循环结构while执行需要1秒!
1秒种,MCU可以做多少事情?不能白白的等在那里死等吧。
MCU的指令执行是0.1us级的,而串口发送字节需要几百个us,相差几千倍,如果发送字节多的话,高速MCU就被低速的串口拖死了,效率就降低了(浪费在等待上了)。
我给出的结构和方法,相当并行处理,MCU把数据送入缓冲后,可以马上去做其他的事情,不用死等UART发送,这就是中断+缓冲的优点。
现在的MCU速度都很快,就是改良的51,也可以到30M,32位的MCU,甚至达到90M,但是大量的新教科书(包括许多新出版的书,“名师”“名家”的书)还是采用老的轮询方式操作UART,发一个,等一个,再发下一个,看了都心寒。
正所谓:外行看热闹,内行看门道。换个角度解释,当你能看出门道,就是内行了,上升为“高手”了。 非常感谢马老师两次耐心又详细的解答,从您对串口研究和MCU执行的效率研究的如此之透彻让我实在佩服,我也是希望不但要会用书中的经典程序,而且要知道原理才尝试着回答课后的练习题的,以前我也是使用轮训的方法,看了您的书真是受益匪浅!
页:
[1]