amoBBS 阿莫电子论坛

 找回密码
 注册
搜索
bottom↓
查看: 568|回复: 33

请教串口接收数据被别的事件长时间打断后再接收数据错...

[复制链接]
发表于 2019-8-29 14:35:20 | 显示全部楼层 |阅读模式
在STC15串口3接了一个测距模块,模块是上电后就发送数据的,现在碰到一个问题,正常上电后收到的模块数据是对的(把串口3收到的数据同时转发到串口1,用串口助手监控),如果按了外接的一个按键,进行别的操作,中间差不多延时20秒的时间,等操作结束发现数据错位了,比如正常的十几个字节是00 11 22 33 44 55 66 77 88 99 AA BB这样的,进行按键操作结束串口收到的数据就变成 BB 00 11 22 33 44 55 66 77 88 99 AA这样的了,不知道有的兄弟4个串口同时使用的时候是怎么分别处理不会影响的?

/********************** 主程序**************************/
void main()
{

        P0n_standard(0xff);        //设置为准双向口
        P1n_standard(0xff);        //设置为准双向口
        P2n_standard(0xff);        //设置为准双向口
        P3n_standard(0xff);        //设置为准双向口
        P4n_standard(0xff);        //设置为准双向口
        P5n_standard(0xff);        //设置为准双向口
        P6n_standard(0xff);        //设置为准双向口
        P7n_standard(0xff);        //设置为准双向口
       
        UART1_config(2);        // 选择波特率, 2: 使用Timer2做波特率, 其它值: 使用Timer1做波特率.
        UART2_config(2);        // 选择波特率, 2: 使用Timer2做波特率, 其它值: 无效.
        UART3_config(2);        // 选择波特率, 2: 使用Timer2做波特率, 其它值: 使用Timer3做波特率.
        UART4_config(2);        // 选择波特率, 2: 使用Timer2做波特率, 其它值: 使用Timer4做波特率.
       
        EA = 1;
       
       
        RunLED = 0;                                //开机运行灯亮
        LED = 1;                       //状态灯灭
       
        while(1)
                {
                        KeyScan();              //按键扫描
                        UART3_Proc();                        //串口3测距处理
                }
}


/********************** 1ms延时程序**************************/
void Delay1ms(unsigned int mDelay1)        //1ms延时,22.1184M晶振
{
        unsigned int mDelay2;
        for(;mDelay1>0;mDelay1--)
        {       
                for(mDelay2=0;mDelay2<2000;mDelay2++)
                {;;}
        }
}




/********************** 开门/关门按键检测**************************/
void KeyScan()
{
        if(OpenKey == 0)                //有开门按键
        {       
                Delay1ms(20);
                if(OpenKey == 0)               
                {  

                        MotorFI = 1;                        //电机正转开门
                        MotorBI = 0;                               
                        Delay1ms(20000);                //等20秒
                        MotorFI = 0;                        //电机反转关门
                        MotorBI = 1;       
                }               
        }


        if(CloseKey == 0)           //如果检测到关门开关闭合
        {
                Delay1ms(20);       
                if(CloseKey == 0)
                {  
                        Delay1ms(20000);                //等20秒
                        MotorFI = 1;                        //关闭电机供电
                        MotorBI = 1;                               
                }
        }
}



//========================================================================
// 函数: SetTimer2Baudraye(u16 dat)
// 描述: 设置Timer2做波特率发生器。
// 参数: dat: Timer2的重装值.
// 返回: none.
// 版本: VER1.0
// 日期: 2014-11-28
// 备注:
//========================================================================
void        SetTimer2Baudraye(u16 dat)        // 选择波特率, 2: 使用Timer2做波特率, 其它值: 使用Timer1做波特率.
{
        AUXR &= ~(1<<4);        //Timer stop
        AUXR &= ~(1<<3);        //Timer2 set As Timer
        AUXR |=  (1<<2);        //Timer2 set as 1T mode
        TH2 = dat / 256;
        TL2 = dat % 256;
        IE2  &= ~(1<<2);        //禁止中断
        AUXR |=  (1<<4);        //Timer run enable
}


