搜索
bottom↓
回复: 28

请问UCOS II中串口收到的数据如何发送给主任务?

[复制链接]

出55入4汤圆

发表于 2022-8-12 10:38:22 | 显示全部楼层 |阅读模式
199汤圆
本帖最后由 ChenXC1121 于 2022-8-12 10:45 编辑

现有系统对串口数据的处理方式如下,具体代码在最后面。
1、系统串口每隔100ms左右会收到50个BYTE的数据,目前是定义了一个200BYTE的全局数组usart_rxbuf和一个全局变量作为接收标志位rec_flag,每次串口有收到数据就会往里面存,并且标志位置2,在主任务较忙的情况下,最多可以缓存四块数据(每块数据50个BYTE,BYTE0如果等于0xff,则代表这一块50BYTE数据已经被处理过了,可以往里面存入有效数据)。
2、主任务在while循环里面不断判断rec_flag标志位,如果等于2,则把usart_rxbuf数据中有效的数据赋给内部缓存,并且把对应数据块的BYTE0给置成0xff,这样串口中断里面判断到0xff,就可以继续存入。之后就是对收到的串口数据进行处理。
3、主任务比较空闲的时候,基本上都是串口存入数据块1,然后主任务处理数据块1,串口存入数据块1.....依此流程循环下去


系统在跑了几万个循环后会无缘无故卡死了,系统完全停止,串口调试信息也不输出,也不是进入数组溢出等HardFault_Handler错误,目前比较怀疑是usart_rxbuf全局数组在两个任务里面都有读写,会不会是usart_rxbuf数组在主任务里面正在被读取,这时串口中断来了,又进入串口中断去读写usart_rxbuf全局数组,就导致系统无故卡死了。哪位大哥有什么办法可以处理掉这种同时读写的情况的,这样轻微改动后解决这个问题最好。
如果上面这种方式铁定不行,请问大伙在UCOS II系统中都是怎么处理串口接收到的数据的,有例程最好,谢谢!




/********************串口中断处理函数*******************************/
volatile u8 usart_rxbuf[200];
volatile u8 rec_flag=0;
volatile u8 USART2_RX_BUF[100];                                 //接收缓冲

void USART2_IRQHandler(void)
{
        u8 res;
        u8 i=0,j=0;
        u8 temp=0;
        u16 crc_check=0;       
        OSIntEnter();   
        if(USART2->SR&(1<<5))//接收到数据
        {         
                res=USART2->DR;        
                if((res==0xAA)&&(start_rev==0))
                {
                        rev_num=0;
                        start_rev=1;
                }
                if(start_rev==1)
                {
                        USART2_RX_BUF[rev_num]=res;
                        rev_num++;
                        if(rev_num>63)
                        {
                                if(USART2_RX_BUF[1]==0x55)
                                {
                                        if(USART2_RX_BUF[3]==0x5C)        //采集模块数据上报
                                        {
                                                crc_check=cal_crc(&USART2_RX_BUF[0],63);
                                                temp=crc_check&0x00ff;
                                                if(temp==USART2_RX_BUF[63])        //必须校验和正确  才会执行写入动作
                                                {               
                                                        rec_flag=2;                 
                                                        UART2_SEND(0xAA);               
                                                        UART2_SEND(0x55);
                                                        UART2_SEND(0x02);
                                                        UART2_SEND(0x5D);                //数据上报接收应答 在串口中断里面发送串口数据会不会也会导致系统卡死?
                                                        UART2_SEND(0xFF);                               

                                                        for(j=0;j<4;j++)
                                                        {
                                                                temp=j*50;
                                                                if(usart_rxbuf[temp]==0xff)        //0xff代表本段没数据  可以存入
                                                                {
                                                                        for(i=5;i<45;i++)
                                                                                usart_rxbuf[4+i+temp]=USART2_RX_BUF;       

                                                                        usart_rxbuf[temp]=USART2_RX_BUF[52];                //点数
                                                                        usart_rxbuf[temp+1]=USART2_RX_BUF[53];
                                                                        usart_rxbuf[temp+2]=USART2_RX_BUF[45];                //周期
                                                                        usart_rxbuf[temp+3]=USART2_RX_BUF[46];
                                                                        usart_rxbuf[temp+4]=USART2_RX_BUF[47];       
                                                                        usart_rxbuf[temp+5]=USART2_RX_BUF[54];                //范围1~4       

                                                                        usart_rxbuf[temp+6]=USART2_RX_BUF[57];                //电池电量高位
                                                                        usart_rxbuf[temp+7]=USART2_RX_BUF[58];                //电池电量低位
                                                                        usart_rxbuf[temp+8]=USART2_RX_BUF[59];               
                                                                        break;
                                                                }
                                                        }
                                                }
                                                else
                                                {
                                                        errtime+=1;                                                                                                               
                                                }
                                        }                                       
                                        start_rev=0;
                                        rev_num=0;                                       
                                }                               
                                else
                                {
                                        start_rev=0;
                                        rev_num=0;
                                }
                        }
                }
        }                                 
        OSIntExit();  
}



