OpenEdv-开源电子网

 找回密码
 立即注册
正点原子全套STM32/Linux/FPGA开发资料,上千讲STM32视频教程免费下载...
查看: 4068|回复: 10

MODBUS通信中的多任务资源共享问题,大家有什么好的办法可以简便的解决

[复制链接]

19

主题

334

帖子

0

精华

金牌会员

Rank: 6Rank: 6

积分
1108
金钱
1108
注册时间
2018-11-6
在线时间
240 小时
发表于 2022-11-28 17:05:47 | 显示全部楼层 |阅读模式
1金钱
本帖最后由 霸王猫 于 2022-11-28 17:09 编辑



     问题:
           我要实现MODBUS通信(串口为USART3),我不满意我目前采用的资源共享方法,想请求大家帮我想个更简便的方法:

                  1、上位机(MODBUS主站)发出请求报文,USART3中断服务程序发送消息队列激活【MODBUS任务】,MODBUS任务将30001,30002,30003上传给上位机。
                  2、【任务A】通过消息队列将变量XA (对应于30001)传输给【MODBUS任务】。
                  3、【任务B】通过消息队列将变量XB (对应于30002)传输给【MODBUS任务】。
                  4、【任务C】通过消息队列将变量XC (对应于30003)传输给【MODBUS任务】。

                  5、消息队列格式如下:
                          msg[0] = 1,代表 【USART3中断服务程序】发送的消息队列
                          msg[0] = 2,代表 【任务A】发送的消息队列
                          msg[0] = 3,代表 【任务B】发送的消息队列   
                          msg[0] = 4,代表 【任务C】发送的消息队列   
                          msg[1] = XA或XB或XC

                  6、为了防止正在进行MODBUS报文解析时,再次触发USART3中断,需要将USART3缓冲区进行资源共享(用互斥信号量保护)

                  7、下面是将XA (对应于30001),XB (对应于30002),XC (对应于30003) 添加互斥信号量保护原子完整性

  1. int16_t USART3_GetInputRegisterVal(uint16_t tempAddr)
  2. {
  3.         int16_t tempData = 0x00;

  4.         
  5.         switch (tempAddr + 1 )
  6.         {
  7.                 case 1:        
  8.                                //任务A互斥信号量
  9.                                xSemaphoreTake(<font color="#ff00ff">xMutex1</font>, portMAX_DELAY);                                                
  10.                         tempData = XA ;                                                //30001
  11.                         xSemaphoreGive(<font color="#ff00ff">xMutex1</font>);
  12.                         break;                                
  13.                 case 2:
  14.                                //任务B互斥信号量
  15.                                xSemaphoreTake(xMutex2, portMAX_DELAY);                                                
  16.                         tempData = XB ;                                                //30002
  17.                         xSemaphoreGive(xMutex2);
  18.                         break;               
  19.                 case 3:                                                
  20.                                //任务C互斥信号量
  21.                                xSemaphoreTake(xMutex3, portMAX_DELAY);                                                
  22.                         tempData = XC ;                                                //30003
  23.                         xSemaphoreGive(xMutex3);
  24.                         break;
  25.         }
  26. }
复制代码


                       8、下面是USART3中断服务程序,将USART3通信缓冲区进行互斥信号量保护,防止正在进行MODBUS报文解析时,再次触发USART3中断覆盖正在进行的报文分析工作,确保数据完整性。
  1. void USART3_IRQHandler(void)
  2. {
  3.         uint16_t ch;
  4.         uint8_t Buffer[10];        
  5.         BaseType_t xHigherPriorityTaskWoken = pdFALSE;
  6.         
  7.         
  8.         if (USART_GetITStatus(USART3,USART_IT_IDLE) != RESET)
  9.         {               
  10.                         
  11.                       //使用互斥信号量,保护通信缓冲区(MB_USART3.mscomm_buffer)数据的完整性
  12.                        xSemaphoreTakeFromISR(xMutex4, portMAX_DELAY);   
  13.                        memcpy(MB_USART3.mscomm_buffer , USART3_DMA_RX_Buffer , 255);
  14.                   xSemaphoreGiveFromISR(xMutex4, portMAX_DELAY);   

  15.                      // 发送消息队列
  16.                 Buffer[0] = 1;
  17.                 xQueueSendToBackFromISR(xQueueWIFI , Buffer , &xHigherPriorityTaskWoken);
  18.                 portYIELD_FROM_ISR(xHigherPriorityTaskWoken);   
  19.         }
  20. }
