打印

51单片机中串口的全双工问题求解

[复制链接]
4842|15
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
libing2005|  楼主 | 2008-9-18 01:20 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
我一直都坚信51单片机的串口是全双工的,但是一个简单的程序,我却解释不通!
代码如下:
#include <at89c51RC2.h>

/*******************************************************/

/* define */

sbit work_led = P1^5;

/* end define */

/*******************************************************/

/* declare functions */

void init_uart_t0(void);

//void it_uart(void) interrupt 4;

void it_t0(void);

/* end declare functions */

/*******************************************************/

/* declare global variable */

unsigned char counter_50ms = 0

unsigned char uart_data = 0;

bit flag_500ms = 0;

/* end declare global variable */

/******************************************************/

/* start main function */

void main(void)

    init_uart_t0();

    while(1)
    {
        if(flag_500ms)
        {
            flag_500ms = 0;

            work_led = ~work_led;
        }
    }        
}
/* end main function */

/*****************************************************/

void init_uart_t0(void)
{
    SCON = 0x50;             // uart in mode 1 (8 bit), REN=1

    TMOD = TMOD | 0x20 ;     // Timer 1 in mode 2 

    TMOD &= 0xF0;            // Timer 0 mode 1 with software gate

    TMOD |= 0x01;           // GATE0=0; C/T0#=0; M10=0; M00=1;

    TH0 = 0x4c;                //init values  t=(2$16-th0tl0)*11.0592MHz*12=50ms 

    TL0 = 0x00;        

    TH1 = 0xFD; // 9600 Bds at 11.0592MHz 

    TL1 = 0xFD; //9600 Bds at 11.0592MHz 

    ET0 = 1;    // enable timer0 interrupt    

    ES = 1;     // Enable serial interrupt

    EA = 1;     // Enable global interrupt 

    TR0=1;        // timer0 run 

    TR1 = 1;     // Timer 1 run 
}

/************************************************/

void it_uart(void) interrupt 4
{
    if (RI)
    {                     
        RI = 0;         

        uart_data = SBUF; // Read receive data
        
        SBUF = uart_data; 
    }
    else TI = 0;         
}                     
/***************************************************/

void it_t0(void) interrupt 1 /* interrupt address is 0x000b */
{
    TH0 = 0x4c;                //init values  t=(2$16-th0tl0)*11.0592MHz*12=50ms 

    TL0 = 0x00;        
    
    counter_50ms ++;

    counter_tx50ms ++;

    if(counter_50ms >= 10)
    {
        counter_50ms = 0;

        flag_500ms = 1;
    }                        
}
/***********************************************/
//all program end

这样能过串口调试助手向单片机连续发送数据,不同数据组间隔几百毫秒,单片机返回的数据数量总是小于发送给它的数据数量,也就是说有丢包现象!将串口的发送与接收分时操作,即改成半双工模式,未现问题!!

请大侠指点迷津!小弟多谢!

相关帖子

沙发
gjg191| | 2008-9-18 08:07 | 只看该作者

o

uart_data = SBUF; // Read receive data
        
        SBUF = uart_data; 
这里又收又发的你没判断TI阿

使用特权

评论回复
板凳
libing2005|  楼主 | 2008-9-18 09:10 | 只看该作者
你是说可能产生发送冲突了,上一次数据未发送完毕,又进行发送了?我觉得也有这种可能,我将其改成查询方式试试!

使用特权

评论回复
地板
原野之狼| | 2008-9-18 09:31 | 只看该作者

输入输出要做成缓冲机制

全双工完全没有问题。

使用特权

评论回复
5
libing2005|  楼主 | 2008-9-18 10:21 | 只看该作者

还有疑问,请大侠帮忙

如果是发送冲突,那是不是说,接收比发送快?否则怎么会在第二次接收时还未发送完毕??而对全双工的串口,发送与接收是相同速度的啊!

如果是这样,改成查询方式也不能解决问题啊,那只能设置接收缓冲区了么??

使用特权

评论回复
6
农民讲习所| | 2008-9-18 11:06 | 只看该作者

PC发送是连续的

一个挨一个。51中可能发生T0中断,延迟了串口接收,前个数据还放在接收缓冲寄存器中没读出。这样会发生:下次读出的时候,前次硬件发送没发送完。发送数据就冲了。这是没发生缓冲的后果。
你可以把T0中断关了再测试。

使用特权

评论回复
7
libing2005|  楼主 | 2008-9-18 11:57 | 只看该作者

多谢大侠指点

如果可能的话,也是延迟了发送,从而在下一次接收到来时,上一次还未发送完毕,从而造成冲突,我试试,把T0中断关了,看有没有效果!

使用特权

评论回复
8
lyjian| | 2008-9-18 12:02 | 只看该作者

接收也是双缓冲结构