/************主任务程序*****************/
while(1)
{
        if(rec_flag==2)
        {
                rec_flag=0;
               
                for(i=0;i<4;i++)
                {
                        temp=i*50;
                        if(usart_rxbuf[temp]!=0xff)        //代表该位置存在数据
                        {
                                //INTX_DISABLE();
                                for(j=0;j<50;j++)       
                                {
                                        //usart_rxbuffer数组是while循环内部定义的局部变量
                                        usart_rxbuffer[j]=usart_rxbuf[temp+j];                                       
                                }
                                usart_rxbuf[temp]=0xff;        //该条数据处理完成后第一个BYTE置FF  代表数据已处理过了
                                //INTX_ENABLE();

                                //后面是对收到的数据进行处理和显示

                        }
                }
        }
}

最佳答案

查看完整内容

是的,后面把发送部分放到中断外就没事了。

出0入0汤圆

发表于 2022-8-12 10:38:23 | 显示全部楼层
ChenXC1121 发表于 2022-8-12 14:14
多谢大神的回复,那您最后是把串口发送去掉就解决死机问题了吗?
(引用自15楼)

是的,后面把发送部分放到中断外就没事了。

出0入0汤圆

发表于 2022-8-12 11:18:16 | 显示全部楼层
1.建立串口环形接收区,建立串口信号量
2.串口接收中断只接收数据并放入环形接收区
3.开启串口空闲中断,在空闲中断中发送串口信号量
4.线程等待信号量后,从环形接收区读取数据并进行数据解析,然后执行相应的操作

出0入115汤圆

发表于 2022-8-12 12:00:53 | 显示全部楼层
看到楼主,在中断里面发送串口数据了,我这么多年的软件编程习惯一般不会这么干,串口接收中断,一般只负责存储数据,协议解析和协议应答一般在任务里完成的。

出0入115汤圆

发表于 2022-8-12 12:01:54 | 显示全部楼层
死机是什么表象,建议把串口解析协议的那个task的栈搞大点

出55入4汤圆

 楼主| 发表于 2022-8-12 12:29:34 | 显示全部楼层
三年模拟 发表于 2022-8-12 12:01
死机是什么表象,建议把串口解析协议的那个task的栈搞大点
(引用自4楼)



之前主任务的栈是1000,我现在已经改成2000了,还是会出现,估计不是这个原因,而且用仿真查看主任务的栈使用情况,大部分时候看到的都是只用了500左右

出0入0汤圆

发表于 2022-8-12 12:33:45 | 显示全部楼层
这么写程序 还有必要用操作系统吗。

出55入4汤圆

 楼主| 发表于 2022-8-12 12:33:56 | 显示全部楼层
niechao15 发表于 2022-8-12 11:18
1.建立串口环形接收区,建立串口信号量
2.串口接收中断只接收数据并放入环形接收区
3.开启串口空闲中断,在 ...
(引用自2楼)

感觉大神说的这种方式应该不错,请问有可以参考的例程吗?或者哪个例程比较接近的。

出55入4汤圆

 楼主| 发表于 2022-8-12 12:36:13 | 显示全部楼层
mPiDDR 发表于 2022-8-12 12:33
这么写程序 还有必要用操作系统吗。
(引用自6楼)


