newywx 发表于 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 = 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 = 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 = 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 = 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 = 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 = 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=RT3;        //数据存入缓冲区
        CT3++;                  //指针加1指向一下存储单元
        if(CT3>=13)         //判断指针是否超范围
                {
                CT3=0;       

                        UART1_Send(RX3_Buffer);//转发到串口1查看是否正确
                        UART1_Send(RX3_Buffer);
                        UART1_Send(RX3_Buffer);
                        UART1_Send(RX3_Buffer);
                        UART1_Send(RX3_Buffer);
                        UART1_Send(RX3_Buffer);
                        UART1_Send(RX3_Buffer);
                        UART1_Send(RX3_Buffer);
                        UART1_Send(RX3_Buffer);
                        UART1_Send(RX3_Buffer);
                        UART1_Send(RX3_Buffer);
                        UART1_Send(RX3_Buffer);
                        UART1_Send(RX3_Buffer);
        }
}

ningming 发表于 2019-8-29 14:52:59

缓冲区太小, 循环覆盖了。

newywx 发表于 2019-8-29 15:18:58

模块每次发送数据是固定的,13个字节

UVPOWER 发表于 2019-8-29 17:23:58

没有仔细看程序,如果模块的数据是一直发送的,可以考虑加个数据校验吧,校验不正确的数据就丢弃了

1a2b3c 发表于 2019-8-29 17:32:19

没有看明白楼主,先不看楼主代码,看下楼主人吧,
肯定是设计思路问题,数据是一直有还是随机有,还是不知道啥时候有,你那20秒钟时间内要收多少串口数据{:lol:}

向北 发表于 2019-8-29 17:47:57

我也好想遇到过,我用的串口1和串口2,串口2连了一块串口屏,不确定什么时候会用数据过来,关键是我还tm用了一个ad,读取ad的时候时序不能被打断,所以只能关掉中断,那么如果我在读ad的时候来了串口数据,我还能收到吗?如果低优先级的串口正在接受数据这时候高级别串口中断来了,那么我低优先级的中断不会被打乱吗?串口中断执行后距离下一个字符过来还有一点点时间,这个时间我的单片机是在执行我的程序吗?如果正好执行到了ad那部分,把中断关了,那么下一帧就接受不到了啊?,,,我最后只用了一个串口。。我也不知道怎么解决

1a2b3c 发表于 2019-8-29 17:55:29

/********************* UART3中断函数************************/
void UART3_int (void) interrupt UART3_VECTOR
{
      if(RI3)
      {
                CLR_RI3();
                RX3_Buffer = S3BUF;
                RT3 = S3BUF;                         //读入缓冲区
                R3_flag=1;                           //有数据,建立标志
                if(++RX3_write >= RX3_Length)      RX3_write = 0;               你这里啥意思,接收个数达到 RX3_Length 后RX3_write=0那么下一个数据来了不就是覆盖吗,难道是这么简单的思路{:lol:}{:biggrin:}{:funk:}{:curse:}
      }

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

}

newywx 发表于 2019-8-29 18:32:57

UVPOWER 发表于 2019-8-29 17:23
没有仔细看程序,如果模块的数据是一直发送的,可以考虑加个数据校验吧,校验不正确的数据就丢弃了 ...

模块数据一直发送的,我看看你说的校验问题

newywx 发表于 2019-8-29 18:34:25

1a2b3c 发表于 2019-8-29 17:32
没有看明白楼主,先不看楼主代码,看下楼主人吧,
肯定是设计思路问题,数据是一直有还是随机有,还是不知 ...

数据一直有的,模块只要上电就一直发送,我知道肯定是我思路问题,所以才来求助坛友了^_^

my_avr 发表于 2019-8-29 18:47:03

程序里有死等的都不是好程序

1a2b3c 发表于 2019-8-29 18:52:26

newywx 发表于 2019-8-29 18:34
数据一直有的,模块只要上电就一直发送,我知道肯定是我思路问题,所以才来求助坛友了^_^ ...

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

你明知道串口数据会源源不断的接收到,那么不做思路去限制么?或者即使想你目前这样允许覆盖接收,类似环形缓冲区,但是你就得自己手动去搜索完整的数据帧,在你的缓冲区找到你要的一帧信息。

newywx 发表于 2019-8-29 21:15:24

1a2b3c 发表于 2019-8-29 18:52
后面我回复你的那个帖子都给你指出来了问题了没有看??

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

正在看^_^

newywx 发表于 2019-8-29 21:15:50

my_avr 发表于 2019-8-29 18:47
程序里有死等的都不是好程序

对的,得改

cu_ice 发表于 2019-8-29 21:23:33

"中间差不多延时20秒的时间,"

{:funk:}{:funk:}{:funk:}{:funk:},你牛,我写的程序,除非是几个空操作的时间,其他一概不等的

newywx 发表于 2019-8-29 21:27:20

向北 发表于 2019-8-29 17:47
我也好想遇到过,我用的串口1和串口2,串口2连了一块串口屏,不确定什么时候会用数据过来,关键是我还tm用 ...

我看论坛里有兄弟4个串口都用的,不知道他们是怎么防止类似问题的

newywx 发表于 2019-8-29 21:29:10

cu_ice 发表于 2019-8-29 21:23
"中间差不多延时20秒的时间,"

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

那类似的长时间延时用定时器?

cu_ice 发表于 2019-8-29 21:32:08

定时器,查标志位

newywx 发表于 2019-8-29 21:48:54

cu_ice 发表于 2019-8-29 21:32
定时器,查标志位

看来也得这样

yanyanyan168 发表于 2019-8-29 22:12:38

程序结构建议:非阻塞状态机+队列,需要延时的如楼上:定时器+标志位

huangqi412 发表于 2019-8-29 22:35:02