按230.4Kbps算的话,接收一个字节需要43.4us。
这么长的接收时间相对中断相应时间来说足够了,除非像楼上说的这时发生了T0中断延迟了串口中断。这个问题可以通过设置串口中断为最高优先级来解决。
还有,接收和发送不一定是同时完成的,这段程序这样写就有问题:       
        RI = 0;         
        uart_data = SBUF; // Read receive data
        SBUF = uart_data; 

使用特权

评论回复
9
libing2005|  楼主 | 2008-9-18 12:13 | 只看该作者

回复6楼,农民讲习所

关掉T0中断后,问题依然,但PC只发送一个字节就没有问题,若连续发送一组数据就有问题,不是发送数量与接收数量不对,就是有乱码!

使用特权

评论回复
10
gjg191| | 2008-9-18 13:10 | 只看该作者

o

把你改后的程序再弄上来

使用特权

评论回复
11
农民讲习所| | 2008-9-18 13:55 | 只看该作者

估计你得检查下波特率是否正确

使用特权

评论回复
12
fsaok| | 2008-9-18 15:38 | 只看该作者

间距时间

因为PC的指令比51快,所以发送每个字符之间的间距时间,PC的比51的短

使用特权

评论回复
13
libing2005|  楼主 | 2008-9-18 16:26 | 只看该作者

回复10楼gjg191:我想查询,但不知怎么写

我将程序改成这样,半双工,没有问题,就是说间隔100毫秒没收到数据,就认这一组数据接收完了,然后在下一组数据来临前将收到的数据发出去

#include <at89c51RC2.h>

/*******************************************************/

/* define */

sbit work_led = P2^7;

/* end define */

/*******************************************************/

/* declare functions */

void init_uart_t0(void);

//void it_uart(void) interrupt 4;

void it_t0(void);

/* end declare functions */

/*******************************************************/

/* declare global variable */

unsigned char counter_50ms = 0,counter_tx50ms = 0;

unsigned char uart_buf[32];

unsigned char r_in = 0,uart_data = 0;

bit flag_500ms = 0;

/* end declare global variable */

/******************************************************/

/* start main function */

void main(void)
{
    unsigned char i = 0;

    unsigned int j = 0;

    init_uart_t0();

    while(1)
    {
        if(flag_500ms)
        {
            flag_500ms = 0;

            work_led = ~work_led;
        }
        if((counter_tx50ms >= 2) && (r_in != 0))
        {
            for(i = 0; i < r_in; i++)
            {
                SBUF = uart_buf;

                j = 2000;
                
                while(j--);
            }
            r_in = 0;
        } 
    }
}
/* end main function */

/*****************************************************/

void init_uart_t0(void)
{
    SCON = 0x50;             // uart in mode 1 (8 bit), REN=1

    TMOD = TMOD | 0x20 ;     // Timer 1 in mode 2

    TMOD &= 0xF0;            // Timer 0 mode 1 with software gate

    TMOD |= 0x01;           // GATE0=0; C/T0#=0; M10=0; M00=1;

    TH0 = 0x4c;                //init values  t=(2$16-th0tl0)*11.0592MHz*12=50ms

    TL0 = 0x00;

    TH1 = 0xFD; // 9600 Bds at 11.0592MHz

    TL1 = 0xFD; //9600 Bds at 11.0592MHz

    ET0 = 1;    // enable timer0 interrupt

    ES = 1;     // Enable serial interrupt

    EA = 1;     // Enable global interrupt

    TR0=1;        // timer0 run

    TR1 = 1;     // Timer 1 run
}

/************************************************/

void it_uart(void) interrupt 4
{
    if (RI)
    {                     // if reception occur
        RI = 0;         // clear reception flag for next reception

        uart_buf[r_in] = SBUF; // Read receive data

        r_in = ++r_in &0x1f;

        counter_tx50ms = 0;

        uart_data = SBUF;

        SBUF = uart_data;
    }
    else TI = 0;         // if emission occur
}                         // clear emission flag for next emission

/***************************************************/

void it_t0(void) interrupt 1 /* interrupt address is 0x000b */
{
    TH0 = 0x4c;                //init values  t=(2$16-th0tl0)*11.0592MHz*12=50ms

    TL0 = 0x00;

    counter_50ms ++;

    counter_tx50ms ++;

    if(counter_50ms >= 10)
    {
        counter_50ms = 0;

        flag_500ms = 1;
    }
}
/***********************************************/
//all program end

使用特权

评论回复
14
gjg191| | 2008-9-18 16:41 | 只看该作者

o

还是老毛病发送数据没管TI

使用特权

评论回复
15
libing2005|  楼主 | 2008-9-18 16:50 | 只看该作者

我不知怎么管

我在发送后加了延时啊,并且现在发一串字符没问题,但不能太长,不能发送时间不能太长,否则就达不到那个100毫秒了!
关于TI
while(!TI); TI = 0;应该可以吧!

使用特权

评论回复
16
wch16621| | 2008-9-21 11:09 | 只看该作者

上楼说的有道理

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

2

主题

8

帖子

0

粉丝