复制代码


                 89、下面是MODBUS任务,将USART3通信缓冲区进行互斥信号量保护,防止正在进行MODBUS报文解析时,再次触发USART3中断覆盖正在进行的报文分析工作,确保数据完整性。最后解析完报文后,将30001,30002,30003上传给上位机(MODBUS主站)
  1. static void vTask_Modbus(void *pvParameters)
  2. {
  3.         BaseType_t xStatus;        
  4.         const TickType_t xTicksToWait = pdMS_TO_TICKS( 100UL );
  5.         uint8_t Buffer[10];
  6.         

  7.         USART3_Configuration();
  8.         USART3_DMA_Tx_Configuration();
  9.         USART3_DMA_Rx_Configuration();        
  10.     while(1)
  11.     {               
  12.         xStatus = xQueueReceive( xQueueWIFI, &Buffer, xTicksToWait );
  13.         if ( xStatus == pdPASS )
  14.         {                        
  15.                 switch (Buffer[0])
  16.                 {
  17.                         case 1:
  18.                                 //使用互斥信号量,保护通信缓冲区(MB_USART3.mscomm_buffer)数据的完整性
  19.                                 xSemaphoreTake(xMutex4, portMAX_DELAY);
  20.                                 从缓冲区MB_USART3.mscomm_buffer中解析MODBUS报文,将30001,30002,30003上传给上位机。                                      
  21.                                 xSemaphoreGive(xMutex4);
  22.                                 break;
  23.                         case 2:
  24.                                         //任务A互斥信号量
  25.                                        xSemaphoreTake(xMutex1, portMAX_DELAY);
  26.                                          //从消息队列中读取任务A的变量XA                                                
  27.                                  XA  = buffer[1];                                                //30001
  28.                                 xSemaphoreGive(xMutex1);
  29.                                         break;

  30.                         case 2:
  31.                                         //任务B互斥信号量
  32.                                        xSemaphoreTake(xMutex2, portMAX_DELAY);
  33.                                          //从消息队列中读取任务B的变量XB                                                
  34.                                  XB  = buffer[1];                                                //30002
  35.                                 xSemaphoreGive(xMutex2);
  36.                                         break;

  37.                         case 3:
  38.                                         //任务C互斥信号量
  39.                                        xSemaphoreTake(xMutex3, portMAX_DELAY);
  40.                                          //从消息队列中读取任务C的变量XC                                                
  41.                                  XC  = buffer[1];                                                //30003
  42.                                 xSemaphoreGive(xMutex3);
  43.                                         break;

  44.                 }//switch (Buffer[0])        
  45.         }//if( xStatus == pdPASS )
  46.     }//while(1)
  47. }
复制代码





正点原子逻辑分析仪DL16劲爆上市
回复

使用道具 举报

19

主题

334

帖子

0

精华

金牌会员

Rank: 6Rank: 6

积分
1108
金钱
1108
注册时间
2018-11-6
在线时间
240 小时
 楼主| 发表于 2022-11-29 09:04:12 | 显示全部楼层
本帖最后由 霸王猫 于 2022-11-29 09:14 编辑


   一、串口中断服务程序的通信缓冲区需要用互斥信号量保护(防止正在处理通信缓冲区时,中断再次触发)
   二、很多任务的零散数据汇总到一起,然后统一上传给上位机(MODBUS主站),也需要用互斥信号量保护。
               MODBUS协议读取3X寄存器,最大可以读取125个寄存器,如果零散数据太多,每个零散数据都用互斥信号量保护,就太麻烦啦!
        下面是将零散数据(3X寄存器类型数据)汇总上传到上位机(MODBUS主站)的代码示意:
               
                  每个零散数据都用互斥信号量保护,太麻烦啦!

  1. int16_t USART3_GetInputRegisterVal(uint16_t tempAddr)
  2. {
  3.         int16_t tempData = 0x00;

  4.         
  5.         switch (tempAddr + 1 )
  6.         {
  7.                 case 1:        
  8.                                //任务A互斥信号量
  9.                                xSemaphoreTake(xMutex1, portMAX_DELAY);                                                
  10.                                 tempData = XA ;                                                        //30001
  11.                                 xSemaphoreGive(xMutex1);
  12.                                 break;                                
  13.                 case 2:
  14.                                //任务B互斥信号量
  15.                                xSemaphoreTake(xMutex2, portMAX_DELAY);                                                
  16.                                 tempData = XB ;                                                        //30002
  17.                                 xSemaphoreGive(xMutex2);
  18.                                 break;               
  19.                 case 3:                                                
  20.                                //任务C互斥信号量
  21.                                xSemaphoreTake(xMutex3, portMAX_DELAY);                                                
  22.                                 tempData = XC ;                                                        //30003
  23.                                 xSemaphoreGive(xMutex3);
  24.                                 break;
  25.                 ...
  26.                 case 125:
  27.                                //任务A互斥信号量
  28.                                xSemaphoreTake(xMutex1, portMAX_DELAY);                                                
  29.                                 tempData = XA_125 ;                                                //30125
  30.                                 xSemaphoreGive(xMutex1);
  31.                                 break;
  32.         }
  33. }