//========================================================================
// 函数: void        UART1_config(u8 brt)
// 描述: UART1初始化函数。
// 参数: brt: 选择波特率, 2: 使用Timer2做波特率, 其它值: 使用Timer1做波特率.
// 返回: none.
// 版本: VER1.0
// 日期: 2014-11-28
// 备注:
//========================================================================
void        UART1_config(u8 brt)        // 选择波特率, 2: 使用Timer2做波特率, 其它值: 使用Timer1做波特率.
{
        u8        i;
        /*********** 波特率使用定时器2 *****************/
        if(brt == 2)
        {
                AUXR |= 0x01;                //S1 BRT Use Timer2;
                SetTimer2Baudraye(65536UL - (MAIN_Fosc / 4) / UART_BaudRate1);
        }

        /*********** 波特率使用定时器1 *****************/
        else
        {
                TR1 = 0;
                AUXR &= ~0x01;                //S1 BRT Use Timer1;
                AUXR |=  (1<<6);        //Timer1 set as 1T mode
                TMOD &= ~(1<<6);        //Timer1 set As Timer
                TMOD &= ~0x30;                //Timer1_16bitAutoReload;
                TH1 = (65536UL - (MAIN_Fosc / 4) / UART_BaudRate1) / 256;
                TL1 = (65536UL - (MAIN_Fosc / 4) / UART_BaudRate1) % 256;
                ET1 = 0;        //禁止中断
                INT_CLKO &= ~0x02;        //不输出时钟
                TR1  = 1;
        }
        /*************************************************/

        SCON = (SCON & 0x3f) | (1<<6);        // 8位数据, 1位起始位, 1位停止位, 无校验
//        PS  = 1;        //高优先级中断
        ES  = 1;        //允许中断
        REN = 1;        //允许接收
        P_SW1 = P_SW1 & 0x3f;        //切换到 P3.0 P3.1
//        P_SW1 = (P_SW1 & 0x3f) | (1<<6);        //切换到P3.6 P3.7
//        P_SW1 = (P_SW1 & 0x3f) | (2<<6);        //切换到P1.6 P1.7 (必须使用内部时钟)

        for(i=0; i<RX1_Length; i++)                RX1_Buffer[i] = 0;
        B_TX1_Busy  = 0;
        TX1_read    = 0;
        RX1_write   = 0;
}


//========================================================================
// 函数: void        UART2_config(u8 brt)
// 描述: UART2初始化函数。
// 参数: brt: 选择波特率, 2: 使用Timer2做波特率, 其它值: 无效.
// 返回: none.
// 版本: VER1.0
// 日期: 2014-11-28
// 备注:
//========================================================================
void        UART2_config(u8 brt)        // 选择波特率, 2: 使用Timer2做波特率, 其它值: 无效.
{
        u8        i;
        /*********** 波特率固定使用定时器2 *****************/
        if(brt == 2)        SetTimer2Baudraye(65536UL - (MAIN_Fosc / 4) / UART_BaudRate2);

        S2CON &= ~(1<<7);        // 8位数据, 1位起始位, 1位停止位, 无校验
        IE2   |= 1;                        //允许中断
        S2CON |= (1<<4);        //允许接收
        P_SW2 &= ~1;                //切换到 P1.0 P1.1
//        P_SW2 |= 1;                        //切换到 P4.6 P4.7

        for(i=0; i<RX2_Length; i++)                RX2_Buffer[i] = 0;
        B_TX2_Busy  = 0;
        TX2_read    = 0;
        RX2_write   = 0;
}