太牛逼离开20s你的单片机是出门拉屎去了吗

lb0857 发表于 2019-8-29 22:40:07

这个项目是自己做着玩 还是公司开发   怕是要重新推翻进行哦

RAMILE 发表于 2019-8-30 00:51:03

自己主动玩的,不会有水平太差的,逻辑上有冲突

学习其实是世界上最痛苦的事

1826772880 发表于 2019-8-30 08:35:45

把串口优先级开最高

newywx 发表于 2019-8-30 08:53:25

本帖最后由 newywx 于 2019-8-30 08:57 编辑

谢楼上几位,自己倒腾的

xuwuhan 发表于 2019-8-30 08:58:31

首先,不要用while(1);做循环。然后串口报文必须加报头及校验和,这样才能数据纠错,传输数据才可靠。

newywx 发表于 2019-8-30 09:12:53

xuwuhan 发表于 2019-8-30 08:58
首先,不要用while(1);做循环。然后串口报文必须加报头及校验和,这样才能数据纠错,传输数据才可靠。 ...

谢兄弟回复,像22楼兄弟说的逻辑上有冲突,得好好理理

xuwuhan 发表于 2019-8-30 09:23:43

newywx 发表于 2019-8-30 09:12
谢兄弟回复,像22楼兄弟说的逻辑上有冲突,得好好理理

好好再看看,建议找个串口通讯带校验的例程看看

xf331785508 发表于 2019-8-30 10:14:14

本帖最后由 xf331785508 于 2019-8-30 10:16 编辑

newywx 发表于 2019-8-29 21:29
那类似的长时间延时用定时器?

修级打怪方法 :
void func1( void )
{
       lvDoWork1();
      delay_ms(10);
      lvDoWork2();

   if(Condition == 1)
   {
            if(Condition2 == 2 )
            {
                  lvDoWork3();
                  delay_ms(100);
                  lvDoWork4();
             }
      lvDoWork5();
   }
}


void func2( void )
{
   switch( gbStep )
    {
   case 0:
            if( Condition == 1 )
            {
                   DelayCount = 0; //方法一
                  TimerAsynEvent(eCounter, callBackFunc, para, Start); // 方法二
               gbStep = 1;
          }
   break;
   case 1:
               if( DelayCounter >= 500)
            {
                         //do something;
               }
               else
               {
                     DelayCounter++;
               }
               if( AsynEventFlag ) //方法二
            {
                     //do something
                }
         break;
         default:
         break;
    }
}

newywx 发表于 2019-8-30 11:42:55

xf331785508 发表于 2019-8-30 10:14
修级打怪方法 :

谢谢,好好看看!

小李非刀 发表于 2019-9-2 11:41:04

没空代码,只提建议:
1、不是紧急的事(特别是需要长时间处理的)不要放在中断中处理,中断占太长时间会影响别的中断响应。
2、可以将紧急的中断设置为高优先级(比如串口中断、定时器中断)。
3、串口要用中断来收发。串口接收是硬件自动的,本身不会丢失字节,只有程序没及时取走数据造成丢失。
   串口发送是硬件的,是主动的操作,发送方不应该有任何丢失的说法。

ayumi8 发表于 2019-9-2 14:59:46

太可怕了 用 delay 做20秒延时
延时这个最好改成定时器计数来做   
串口数据要有个头来判断开始    然后缓存置0 开始接收数据   

first_blood 发表于 2019-9-2 19:01:45

Delay1ms(20000);                //等20秒
吓死人哦

没系统,就要学会用状态机

XA144F 发表于 2019-9-2 20:23:07

不说51,就算stm32我也不敢这么做。

zhaoyuanxian 发表于 2019-9-2 21:36:05

不能这么死等的,或者周期check也行

龍黍 发表于 2019-11-29 16:57:07

huangqi412 发表于 2019-8-29 22:35
太牛逼离开20s你的单片机是出门拉屎去了吗

程序里面好像是20ms,楼主写出20S了{:lol:}

龍黍 发表于 2019-11-29 16:58:20

看错了,还真有给20秒的延时{:sweat:}

losingrose 发表于 2019-11-29 18:38:05

能否正常收到就看dma是如何处理的了,dma满了就会覆盖写入。

dukelec 发表于 2019-11-29 19:04:21

向北 发表于 2019-8-29 17:47
我也好想遇到过,我用的串口1和串口2,串口2连了一块串口屏,不确定什么时候会用数据过来,关键是我还tm用 ...

如果 AD 真的不能被打斷,那就只能用 DMA 接收串口數據,或者用外部芯片接收,譬如 CDCTL-B1.
如果你的串口速率足夠低,AD 關中斷的時間小於 1 個 byte 的接收時間,用中斷接收也可以。

向北 发表于 2019-11-29 19:44:22

dukelec 发表于 2019-11-29 19:04
如果 AD 真的不能被打斷,那就只能用 DMA 接收串口數據,或者用外部芯片接收,譬如 CDCTL-B1.
如果你的串 ...

STC没有DMA。。。我也没找到您说的那个芯片。。。

dukelec 发表于 2019-11-29 20:33:27

向北 发表于 2019-11-29 19:44
STC没有DMA。。。我也没找到您说的那个芯片。。。

是的,STC 沒 DMA.

我簽名就有它資料。

不過一般情況,ADC 是可以中斷的吧。

wqy0410 发表于 2019-12-1 10:24:51

wxdn 发表于 2019-11-30 00:40
我的一个项目里是这么做的,由于收发都是定长的,就定义了两个uchar变量和一个接收标志位,其中一个变量是 ...

这个应该就是接收超时吧,接收不定时数据我就用这方式做的
页: [1]
查看完整版本: 请教串口接收数据被别的事件长时间打断后再接收数据错...