大神请帮忙指条明路,我主要是负责硬件开发的,软件方面研究的不是很深入 ,没办法,小公司就是一人顶两啊

出0入0汤圆

发表于 2022-8-12 13:04:54 | 显示全部楼层
告诉你一个简单的思路:200BYTE的全局数组usart_rxbuf,把这个分为两个100B的数组,轮流操作(乒乓算法),再试试

出0入0汤圆

发表于 2022-8-12 13:05:47 | 显示全部楼层
ChenXC1121 发表于 2022-8-12 12:36
大神请帮忙指条明路,我主要是负责硬件开发的,软件方面研究的不是很深入 ,没办法,小公司就是 ...
(引用自8楼)

建议先用裸机开发,别上操作系统。
按你的想法跑通程序就可以了。

操作系统像雙截棍一样,水平还不够的话,会容易玩残。

出55入4汤圆

 楼主| 发表于 2022-8-12 13:34:25 | 显示全部楼层
mPiDDR 发表于 2022-8-12 13:05
建议先用裸机开发,别上操作系统。
按你的想法跑通程序就可以了。

(引用自10楼)


主要我有一部分代码需要用到操作系统,不然系统触摸屏操作反应会很慢,没办法去掉,只能硬着头皮上啊,遇到什么问题就处理什么啊。

出105入79汤圆

发表于 2022-8-12 13:37:32 | 显示全部楼层
HardFault_Handler里面 看Call stack, 有机会看到是哪里引起

出55入4汤圆

 楼主| 发表于 2022-8-12 13:47:34 | 显示全部楼层
qwe2231695 发表于 2022-8-12 13:37
HardFault_Handler里面 看Call stack, 有机会看到是哪里引起
(引用自12楼)


死机时不是进入HardFault_Handler   而是串口打印啥也没了

出0入0汤圆

发表于 2022-8-12 14:09:47 | 显示全部楼层
不要在中断接收中发送数据,如果任务少的话,还没事,多了就会出现运行一段时间后死机的问题,我之前就遇到过这种问题。

出55入4汤圆

 楼主| 发表于 2022-8-12 14:14:21 | 显示全部楼层
52HLX 发表于 2022-8-12 14:09
不要在中断接收中发送数据,如果任务少的话,还没事,多了就会出现运行一段时间后死机的问题,我之前就遇到 ...
(引用自14楼)


多谢大神的回复,那您最后是把串口发送去掉就解决死机问题了吗?

出105入79汤圆

发表于 2022-8-12 14:51:25 | 显示全部楼层
只要能稳定复现,就可以一块一块代码进行排除

出95入8汤圆

发表于 2022-8-12 14:57:57 | 显示全部楼层
52HLX 发表于 2022-8-12 14:09
不要在中断接收中发送数据,如果任务少的话,还没事,多了就会出现运行一段时间后死机的问题,我之前就遇到 ...
(引用自14楼)

这个原因有解释么?

中断中为啥不能发?

数据长度一定,串口发送用轮询,  就是个时间确定的 任务,中断中不可以放?

出55入4汤圆

 楼主| 发表于 2022-8-12 14:58:09 | 显示全部楼层
52HLX 发表于 2022-8-12 14:18
是的,后面把发送部分放到中断外就没事了。
(引用自16楼)


那串口数据的获取您是用什么方式转给主任务的啊,也是用全局变量吗?这种方式会不会也会导致系统死机?

出55入4汤圆

 楼主| 发表于 2022-8-12 15:08:04 | 显示全部楼层
qwe2231695 发表于 2022-8-12 14:51
只要能稳定复现,就可以一块一块代码进行排除
(引用自17楼)


很难固定规律出现,有时系统跑的时间短,有时长,真头疼

出0入0汤圆

发表于 2022-8-13 10:53:18 | 显示全部楼层
zzsczz 发表于 2022-8-12 14:57
这个原因有解释么?

中断中为啥不能发?
(引用自18楼)

不是不可以发,尽量不要在中断中发一组串口数据。串口就作为接收数据用,一组数据接收完,就在主程序中解析数据然后再发送回复数据。我当时遇到的情况就是接收一组数据后,我在串口中断中回复数据,运行几个小时就会出现死机,后面把数据放到主循环中就没事了。串口中断就作为接收数据使用,养成一个好习惯。