//========================================================================
// 函数: void        UART3_config(u8 brt)
// 描述: UART3初始化函数。
// 参数: brt: 选择波特率, 2: 使用Timer2做波特率, 其它值: 使用Timer3做波特率.
// 返回: none.
// 版本: VER1.0
// 日期: 2014-11-28
// 备注:
//========================================================================
void        UART3_config(u8 brt)        // 选择波特率, 2: 使用Timer2做波特率, 其它值: 使用Timer3做波特率.
{
        u8        i;
        /*********** 波特率固定使用定时器2 *****************/
        if(brt == 2)
        {
                S3CON &= ~(1<<6);        //BRT select Timer2
                SetTimer2Baudraye(65536UL - (MAIN_Fosc / 4) / UART_BaudRate3);
        }
        /*********** 波特率使用定时器3 *****************/
        else
        {
                S3CON |= (1<<6);        //BRT select Timer3
                T4T3M &= 0xf0;                //停止计数, 清除控制位
                IE2  &= ~(1<<5);        //禁止中断
                T4T3M |=  (1<<1);        //1T
                T4T3M &= ~(1<<2);        //定时
                T4T3M &= ~1;                //不输出时钟
                TH3 = (65536UL - (MAIN_Fosc / 4) / UART_BaudRate3) / 256;
                TL3 = (65536UL - (MAIN_Fosc / 4) / UART_BaudRate3) % 256;
                T4T3M |=  (1<<3);        //开始运行
        }
       
        S3CON &= ~(1<<5);        //禁止多机通讯方式
        S3CON &= ~(1<<7);        // 8位数据, 1位起始位, 1位停止位, 无校验

//        IE2   &= ~(1<<3);        //串口3禁止中断
                IE2   |=  (1<<3);        //串口3允许中断
        S3CON |=  (1<<4);        //串口3允许接收

       
        P_SW2 &= ~2;                //切换到 P0.0 P0.1
//        P_SW2 |= 2;                        //切换到 P5.0 P5.1

        for(i=0; i<RX3_Length; i++)                RX3_Buffer[i] = 0;
        B_TX3_Busy  = 0;
        TX3_read    = 0;
        RX3_write   = 0;
}

//========================================================================
// 函数: void        UART4_config(u8 brt)
// 描述: UART4初始化函数。
// 参数: brt: 选择波特率, 2: 使用Timer2做波特率, 其它值: 使用Timer4做波特率.
// 返回: none.
// 版本: VER1.0
// 日期: 2014-11-28
// 备注:
//========================================================================
void        UART4_config(u8 brt)        // 选择波特率, 2: 使用Timer2做波特率, 其它值: 使用Timer4做波特率.
{
        u8        i;
        /*********** 波特率固定使用定时器2 *****************/
        if(brt == 2)
        {
                S4CON &= ~(1<<6);        //BRT select Timer2
                SetTimer2Baudraye(65536UL - (MAIN_Fosc / 4) / UART_BaudRate4);
        }
        /*********** 波特率使用定时器3 *****************/
        else
        {
                S4CON |= (1<<6);        //BRT select Timer4
                T4T3M &= 0x0f;                //停止计数, 清除控制位
                IE2   &= ~(1<<6);        //禁止中断
                T4T3M |=  (1<<5);        //1T
                T4T3M &= ~(1<<6);        //定时
                T4T3M &= ~(1<<4);        //不输出时钟
                TH4 = (65536UL - (MAIN_Fosc / 4) / UART_BaudRate4) / 256;
                TL4 = (65536UL - (MAIN_Fosc / 4) / UART_BaudRate4) % 256;
                T4T3M |=  (1<<7);        //开始运行
        }
       
        S4CON &= ~(1<<5);        //禁止多机通讯方式
        S4CON &= ~(1<<7);        // 8位数据, 1位起始位, 1位停止位, 无校验
        IE2   |=  (1<<4);        //允许中断
        S4CON |=  (1<<4);        //允许接收
        P_SW2 &= ~4;                //切换到 P0.2 P0.3
//        P_SW2 |= 4;                        //切换到 P5.2 P5.3

        for(i=0; i<RX4_Length; i++)                RX4_Buffer[i] = 0;
        B_TX4_Busy  = 0;
        TX4_read    = 0;
        RX4_write   = 0;
}


//========================================================================
//
//
// 串口发送一个字节和字符串
//
//  
//========================================================================
void UART1_Send(u8 tx1)
{
                B_TX1_Busy = 1;                //标志发送忙
                SBUF = tx1;                //发一个字节
                while(B_TX1_Busy);        //等待发送完成
}



void UART3_Send(u8 tx3)
{
                B_TX3_Busy = 1;                //标志发送忙
                S3BUF = tx3;                //发一个字节
                while(B_TX3_Busy);        //等待发送完成
}



void PrintString1(u8 *puts)
{
    for (; *puts != 0;        puts++)
        {
                B_TX1_Busy = 1;                //标志发送忙
                SBUF = *puts;                //发一个字节
                while(B_TX1_Busy);        //等待发送完成
        }
}



