搜索
bottom↓
回复: 4

我做的第14章思考与练习3、4的答案,请马老师点评

[复制链接]

出0入0汤圆

发表于 2011-9-17 17:01:11 | 显示全部楼层 |阅读模式
第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[TX_BUFFER_SIZE];
unsigned char tx_wr_index,tx_rd_index,tx_counter;//队列写指针,读指针,待发送字符个数

// USART Transmitter interrupt service routine
interrupt [USART_TXC] void usart_tx_isr(void)
{
    if(tx_counter)//如果队列中还有未发送的数据
    {
        --tx_counter;//未发送字符个数减1
        UDR=tx_buffer[tx_rd_index];//发送一个字符
        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[tx_wr_index]=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时,关闭发送使能和发送中断使能(我不知道这样测试行不行)
实验结果,每次单片机复位后,串口调试助手接收到的数如下

串口调试助手接收情况 (原文件名:2.jpg)
本来我以为不会丢失,但每次按下复位按钮后,都只能收到995个数,所以我认为会产生丢失字符。后来我又把i改成2000或者别的数,但每次都是收到的数都少5个,我的发送中断缓冲区大小是5,后来又改了发送缓冲区的大小,发现发送缓冲区是多大,就会丢多少个数据,请马老师解释一下吧

第4题:当本机没有收到任何数据时在主程序中调用getchar(),我认为会发生死等现象,就是程序会一直停在while(rx_counter==0);上导致系统不能干其他的事,我的解决办法是将while(rx_counter==0)改成
if(rx_counter==0)return
else...后面的程序不变,不知道行不行?

阿莫论坛20周年了!感谢大家的支持与爱护!!

月入3000的是反美的。收入3万是亲美的。收入30万是移民美国的。收入300万是取得绿卡后回国,教唆那些3000来反美的!

出0入0汤圆

发表于 2011-9-18 14:09:41 | 显示全部楼层
我教程中的许多练习和思考都是为向高手进阶准备的,不同层次的人,给出的解答也不同。
对于你的解答,还是入门级,而且还木有真正入门。

书中这个题目中有个括号,里面的问题你思考过吗?

先说明你的测试为什么会少接受字符,错误在于你的主程序的发送部分:
        当你把最后一个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,仍然没有任何接收的话,那么就是通信线路出现问题,没接或断了。

通信的最基本和低层是正确发送一个字节,这个主要依赖硬件,而完整的、实用可靠的一个通信过程,是需要程序员设计的。

出0入0汤圆

 楼主| 发表于 2011-9-18 21:05:07 | 显示全部楼层
缓冲区为5个和缓冲区为1000个时,第1个循环结构while执行完成需要的时间一样吗?差别是多少?
===============================================
答:不一样,5个时慢,1000个时快,差别是995个执行一次putchar()的时间,因为缓冲区为5时,当tx_counter=5时就会等待发送中断,不会再执行下一次putchar(),而缓冲区1000时,直到tx_counter=1000时,才会等待发送中断,之后发送数据的时间都是一样的,都是发送一个数后tx_counter减1,然后进发送中断发一个数,如此下去直到发送完毕,时间的差别来在第一次进入发送中断函数前,不知道这样解释对不对?

出0入0汤圆

发表于 2011-9-18 23:31:21 | 显示全部楼层
差别不是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,发一个,等一个,再发下一个,看了都心寒。

正所谓:外行看热闹,内行看门道。换个角度解释,当你能看出门道,就是内行了,上升为“高手”了。

出0入0汤圆

 楼主| 发表于 2011-9-19 08:38:29 | 显示全部楼层
非常感谢马老师两次耐心又详细的解答,从您对串口研究和MCU执行的效率研究的如此之透彻让我实在佩服,我也是希望不但要会用书中的经典程序,而且要知道原理才尝试着回答课后的练习题的,以前我也是使用轮训的方法,看了您的书真是受益匪浅!
回帖提示: 反政府言论将被立即封锁ID 在按“提交”前,请自问一下:我这样表达会给举报吗,会给自己惹麻烦吗? 另外:尽量不要使用Mark、顶等没有意义的回复。不得大量使用大字体和彩色字。【本论坛不允许直接上传手机拍摄图片,浪费大家下载带宽和论坛服务器空间,请压缩后(图片小于1兆)才上传。压缩方法可以在微信里面发给自己(不要勾选“原图),然后下载,就能得到压缩后的图片】。另外,手机版只能上传图片,要上传附件需要切换到电脑版(不需要使用电脑,手机上切换到电脑版就行,页面底部)。
您需要登录后才可以回帖 登录 | 注册

本版积分规则

手机版|Archiver|amobbs.com 阿莫电子技术论坛 ( 粤ICP备2022115958号, 版权所有:东莞阿莫电子贸易商行 创办于2004年 (公安交互式论坛备案:44190002001997 ) )

GMT+8, 2024-4-27 07:00

© Since 2004 www.amobbs.com, 原www.ourdev.cn, 原www.ouravr.com

快速回复 返回顶部 返回列表