出0入0汤圆

发表于 2022-8-13 10:58:36 | 显示全部楼层
ChenXC1121 发表于 2022-8-12 14:58
那串口数据的获取您是用什么方式转给主任务的啊,也是用全局变量吗?这种方式会不会也会导致系统死机? ...
(引用自19楼)

我当时用的是裸机,接收类似2楼说的环形接收区。 我觉得2楼说的很详细了。

出0入0汤圆

发表于 2022-8-13 11:30:43 | 显示全部楼层
连接调试器在线调试,出问题了就下暂停一下看看卡死在哪里了就好分析了,你这个逻辑看上去没有什么问题,可能是其他什么临界冲突导致的逻辑死锁,在线debug就知道了

出0入31汤圆

发表于 2022-8-13 11:32:02 | 显示全部楼层
还是先治标,忽然建议楼主把整个架构都改掉是不现实的。如果确定可以复现,挂仿真器看“死掉”到底是个怎么死法。

出95入8汤圆

发表于 2022-8-13 11:52:59 | 显示全部楼层
52HLX 发表于 2022-8-13 10:53
不是不可以发,尽量不要在中断中发一组串口数据。串口就作为接收数据用,一组数据接收完,就在主程序中解 ...
(引用自21楼)

查了一下, stm32所有串口 共用一个 中断 ,包括 发送和接收

出0入16汤圆

发表于 2022-8-13 15:32:06 | 显示全部楼层
本帖最后由 初音之恋 于 2022-8-13 15:43 编辑

前面的说得对,串口接受中断里就不要发数据,会卡中断,要发也是用串口发送中断发,   发送尽量在主程序里进行,通知方式有很多种,
1、中断接受完后设置全局变量通知主程序然后发送
2、中断接受完后产生信号量通知主程序然后发送
3、中断接受完后添加消息队列通知主程序然后发送
速度快且密集以及不需要即时响应的推荐消息队列,缓冲区想设多大就多大
for(i=0;i<len;i++)
{
      usart_data_transmit(MACDrv_UART,buf);
      while(usart_flag_get(MACDrv_UART,USART_FLAG_TBE)==RESET);
}
while(usart_flag_get(MACDrv_UART,USART_FLAG_TC)==RESET);//以前发送数据太快遇到过这一句卡死的,N条指令连续发送没延时

出55入4汤圆

 楼主| 发表于 2022-8-16 15:30:25 | 显示全部楼层
niechao15 发表于 2022-8-12 11:18
1.建立串口环形接收区,建立串口信号量
2.串口接收中断只接收数据并放入环形接收区
3.开启串口空闲中断,在 ...
(引用自2楼)


大神你好  请问串口环形接收区是定义成全局数组吗?

出0入30汤圆

发表于 2022-8-16 16:25:51 | 显示全部楼层
消息队列就是干这个的。用空闲中断或者接收超时中断,收完数据后,用UCOS的消息队列将数据发送出去。串口解析任务只管pend消息队列。最好 结合ucos的内存管理

出0入0汤圆

发表于 2022-8-16 16:29:11 | 显示全部楼层
ChenXC1121 发表于 2022-8-16 15:30
大神你好  请问串口环形接收区是定义成全局数组吗?
(引用自27楼)

类似于fifo
回帖提示: 反政府言论将被立即封锁ID 在按“提交”前,请自问一下:我这样表达会给举报吗,会给自己惹麻烦吗? 另外:尽量不要使用Mark、顶等没有意义的回复。不得大量使用大字体和彩色字。【本论坛不允许直接上传手机拍摄图片,浪费大家下载带宽和论坛服务器空间,请压缩后(图片小于1兆)才上传。压缩方法可以在微信里面发给自己(不要勾选“原图),然后下载,就能得到压缩后的图片】。另外,手机版只能上传图片,要上传附件需要切换到电脑版(不需要使用电脑,手机上切换到电脑版就行,页面底部)。
您需要登录后才可以回帖 登录 | 注册

本版积分规则

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

GMT+8, 2024-4-29 07:13

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

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