void PrintString3(u8 *puts)
{
    for (; *puts != 0;        puts++)
        {
                B_TX3_Busy = 1;                //标志发送忙
                S3BUF = *puts;                //发一个字节
                while(B_TX3_Busy);        //等待发送完成
        }
}





/********************* UART1中断函数************************/
void UART1_int (void) interrupt UART1_VECTOR
{
        if(RI)
        {
                RI = 0;
                RX1_Buffer[RX1_write] = SBUF;
                if(++RX1_write >= RX1_Length)        RX1_write = 0;
        }

        if(TI)
        {
                TI = 0;
                B_TX1_Busy = 0;
        }
}


/********************* UART3中断函数************************/
void UART3_int (void) interrupt UART3_VECTOR
{
        if(RI3)
        {
                CLR_RI3();
                RX3_Buffer[RX3_write] = S3BUF;
                RT3 = S3BUF;                         //读入缓冲区
                R3_flag=1;                           //有数据,建立标志
                if(++RX3_write >= RX3_Length)        RX3_write = 0;
        }

        if(TI3)
        {
                CLR_TI3();
                B_TX3_Busy = 0;
        }

}


/********************* UART3处理函数************************/
void UART3_Proc()
{
        if(!R3_flag)return;                  //没有新数据,返回
        R3_flag=0;                                //清零数据标志
        RX3_Buffer[CT3]=RT3;        //数据存入缓冲区
        CT3++;                  //指针加1指向一下存储单元
        if(CT3>=13)         //判断指针是否超范围
                {
                CT3=0;       

                        UART1_Send(RX3_Buffer[0]);//转发到串口1查看是否正确
                        UART1_Send(RX3_Buffer[1]);
                        UART1_Send(RX3_Buffer[2]);
                        UART1_Send(RX3_Buffer[3]);
                        UART1_Send(RX3_Buffer[4]);
                        UART1_Send(RX3_Buffer[5]);
                        UART1_Send(RX3_Buffer[6]);
                        UART1_Send(RX3_Buffer[7]);
                        UART1_Send(RX3_Buffer[8]);
                        UART1_Send(RX3_Buffer[9]);
                        UART1_Send(RX3_Buffer[10]);
                        UART1_Send(RX3_Buffer[11]);
                        UART1_Send(RX3_Buffer[12]);
        }
}

发表于 2019-8-29 14:52:59 | 显示全部楼层
缓冲区太小, 循环覆盖了。
 楼主| 发表于 2019-8-29 15:18:58 | 显示全部楼层
模块每次发送数据是固定的,13个字节
发表于 2019-8-29 17:23:58 | 显示全部楼层
没有仔细看程序,如果模块的数据是一直发送的,可以考虑加个数据校验吧,校验不正确的数据就丢弃了
发表于 2019-8-29 17:32:19 | 显示全部楼层
没有看明白楼主,先不看楼主代码,看下楼主人吧,
肯定是设计思路问题,数据是一直有还是随机有,还是不知道啥时候有,你那20秒钟时间内要收多少串口数据
发表于 2019-8-29 17:47:57 | 显示全部楼层
我也好想遇到过,我用的串口1和串口2,串口2连了一块串口屏,不确定什么时候会用数据过来,关键是我还tm用了一个ad,读取ad的时候时序不能被打断,所以只能关掉中断,那么如果我在读ad的时候来了串口数据,我还能收到吗?如果低优先级的串口正在接受数据这时候高级别串口中断来了,那么我低优先级的中断不会被打乱吗?串口中断执行后距离下一个字符过来还有一点点时间,这个时间我的单片机是在执行我的程序吗?如果正好执行到了ad那部分,把中断关了,那么下一帧就接受不到了啊?,,,我最后只用了一个串口。。我也不知道怎么解决
发表于 2019-8-29 17:55:29 | 显示全部楼层
/********************* UART3中断函数************************/
void UART3_int (void) interrupt UART3_VECTOR
{
        if(RI3)
        {
                CLR_RI3();
                RX3_Buffer[RX3_write] = S3BUF;
                RT3 = S3BUF;                         //读入缓冲区
                R3_flag=1;                           //有数据,建立标志
                if(++RX3_write >= RX3_Length)        RX3_write = 0;                 你这里啥意思,接收个数达到 RX3_Length 后  RX3_write=0那么下一个数据来了不就是覆盖吗,难道是这么简单的思路
        }

        if(TI3)
        {
                CLR_TI3();
                B_TX3_Busy = 0;
        }

}
 楼主| 发表于 2019-8-29 18:32:57 | 显示全部楼层