复制代码


    有什么好的方法吗?



回复

使用道具 举报

19

主题

334

帖子

0

精华

金牌会员

Rank: 6Rank: 6

积分
1108
金钱
1108
注册时间
2018-11-6
在线时间
240 小时
 楼主| 发表于 2022-11-29 10:59:01 | 显示全部楼层
本帖最后由 霸王猫 于 2022-11-29 11:26 编辑

专门设计一个任务Z

    1、其它【所有任务】将这些零散的数据【通过消息队列】发送给任务Z
    2、任务Z将这些零散的数据组织好后,再【通过消息队列】发送给【MODBUS主站】 任务

    不知道这种方案是否稍微好一点
回复

使用道具 举报

51

主题

2165

帖子

2

精华

论坛元老

Rank: 8Rank: 8

积分
10652
金钱
10652
注册时间
2017-4-14
在线时间
2780 小时
发表于 2022-11-30 12:54:20 | 显示全部楼层
感觉都没必要  你的单片机是从机 应该有个modbus的地址全局变量映射数组,每次直接数组里面取不就行了
回复

使用道具 举报

19

主题

334

帖子

0

精华

金牌会员

Rank: 6Rank: 6

积分
1108
金钱
1108
注册时间
2018-11-6
在线时间
240 小时
 楼主| 发表于 2022-11-30 13:42:06 | 显示全部楼层
nashui_sx 发表于 2022-11-30 12:54
感觉都没必要  你的单片机是从机 应该有个modbus的地址全局变量映射数组,每次直接数组里面取不就行了



    各个任务往数组里装的时候,也需要互斥信号时吧!

            任务A往 数组[0]里装 30001的数据需要互斥信号保护 ,  另外一个任务(MODBUS任务)读取的时候为了保证数据的完整性,也需要使用互斥信号保护,防止正在读取的时候,突然A又往数组【0】里装呀。
            任务B往 数组[1]里装 30002的数据需要互斥信号保护 ,  另外一个任务(MODBUS任务)读取的时候为了保证数据的完整性,也需要使用互斥信号保护,防止正在读取的时候,突然B又往数组【1】里装呀。


回复

使用道具 举报

51

主题

2165

帖子

2

精华

论坛元老

Rank: 8Rank: 8

积分
10652
金钱
10652
注册时间
2017-4-14
在线时间
2780 小时
发表于 2022-11-30 15:03:59 | 显示全部楼层
本帖最后由 nashui_sx 于 2022-11-30 15:17 编辑
霸王猫 发表于 2022-11-30 13:42
各个任务往数组里装的时候,也需要互斥信号时吧!

            任务A往 数组[0]里装 30001的 ...

你这单核,就读写一个变量 的时间短的不能在短了 一个任务读的时候就没有任务能去写   

假如你串口中断的数据只是接受,处理在任务里面,能破坏数据的串口打断都没有  啥都不用弄,顶多mobus映射的数组volatile修饰下   
再说你mobus组包过程中肯定也是数据拷贝+校验    假如数据拷贝过来了 校验算的时候打断了   别的任务写数组给你组包的数据也没影响呀

感觉你这个场景的数据共享这样整就失去跑系统的意义了 简单东西复杂化了,你不用系统你说你一个数组读写你加不加屏蔽中断保护也是不加的嘛
人家数据共享考虑读写保护都是数组比较大 拷贝时间长,可能拷贝倍打断 另一个任务去写了 要么互斥下  要么临界段保护下不让任务切换   你这就装一个数据我感觉毫无意义
回复

使用道具 举报

51

主题

2165

帖子

2

精华

论坛元老

Rank: 8Rank: 8

积分
10652
金钱
10652
注册时间
2017-4-14
在线时间
2780 小时
发表于 2022-11-30 15:26:39 | 显示全部楼层
霸王猫 发表于 2022-11-29 09:04
一、串口中断服务程序的通信缓冲区需要用互斥信号量保护(防止正在处理通信缓冲区时,中断再次触发)
...