UVPOWER 发表于 2019-8-29 17:23
没有仔细看程序,如果模块的数据是一直发送的,可以考虑加个数据校验吧,校验不正确的数据就丢弃了 ...

模块数据一直发送的,我看看你说的校验问题
 楼主| 发表于 2019-8-29 18:34:25 | 显示全部楼层
1a2b3c 发表于 2019-8-29 17:32
没有看明白楼主,先不看楼主代码,看下楼主人吧,
肯定是设计思路问题,数据是一直有还是随机有,还是不知 ...

数据一直有的,模块只要上电就一直发送,我知道肯定是我思路问题,所以才来求助坛友了^_^
发表于 2019-8-29 18:47:03 来自手机 | 显示全部楼层
程序里有死等的都不是好程序
发表于 2019-8-29 18:52:26 | 显示全部楼层
newywx 发表于 2019-8-29 18:34
数据一直有的,模块只要上电就一直发送,我知道肯定是我思路问题,所以才来求助坛友了^_^ ...

后面我回复你的那个帖子都给你指出来了问题了没有看??

你明知道串口数据会源源不断的接收到,那么不做思路去限制么?或者即使想你目前这样允许覆盖接收,类似环形缓冲区,但是你就得自己手动去搜索完整的数据帧,在你的缓冲区找到你要的一帧信息。
 楼主| 发表于 2019-8-29 21:15:24 | 显示全部楼层
1a2b3c 发表于 2019-8-29 18:52
后面我回复你的那个帖子都给你指出来了问题了没有看??

你明知道串口数据会源源不断的接收到,那么不做 ...

正在看^_^
 楼主| 发表于 2019-8-29 21:15:50 | 显示全部楼层
my_avr 发表于 2019-8-29 18:47
程序里有死等的都不是好程序

对的,得改
发表于 2019-8-29 21:23:33 | 显示全部楼层
"中间差不多延时20秒的时间,"

,你牛,我写的程序,除非是几个空操作的时间,其他一概不等的
 楼主| 发表于 2019-8-29 21:27:20 | 显示全部楼层
向北 发表于 2019-8-29 17:47
我也好想遇到过,我用的串口1和串口2,串口2连了一块串口屏,不确定什么时候会用数据过来,关键是我还tm用 ...

我看论坛里有兄弟4个串口都用的,不知道他们是怎么防止类似问题的
 楼主| 发表于 2019-8-29 21:29:10 | 显示全部楼层
cu_ice 发表于 2019-8-29 21:23
"中间差不多延时20秒的时间,"

,你牛,我写的程序,除非是几个空操作的时 ...

那类似的长时间延时用定时器?
发表于 2019-8-29 21:32:08 | 显示全部楼层
定时器,查标志位
 楼主| 发表于 2019-8-29 21:48:54 | 显示全部楼层
cu_ice 发表于 2019-8-29 21:32
定时器,查标志位

看来也得这样
发表于 2019-8-29 22:12:38 | 显示全部楼层
程序结构建议:非阻塞状态机+队列,需要延时的如楼上:定时器+标志位
发表于 2019-8-29 22:35:02 | 显示全部楼层
太牛逼  离开20s  你的单片机是出门拉屎去了吗
发表于 2019-8-29 22:40:07 | 显示全部楼层
这个项目是自己做着玩 还是公司开发   怕是要重新推翻进行哦
发表于 2019-8-30 00:51:03 | 显示全部楼层
自己主动玩的,不会有水平太差的,逻辑上有冲突

学习其实是世界上最痛苦的事
发表于 2019-8-30 08:35:45 来自手机 | 显示全部楼层
把串口优先级开最高
 楼主| 发表于 2019-8-30 08:53:25 | 显示全部楼层
本帖最后由 newywx 于 2019-8-30 08:57 编辑

谢楼上几位,自己倒腾的
发表于 2019-8-30 08:58:31 | 显示全部楼层
首先,不要用while(1);做循环。然后串口报文必须加报头及校验和,这样才能数据纠错,传输数据才可靠。
 楼主| 发表于 2019-8-30 09:12:53 | 显示全部楼层
xuwuhan 发表于 2019-8-30 08:58
首先,不要用while(1);做循环。然后串口报文必须加报头及校验和,这样才能数据纠错,传输数据才可靠。 ...

谢兄弟回复,像22楼兄弟说的逻辑上有冲突,得好好理理
发表于 2019-8-30 09:23:43 | 显示全部楼层
newywx 发表于 2019-8-30 09:12
谢兄弟回复,像22楼兄弟说的逻辑上有冲突,得好好理理

好好再看看,建议找个串口通讯带校验的例程看看
发表于 2019-8-30 10:14:14 | 显示全部楼层
本帖最后由 xf331785508 于 2019-8-30 10:16 编辑
newywx 发表于 2019-8-29 21:29
那类似的长时间延时用定时器?


修级打怪方法 :
  1. void func1( void )
  2. {
  3.        lvDoWork1();
  4.       delay_ms(10);
  5.       lvDoWork2();

  6.      if(Condition == 1)
  7.      {
  8.             if(Condition2 == 2 )
  9.             {
  10.                   lvDoWork3();
  11.                   delay_ms(100);
  12.                   lvDoWork4();
  13.              }
  14.         lvDoWork5();
  15.      }
  16. }


  17. void func2( void )
  18. {
  19.      switch( gbStep )
  20.     {
  21.      case 0:
  22.             if( Condition == 1 )
  23.             {
  24.                    DelayCount = 0; //方法一
  25.                   TimerAsynEvent(eCounter, callBackFunc, para, Start); // 方法二
  26.                gbStep = 1;
  27.           }
  28.      break;
  29.      case 1:
  30.                if( DelayCounter >= 500)
  31.               {
  32.                          //do something;
  33.                }
  34.                else
  35.                {
  36.                        DelayCounter++;
  37.                }
  38.                if( AsynEventFlag ) //方法二
  39.               {
  40.                        //do something
  41.                 }
  42.          break;
  43.          default:
  44.          break;
  45.     }
  46. }
复制代码
 楼主| 发表于 2019-8-30 11:42:55 | 显示全部楼层

谢谢,好好看看!
发表于 2019-9-2 11:41:04 | 显示全部楼层
没空代码,只提建议:
1、不是紧急的事(特别是需要长时间处理的)不要放在中断中处理,中断占太长时间会影响别的中断响应。
2、可以将紧急的中断设置为高优先级(比如串口中断、定时器中断)。
3、串口要用中断来收发。串口接收是硬件自动的,本身不会丢失字节,只有程序没及时取走数据造成丢失。
     串口发送是硬件的,是主动的操作,发送方不应该有任何丢失的说法。
发表于 2019-9-2 14:59:46 | 显示全部楼层
太可怕了 用 delay 做20秒延时
延时这个最好改成定时器计数来做   
串口数据要有个头来判断开始    然后缓存置0 开始接收数据   
发表于 2019-9-2 19:01:45 | 显示全部楼层
Delay1ms(20000);                //等20秒
吓死人哦

没系统,就要学会用状态机
发表于 2019-9-2 20:23:07 来自手机 | 显示全部楼层
不说51,就算stm32我也不敢这么做。
发表于 2019-9-2 21:36:05 来自手机 | 显示全部楼层
不能这么死等的,或者周期check也行
友情提示:标题不合格、重复发帖,将会被封锁ID。详情请参考:论坛通告:封锁ID、获得注册邀请码、恢复被封ID、投诉必读
您需要登录后才可以回帖 登录 | 注册

本版积分规则

手机版|Archiver|阿莫电子论坛(原ourAVR/ourDEV) ( 公安备案:44190002001997(交互式论坛) 工信部备案:粤ICP备09047143号 )

GMT+8, 2019-9-15 18:51

阿莫电子论坛, 原"中国电子开发网"

© 2004-2018 www.amobbs.com, 原www.ourdev.cn, 原www.ouravr.com

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