对于  一、串口中断服务程序的通信缓冲区需要用互斥信号量保护(防止正在处理通信缓冲区时,中断再次触发)
两种情况,缓冲区不做队列  那你肯定是数据没处理完 接收标志位还没置位 数据之人舍弃了呀  modbus 从机不回数据 会连续再发的
极限处理的超级久 三帧都丢了   这样久需要定长上队列 或者不定长上message buffer了  存起来慢慢处理 也不需要互斥的
回复

使用道具 举报

19

主题

334

帖子

0

精华

金牌会员

Rank: 6Rank: 6

积分
1108
金钱
1108
注册时间
2018-11-6
在线时间
240 小时
 楼主| 发表于 2022-11-30 15:31:13 | 显示全部楼层
本帖最后由 霸王猫 于 2022-11-30 17:12 编辑
nashui_sx 发表于 2022-11-30 15:03
你这单核,就读写一个变量 的时间短的不能在短了 一个任务读的时候就没有任务能去写   

假如你串口中 ...

假如你串口中断的数据只是接受,处理在任务里面,能破坏数据的串口打断都没有  啥都不用弄,顶多mobus映射的数组volatile修饰下   
再说你mobus组包过程中肯定也是数据拷贝+校验    假如数据拷贝过来了 校验算的时候打断了   别的任务写数组给你组包的数据也没影响呀




          MODBUS任务正在读数组的时候,突然某个中断来了,引起任务切换,而那些任务如果比MODBUS任务的优先级高,就有可能破坏数组的完整性。
回复

使用道具 举报

51

主题

2165

帖子

2

精华

论坛元老

Rank: 8Rank: 8

积分
10652
金钱
10652
注册时间
2017-4-14
在线时间
2780 小时
发表于 2022-11-30 15:35:34 | 显示全部楼层
霸王猫 发表于 2022-11-30 15:31
假如你串口中断的数据只是接受,处理在任务里面,能破坏数据的串口打断都没有  啥都不用弄,顶多mobus映 ...

你一般也是一个个的读没啥问题的  不放心就像我前面说的 组包读取数据的时候临界段保护下就行了

要是按照你这个理论  全部其他全局变量难道都要保护吗  
回复

使用道具 举报

19

主题

334

帖子

0

精华

金牌会员

Rank: 6Rank: 6

积分
1108
金钱
1108
注册时间
2018-11-6
在线时间
240 小时
 楼主| 发表于 2022-12-1 13:02:37 | 显示全部楼层
nashui_sx 发表于 2022-11-30 15:35
你一般也是一个个的读没啥问题的  不放心就像我前面说的 组包读取数据的时候临界段保护下就行了

要是 ...



   网上所有资料和文章都说,全局变量必须要用关中断或互斥信号量保护。

     但是没有告诉当全局变量比较多时,例如:MODBUS协议传输3X寄存器(最多可以传输125个),这些变量来自不同的任务,如何用简便的方法保护这些变量。

          我能想到的方案是

               第一步:
                    1、任务A给  【中间任务】发送消息队列,传输 30001
                    2、任务B给  【中间任务】发送消息队列,传输 30002
                    3、任务C给  【中间任务】发送消息队列,传输 30003
               第二步:
                     4、【中间任务】给【MODBUS任务】发送消息队列,传输 30001,30002,30003
                              MODBUS任务将数据读出来,上传给上位机。

         这样就不会出现数据完整性问题啦!


回复

使用道具 举报

51

主题

2165

帖子

2

精华

论坛元老

Rank: 8Rank: 8

积分
10652
金钱
10652
注册时间
2017-4-14
在线时间
2780 小时
发表于 2022-12-1 14:30:18 | 显示全部楼层
霸王猫 发表于 2022-12-1 13:02
网上所有资料和文章都说,全局变量必须要用关中断或互斥信号量保护。

     但是没有告诉当全局 ...

就是上位机连续获取3区的125个数据,顶多组包的时候临界段代码保护一下就行了  真没必须要你那样,你中断内一般都不处理数据,125个数据的变化肯定都在任务里,临界段代码保护足够了

你的方案  125个变量 你要弄125个队列 没意义  不觉得麻烦你可以那样, 不过你的方案中间任务可以不要 直接给MODBUS任务发就行了,MODBUS任务组包时候都从队列获取就好了

等别得大神给你提供指导性意见吧
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则



关闭

原子哥极力推荐上一条 /2 下一条

正点原子公众号

QQ|手机版|OpenEdv-开源电子网 ( 粤ICP备12000418号-1 )

GMT+8, 2024-11-22 17:07

Powered by OpenEdv-开源电子网

© 2001-2030 OpenEdv-开源电子网

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