OpenEdv-开源电子网

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

一问一答的方式进行通信,如何设计任务呢?

[复制链接]

19

主题

334

帖子

0

精华

金牌会员

Rank: 6Rank: 6

积分
1108
金钱
1108
注册时间
2018-11-6
在线时间
240 小时
发表于 2019-12-27 15:29:00 | 显示全部楼层 |阅读模式
1金钱

   【主设备】通过 USART1串口连接1个小设备。
       1、【主设备】大概每隔1秒钟(时钟不需要精准,大概即可)给小设备发送请求报文。
       2、小设备收到USART1串口的请求报文后,给 【主设备】返回应答报文。

     请问到底采用哪种方式设计任务好呢?

                 一、方式1
                       1、【主设备】设计一个发送任务,延时1秒。
                       2、【主设备】设计一个等待信号量的接收任务
                                 在【主设备】的USART1串口接收完数据后,释放信号量同步接收任务。
                 二、方式2
                         【主设备】设计一个任务,发送和同步都在这个任务中。
                           task(void)
                           {
                                  串口初始化;
                                 while (1)
                                 {
                                       【主设备】发送请求报文;
                                         等待信号量(最大等待时间不超过1秒);
                                         vTaskDelay 延时1秒;
                                  }
                             }  


                    三、方式3,其它更好的设计任务的方法





最佳答案

查看完整内容[请看2#楼]

当然是同一个任务里面,这样才能做好数据流控。
正点原子逻辑分析仪DL16劲爆上市
回复

使用道具 举报

37

主题

595

帖子

0

精华

金牌会员

Rank: 6Rank: 6

积分
1572
金钱
1572
注册时间
2017-7-17
在线时间
308 小时
发表于 2019-12-27 15:29:01 | 显示全部楼层
本帖最后由 candylife9 于 2019-12-30 09:38 编辑
霸王猫 发表于 2019-12-30 09:22
中断接收数据,然后发送信号量同步接收任务我明白。
   但是我搞不清楚,定时发送请求报文是否要求独立 ...

当然是同一个任务里面,这样才能做好数据流控。
回复

使用道具 举报

530

主题

11万

帖子

34

精华

管理员

Rank: 12Rank: 12Rank: 12

积分
165309
金钱
165309
注册时间
2010-12-1
在线时间
2108 小时
发表于 2019-12-29 02:02:50 | 显示全部楼层
这种一般用中断接收数据,然后接收完再发送信号量或者设置标志,让其他任务去处理
回复

使用道具 举报

19

主题

334

帖子

0

精华

金牌会员

Rank: 6Rank: 6

积分
1108
金钱
1108
注册时间
2018-11-6
在线时间
240 小时
 楼主| 发表于 2019-12-30 08:49:26 | 显示全部楼层
正点原子 发表于 2019-12-29 02:02
这种一般用中断接收数据,然后接收完再发送信号量或者设置标志,让其他任务去处理

接收用信号量,那定时发送报文的代码是否需要独立建一个任务呢?
回复

使用道具 举报

19

主题

334

帖子

0

精华

金牌会员

Rank: 6Rank: 6

积分
1108
金钱
1108
注册时间
2018-11-6
在线时间
240 小时
 楼主| 发表于 2019-12-30 09:22:55 | 显示全部楼层
正点原子 发表于 2019-12-29 02:02
这种一般用中断接收数据,然后接收完再发送信号量或者设置标志,让其他任务去处理

中断接收数据,然后发送信号量同步接收任务我明白。
   但是我搞不清楚,定时发送请求报文是否要求独立设计一个周期性任务?
还是发送报文的任务和接收信号量的任务合并为一个任务好?
回复

使用道具 举报

19

主题

334

帖子

0

精华

金牌会员

Rank: 6Rank: 6

积分
1108
金钱
1108
注册时间
2018-11-6
在线时间
240 小时
 楼主| 发表于 2019-12-30 10:10:33 | 显示全部楼层
candylife9 发表于 2019-12-30 09:37
当然是同一个任务里面,这样才能做好数据流控。


                         【主设备】设计一个任务,发送和同步都在这个任务中。

因为要兼顾定时发送请求报文和行为同步中断服务程序发送的信号量,因此
在本任务中读取信号量必须设计成有时间限制的等待信号量,
才能满足:兼顾定时发送请求报文这个要求。

    以下我的代码对吗?


                           task(void)
                           {
                                  串口初始化;
                                 while (1)
                                 {
                                       【主设备】发送请求报文;
                                         等待信号量(最大等待时间不超过1秒);
                                         vTaskDelay 延时1秒;
                                  }
                             }  
回复

使用道具 举报

37

主题

595

帖子

0

精华

金牌会员

Rank: 6Rank: 6

积分
1572
金钱
1572
注册时间
2017-7-17
在线时间
308 小时
发表于 2019-12-30 10:28:21 | 显示全部楼层
本帖最后由 candylife9 于 2019-12-30 10:29 编辑
霸王猫 发表于 2019-12-30 10:10
【主设备】设计一个任务,发送和同步都在这个任务中。

因为要兼顾定时发 ...

我从来不用信号量这些东西,特别是在中断里面。不过你这个也差不多,只是需要更完善一些,比如超时检测,失败重发等机制的健全。
回复

使用道具 举报

19

主题

334

帖子

0

精华

金牌会员

Rank: 6Rank: 6

积分
1108
金钱
1108
注册时间
2018-11-6
在线时间
240 小时
 楼主| 发表于 2020-1-21 10:55:35 | 显示全部楼层
我已经解决啦,并在STM32F103VC单片机下跑成功啦!

     具体如下:
          6个任务
          1、任务1控制闪烁3个指示灯
          2、任务2控制闪烁2个指示灯
          3、任务3  采集2路DI,然后发送消息给MODBUS任务
          4、任务4  采集2路DI,然后发送消息给MODBUS任务
          5、任务5  每隔1秒读取一次SD2405时钟芯片的时间,然后发送消息给MODBUS任务
          6、任务6  MODBUS任务,收到上位机的请求命令后,将任务3的2路DI,任务4的2路DI,任务5的SD2405时钟芯片的时间发送给上位机。


     消息队列规划:
          MSG[0] = 1 --->  表示接收到上位机的请求命令
          MSG[0] = 2 ---> 表示接收到任务5的的SD2405时钟芯片的时间
              MSG[1] = 年
              MSG[2] = 月

              MSG[3] = 日

              MSG[4] = 时

              MSG[5] = 分

              MSG[6] = 秒

              MSG[7] = 星期

        MSG[0] = 3 --->表示接收到任务3  采集2路DI
              MSG[1] = 第1路DI
              MSG[2] = 第2路DI
        MSG[0] = 4 --->表示接收到任务4  采集2路DI
              MSG[1] = 第1路DI
              MSG[2] = 第2路DI



   
  1. /************************  Filename: platform.h  *****************************/
  2. /* ========================================================================= */
  3. /*                                                                           */
  4. /* 0000  000   000  00000 0  000  0   0 0 0000                               */
  5. /* 0   0 0  0 0   0 0     0 0   0 0   0 0 0   0                              */
  6. /* 0   0 0  0 0   0 0     0 0     0   0 0 0   0      Einsteinstra遝 6        */
  7. /* 0000  000  0   0 000   0 0     00000 0 0000       91074 Herzogenaurach    */
  8. /* 0     00   0   0 0     0 0     0   0 0 0                                  */
  9. /* 0     0 0  0   0 0     0 0   0 0   0 0 0          Tel: ++49-9132-744-200  */
  10. /* 0     0  0  000  0     0  000  0   0 0 0    GmbH  Fax: ++49-9132-744-204  */
  11. /*                                                                           */
  12. /* ========================================================================= */
  13. /*                                                                           */
  14. /* Description: User defines ( microcontroller, data types, ... ).           */
  15. /*                                                                           */
  16. /* ------------------------------------------------------------------------- */
  17. /*                                                                           */
  18. /* Technical support:       P. Fredehorst                                    */
  19. /*                          Tel. : ++49-9132/744-214                         */
  20. /*                          Fax. :              -204                         */
  21. /*                          eMail: pfredehorst@profichip.com                 */
  22. /*                                             support@profichip.com                 */
  23. /*                                                                           */
  24. /*****************************************************************************/


  25. /*****************************************************************************/
  26. /* contents:

  27.     - header include hierarchy for system environment

  28. */
  29. /*****************************************************************************/
  30. /* reinclude protection */


  31. #ifndef PLATFORM_H
  32. #define PLATFORM_H






  33. /*****************************************************************************/
  34. /* reinclude-protection */


  35. #include "stm32f10x.h"
  36. #include <string.h>
  37. #include <stdlib.h>


  38. /*---------------------------------------------------------------------------*/
  39. /*---------------------------------------------------------------------------*/
  40. /* MACRO                                                                                   */
  41. /*---------------------------------------------------------------------------*/
  42. #define UBYTE                                   uint8_t   
  43. #define UWORD                                   uint16_t
  44. #define BOOL                                    uint8_t
  45. #define ULONG                                   uint32_t


  46. /*---------------------------------------------------------------------------*/
  47. /*---------------------------------------------------------------------------*/
  48. /* MACRO                                                                                   */
  49. /*---------------------------------------------------------------------------*/
  50. #ifndef        TRUE
  51. #define TRUE                                    1
  52. #endif



  53. #ifndef        FALSE
  54. #define FALSE                                   !(TRUE)
  55. #endif


  56. #define MSCOMM_BUFFER_LENGTH                        (255)



  57. #define USART1_DMA_RX_BUFFER_MAX_LENGTH                (MSCOMM_BUFFER_LENGTH)
  58. #define USART1_DMA_TX_BUFFER_MAX_LENGTH                (MSCOMM_BUFFER_LENGTH)

  59. #define USART2_DMA_RX_BUFFER_MAX_LENGTH                (MSCOMM_BUFFER_LENGTH)
  60. #define USART2_DMA_TX_BUFFER_MAX_LENGTH                (MSCOMM_BUFFER_LENGTH)

  61. #define USART3_DMA_RX_BUFFER_MAX_LENGTH                (MSCOMM_BUFFER_LENGTH)
  62. #define USART3_DMA_TX_BUFFER_MAX_LENGTH                (MSCOMM_BUFFER_LENGTH)

  63. #define UART4_DMA_RX_BUFFER_MAX_LENGTH                (MSCOMM_BUFFER_LENGTH)
  64. #define UART4_DMA_TX_BUFFER_MAX_LENGTH                (MSCOMM_BUFFER_LENGTH)

  65. #define UART5_DMA_RX_BUFFER_MAX_LENGTH                (MSCOMM_BUFFER_LENGTH)
  66. #define UART5_DMA_TX_BUFFER_MAX_LENGTH                (MSCOMM_BUFFER_LENGTH)

  67. #define USART6_DMA_RX_BUFFER_MAX_LENGTH                (MSCOMM_BUFFER_LENGTH)
  68. #define USART6_DMA_TX_BUFFER_MAX_LENGTH                (MSCOMM_BUFFER_LENGTH)





  69. //////////////////////////////////////////////////////////////////////
  70. //LED
  71. //////////////////////////////////////////////////////////////////////
  72. #define RCC_LED1_PORT                                        RCC_APB2Periph_GPIOE
  73. #define GPIO_LED1_PORT                                        GPIOE
  74. #define GPIO_LED1_Pin                                        GPIO_Pin_6
  75. #define GPIO_LED1_ON()                                        (GPIO_ResetBits(GPIO_LED1_PORT , GPIO_LED1_Pin))       
  76. #define GPIO_LED1_OFF()                                        (GPIO_SetBits(GPIO_LED1_PORT , GPIO_LED1_Pin))       

  77. #define RCC_LED2_PORT                                        RCC_APB2Periph_GPIOE
  78. #define GPIO_LED2_PORT                                        GPIOE
  79. #define GPIO_LED2_Pin                                        GPIO_Pin_5
  80. #define GPIO_LED2_ON()                                        (GPIO_ResetBits(GPIO_LED2_PORT , GPIO_LED2_Pin))
  81. #define GPIO_LED2_OFF()                                        (GPIO_SetBits(GPIO_LED2_PORT , GPIO_LED2_Pin))

  82. #define RCC_LED3_PORT                                        RCC_APB2Periph_GPIOE
  83. #define GPIO_LED3_PORT                                        GPIOE
  84. #define GPIO_LED3_Pin                                        GPIO_Pin_4
  85. #define GPIO_LED3_ON()                                        (GPIO_ResetBits(GPIO_LED3_PORT , GPIO_LED3_Pin))
  86. #define GPIO_LED3_OFF()                                        (GPIO_SetBits(GPIO_LED3_PORT , GPIO_LED3_Pin))

  87. #define RCC_LED4_PORT                                        RCC_APB2Periph_GPIOE
  88. #define GPIO_LED4_PORT                                        GPIOE
  89. #define GPIO_LED4_Pin                                        GPIO_Pin_3
  90. #define GPIO_LED4_ON()                                        (GPIO_ResetBits(GPIO_LED4_PORT , GPIO_LED4_Pin))
  91. #define GPIO_LED4_OFF()                                        (GPIO_SetBits(GPIO_LED4_PORT , GPIO_LED4_Pin))

  92. #define RCC_LED5_PORT                                        RCC_APB2Periph_GPIOE
  93. #define GPIO_LED5_PORT                                        GPIOE
  94. #define GPIO_LED5_Pin                                        GPIO_Pin_2
  95. #define GPIO_LED5_ON()                                        (GPIO_ResetBits(GPIO_LED5_PORT , GPIO_LED5_Pin))
  96. #define GPIO_LED5_OFF()                                        (GPIO_SetBits(GPIO_LED5_PORT , GPIO_LED5_Pin))





  97. //////////////////////////////////////////////////////////////////////
  98. //DI
  99. //////////////////////////////////////////////////////////////////////
  100. //DI1----采集到高电平,行程开关状态=1;采集到低电平,行程开关状态=0。
  101. #define  RCC_DI1_1_PORT                                        RCC_APB2Periph_GPIOA
  102. #define GPIO_DI1_1_PORT                                        GPIOA
  103. #define GPIO_DI1_1_Pin                                        GPIO_Pin_4
  104. #define GPIO_DI1_1()                                        ((~GPIO_ReadInputData(GPIO_DI1_1_PORT) & 0x0010) >> 4)
  105. //#define GPIO_DI1_1()                                        GPIO_ReadInputDataBit(GPIO_DI1_1_PORT , GPIO_DI1_1_Pin)//((GPIO_ReadInputData(GPIO_DI1_1_PORT) & 0x0010) >> 4)//



  106. //DI2----采集到高电平,行程开关状态=1;采集到低电平,行程开关状态=0。
  107. #define  RCC_DI1_2_PORT                                        RCC_APB2Periph_GPIOA
  108. #define GPIO_DI1_2_PORT                                        GPIOA
  109. #define GPIO_DI1_2_Pin                                        GPIO_Pin_5
  110. #define GPIO_DI1_2()                                        ((~GPIO_ReadInputData(GPIO_DI1_2_PORT) & 0x0020) >> 5)
  111. //#define GPIO_DI1_2()                                        GPIO_ReadInputDataBit(GPIO_DI1_2_PORT , GPIO_DI1_2_Pin)//((GPIO_ReadInputData(GPIO_DI1_2_PORT) & 0x0020) >> 5)//




  112. //DI3----采集到高电平,行程开关状态=1;采集到低电平,行程开关状态=0。
  113. #define  RCC_DI2_1_PORT                                        RCC_APB2Periph_GPIOA
  114. #define GPIO_DI2_1_PORT                                        GPIOA
  115. #define GPIO_DI2_1_Pin                                        GPIO_Pin_6
  116. #define GPIO_DI2_1()                                        ((~GPIO_ReadInputData(GPIO_DI2_1_PORT) & 0x0040) >> 6)
  117. //#define GPIO_DI2_1()                                        ((GPIO_ReadInputData(GPIO_DI2_1_PORT) & 0x0040) >> 6)//GPIO_ReadInputDataBit(GPIO_DI2_1_PORT , GPIO_DI2_1_Pin)




  118. //DI4----采集到高电平,行程开关状态=1;采集到低电平,行程开关状态=0。
  119. #define  RCC_DI2_2_PORT                                        RCC_APB2Periph_GPIOA
  120. #define GPIO_DI2_2_PORT                                        GPIOA
  121. #define GPIO_DI2_2_Pin                                        GPIO_Pin_7
  122. #define GPIO_DI2_2()                                        ((~GPIO_ReadInputData(GPIO_DI2_2_PORT) & 0x0080) >> 7)
  123. //#define GPIO_DI2_2()                                        ((GPIO_ReadInputData(GPIO_DI2_2_PORT) & 0x0080) >> 7)//GPIO_ReadInputDataBit(GPIO_DI2_2_PORT , GPIO_DI2_2_Pin)




  124. //////////////////////////////////////////////////////////////////////
  125. //WIFIRELOAD  //输出上拉  高电平---->PC7
  126. //WIFIREADY   //输入上拉        ---->PC6
  127. //WIFIRESET   //输出上拉        ---->PD14
  128. //WIFILINK    //输入上拉        ---->PD15
  129. //////////////////////////////////////////////////////////////////////


  130. //WIFIRELOAD -----需要在程序中强行置为高电平
  131. //警告:WIFIRELOAD只能配置为输出上拉模式,程序中需要设置为高电平,即:GPIO_WIFIRELOAD_1()
  132. //      禁止将WIFIRELOAD设置为低电平,禁止执行GPIO_WIFIRELOAD_0(),执行GPIO_WIFIRELOAD_0()则
  133. //      WIFI会恢复出厂缺省值。
  134. //WIFIRELOAD(输出)
  135. #define RCC_WIFIRELOAD_PORT                                RCC_APB2Periph_GPIOC
  136. #define GPIO_WIFIRELOAD_PORT                        GPIOC
  137. #define GPIO_WIFIRELOAD_Pin                                GPIO_Pin_7
  138. #define GPIO_WIFIRELOAD_0()                                (GPIO_ResetBits(GPIO_WIFIRELOAD_PORT , GPIO_WIFIRELOAD_Pin))       
  139. #define GPIO_WIFIRELOAD_1()                                (GPIO_SetBits(GPIO_WIFIRELOAD_PORT , GPIO_WIFIRELOAD_Pin))



  140. //WIFIRESET(输出)
  141. #define RCC_WIFIRESET_PORT                                RCC_APB2Periph_GPIOD
  142. #define GPIO_WIFIRESET_PORT                                GPIOD
  143. #define GPIO_WIFIRESET_Pin                                GPIO_Pin_14
  144. #define GPIO_WIFIRESET_0()                                (GPIO_ResetBits(GPIO_WIFIRESET_PORT , GPIO_WIFIRESET_Pin))       
  145. #define GPIO_WIFIRESET_1()                                (GPIO_SetBits(GPIO_WIFIRESET_PORT , GPIO_WIFIRESET_Pin))




  146. //WIFIREADY(输入)
  147. #define RCC_WIFIREADY_PORT                                RCC_APB2Periph_GPIOC
  148. #define GPIO_WIFIREADY_PORT                                GPIOC
  149. #define GPIO_WIFIREADY_Pin                                GPIO_Pin_6
  150. #define GPIO_WIFIREADY()                                (~GPIO_ReadInputDataBit(GPIO_WIFIREADY_PORT , GPIO_WIFIREADY_Pin) & 0x01)        //        低电平为1,高电平为0


  151. //WIFILINK(输入)
  152. #define RCC_WIFILINK_PORT                                RCC_APB2Periph_GPIOD
  153. #define GPIO_WIFILINK_PORT                                GPIOD
  154. #define GPIO_WIFILINK_Pin                                GPIO_Pin_15
  155. #define GPIO_WIFILINK()                                        (~GPIO_ReadInputDataBit(GPIO_WIFILINK_PORT , GPIO_WIFILINK_Pin) & 0x01)        //        低电平为1,高电平为0




  156. //////////////////////////////////////////////////////////////////////
  157. //EC20_断电/供电
  158. //4G模块休断电/供电
  159. //////////////////////////////////////////////////////////////////////
  160. #define RCC_EC20_POWER_SLEEP_PORT                RCC_APB2Periph_GPIOA
  161. #define GPIO_EC20_POWER_SLEEP_PORT                GPIOA
  162. #define GPIO_EC20_POWER_SLEEP_Pin                GPIO_Pin_11
  163. #define GPIO_EC20_POWER_SLEEP()                        (GPIO_ResetBits(GPIO_EC20_POWER_SLEEP_PORT , GPIO_EC20_POWER_SLEEP_Pin))
  164. #define GPIO_EC20_POWER_NO_SLEEP()                (GPIO_SetBits(        GPIO_EC20_POWER_SLEEP_PORT , GPIO_EC20_POWER_SLEEP_Pin))       




  165. //////////////////////////////////////////////////////////////////////
  166. //EC20_RESET硬件复位
  167. //4G模块低电平复位
  168. //4G模块硬件复位过程如下:
  169. //(1)、给4G模块发送低电平------调用GPIO_EC20_RESET_0()
  170. //(2)、延时
  171. //(3)、给4G模块发送高电平------调用GPIO_EC20_RESET_1()
  172. //////////////////////////////////////////////////////////////////////
  173. #define RCC_EC20_RESET_PORT                                RCC_APB2Periph_GPIOA
  174. #define GPIO_EC20_RESET_PORT                        GPIOA
  175. #define GPIO_EC20_RESET_Pin                                GPIO_Pin_12
  176. #define GPIO_EC20_RESET_0()                                (GPIO_ResetBits(GPIO_EC20_RESET_PORT , GPIO_EC20_RESET_Pin))
  177. #define GPIO_EC20_RESET_1()                                (GPIO_SetBits(        GPIO_EC20_RESET_PORT , GPIO_EC20_RESET_Pin))




  178. typedef struct
  179. {       
  180.         uint8_t volatile MBHosts_Query_isr;
  181.     uint8_t volatile Outtime_mark;
  182.     uint16_t volatile sendCount;
  183.     uint16_t volatile sendPosi;
  184.     uint16_t volatile receCount;
  185.     uint8_t send_buffer[MSCOMM_BUFFER_LENGTH];
  186.     uint8_t mscomm_buffer[MSCOMM_BUFFER_LENGTH];
  187. } USART_DataType;





  188. typedef struct
  189. {
  190.         uint8_t year;
  191.         uint8_t month;
  192.         uint8_t day;
  193.         uint8_t hour;
  194.         uint8_t minute;
  195.         uint8_t second;
  196.         uint8_t week;               
  197. } CLOCK_Type;




  198. //#else
  199. //    #pragma message "The header PLATFORM.H is included twice or more !"
  200. #endif




  201. /*****************************************************************************/
  202. /*  Copyright (C) profichip GmbH 2005. Confidential.                         */
  203. /*****************************************************************************/





  204. static TaskHandle_t xHandleTaskLED1 = NULL;
  205. static TaskHandle_t xHandleTaskLED2 = NULL;
  206. static TaskHandle_t xHandleTaskDI1 = NULL;
  207. static TaskHandle_t xHandleTaskDI2 = NULL;
  208. static TaskHandle_t xHandleTaskSD2405ALPI = NULL;
  209. static TaskHandle_t xHandleTaskMODBUS = NULL;


  210. static void LED1_Task(void *pvParameters);
  211. static void LED2_Task(void *pvParameters);
  212. static void DI1_Task(void *pvParameters);
  213. static void DI2_Task(void *pvParameters);
  214. static void SD2405ALPI_Task(void *pvParameters);
  215. static void MODBUS_Task(void *pvParameters);  



  216. extern CLOCK_Type MODBUS_Clock;
  217. extern BOOL MODBUS_DI1_1;
  218. extern BOOL MODBUS_DI1_2;
  219. extern BOOL MODBUS_DI2_1;
  220. extern BOOL MODBUS_DI2_2;


  221. // 二值信号量定义
  222. SemaphoreHandle_t xSemaphore = NULL;
  223. QueueHandle_t xQueue = NULL;



  224. void LED1_Task(void *pvParameters)
  225. {
  226.         //taskENTER_CRITICAL();
  227.         //xTaskCreate(LED2_Task, "GPIO_LED2", configMINIMAL_STACK_SIZE, NULL, tskIDLE_PRIORITY + 3, &xHandleTaskLED2);
  228.         //xTaskCreate(DI1_Task, "DI1", configMINIMAL_STACK_SIZE, NULL, tskIDLE_PRIORITY + 5, &xHandleTaskDI1);
  229.         //xTaskCreate(DI2_Task, "DI2", configMINIMAL_STACK_SIZE, NULL, tskIDLE_PRIORITY + 5, &xHandleTaskDI2);
  230.         //xTaskCreate(SD2405ALPI_Task, "SD2405ALPI", configMINIMAL_STACK_SIZE, NULL, tskIDLE_PRIORITY + 7, &xHandleTaskSD2405ALPI);
  231.         //xTaskCreate(MODBUS_Task, "MODBUS" , configMINIMAL_STACK_SIZE, NULL, tskIDLE_PRIORITY + 9, &xHandleTaskMODBUS);
  232.         //taskEXIT_CRITICAL();
  233.        
  234.     while ( 1 )
  235.     {
  236.         GPIO_LED1_ON();
  237.                 GPIO_LED2_ON();
  238.         vTaskDelay(500 / portTICK_RATE_MS);
  239.         GPIO_LED1_OFF();
  240.                 GPIO_LED2_OFF();               
  241.         vTaskDelay(250 / portTICK_RATE_MS);        
  242.     }
  243. }



  244. void LED2_Task(void *pvParameters)
  245. {
  246.     while(1)
  247.     {
  248.         GPIO_LED3_ON();
  249.                 GPIO_LED4_ON();
  250.                 GPIO_LED5_ON();               
  251.         vTaskDelay(500 / portTICK_RATE_MS);
  252.         GPIO_LED3_OFF();
  253.                 GPIO_LED4_OFF();       
  254.                 GPIO_LED5_OFF();                       
  255.         vTaskDelay(500 / portTICK_RATE_MS);        
  256.     }
  257. }



  258. void DI2_Task(void *pvParameters)
  259. {
  260.         const TickType_t xTicksToWait = pdMS_TO_TICKS( 100UL );       
  261.         BOOL DI2_1 = FALSE;
  262.         BOOL DI2_2 = FALSE;       
  263.         uint8_t di21_array[2];
  264.         uint8_t di22_array[2];
  265.         uint8_t Buffer[10];
  266.        
  267.                
  268.         memset(di21_array , 0x00 , 2);
  269.         memset(di22_array , 0x00 , 2);
  270.     while(1)
  271.     {
  272.                 //第一次采集:采集行程2A和行程2B
  273.                 di21_array[0] = GPIO_DI2_1();
  274.                 di22_array[0] = GPIO_DI2_2();               
  275.                 vTaskDelay(20 / portTICK_RATE_MS);
  276.                 //第二次采集:采集行程2A和行程2B
  277.                 di21_array[1] = GPIO_DI2_1();
  278.                 di22_array[1] = GPIO_DI2_2();               
  279.                                
  280.                 if ((di21_array[0] == di21_array[1]) && (di22_array[0] == di22_array[1]))
  281.                 {       
  282.                         DI2_1 = (di21_array[0] ? TRUE : FALSE);
  283.                         DI2_2 = (di22_array[0] ? TRUE : FALSE);
  284.                 }       

  285.                 //函数 xQueueSend 用于任务中消息发送
  286.                 //第1个参数是消息队列句柄。
  287.                 //第2个参数要传递数据地址, 每次发送都是将消息队列创建函数 xQueueCreate 所指定的单个消息大小复制到消息队列空间中
  288.                 //第3个参数是当消息队列已经满时,等待消息队列有空间时的最大等待时间,单位系统时钟节拍。
  289.                 //返回值,如果消息成功发送返回 pdTRUE,否则返回 errQUEUE_FULL。
  290.                 //使用这个函数要注意以下问题:
  291.                 //1.FreeRTOS 的消息传递是数据的复制,而不是传递的数据地址。
  292.                 //2.此函数是用于任务代码中调用的,故不可以在中断服务程序中调用此函数,中断服务程序中使用的是xQueueSendFromISR
  293.                 //3.如果消息队列已经满且第三个参数为 0,那么此函数会立即返回。
  294.                 //4.如果用户将 FreeRTOSConfig.h 文件中的宏定义 INCLUDE_vTaskSuspend 配置为1且第三个参数配
  295.                 //        置为portMAX_DELAY,那么此发送函数会永久等待直到消息队列有空间可以使用。
  296.                 //5.消息队列还有两个函数 xQueueSendToBack 和 xQueueSendToFront,函数 xQueueSendToBack
  297.                 //        实现的是 FIFO 方式的存取,函数 xQueueSendToFront 实现的是 LIFO 方式的读写。我们这里说的函
  298.                 //数 xQueueSend 等效于 xQueueSendToBack,即实现的是 FIFO 方式的存取。        
  299.                 /* 向消息队列发数据,如果消息队列满了,等待10个时钟节拍 */
  300.                 Buffer[0] = 4;        //Func
  301.                 Buffer[1] = DI2_1;
  302.                 Buffer[2] = DI2_2;
  303.                
  304.         if( xQueueSend(xQueue, (void *) &Buffer , xTicksToWait) != pdPASS )
  305.                 {
  306.                          //发送失败,即使等待了10个时钟节拍
  307.                 }
  308.                 else
  309.                 {
  310.                         // 发送成功
  311.                 }                                       
  312.                 vTaskDelay(100 / portTICK_RATE_MS);               
  313.     }
  314. }



  315. void DI1_Task(void *pvParameters)
  316. {
  317.         const TickType_t xTicksToWait = pdMS_TO_TICKS( 100UL );       
  318.         BOOL DI1_1 = FALSE;
  319.         BOOL DI1_2 = FALSE;       
  320.         uint8_t di11_array[2];
  321.         uint8_t di12_array[2];       
  322.         uint8_t Buffer[10];
  323.        
  324.        
  325.         memset(di11_array , 0x00 , 2);
  326.         memset(di12_array , 0x00 , 2);
  327.     while(1)
  328.     {
  329.                 //第一次采集:采集行程1A和行程1B
  330.                 di11_array[0] = GPIO_DI1_1();
  331.                 di12_array[0] = GPIO_DI1_2();               
  332.                 vTaskDelay(20 / portTICK_RATE_MS);
  333.                 //第二次采集:采集行程1A和行程1B
  334.                 di11_array[1] = GPIO_DI1_1();
  335.                 di12_array[1] = GPIO_DI1_2();       
  336.                                
  337.                 if ((di11_array[0] == di11_array[1]) && (di12_array[0] == di12_array[1]))
  338.                 {       
  339.                         DI1_1 = (di11_array[0] ? TRUE : FALSE);
  340.                         DI1_2 = (di12_array[0] ? TRUE : FALSE);       
  341.                 }                               

  342.                 //函数 xQueueSend 用于任务中消息发送
  343.                 //第1个参数是消息队列句柄。
  344.                 //第2个参数要传递数据地址, 每次发送都是将消息队列创建函数 xQueueCreate 所指定的单个消息大小复制到消息队列空间中
  345.                 //第3个参数是当消息队列已经满时,等待消息队列有空间时的最大等待时间,单位系统时钟节拍。
  346.                 //返回值,如果消息成功发送返回 pdTRUE,否则返回 errQUEUE_FULL。
  347.                 //使用这个函数要注意以下问题:
  348.                 //1.FreeRTOS 的消息传递是数据的复制,而不是传递的数据地址。
  349.                 //2.此函数是用于任务代码中调用的,故不可以在中断服务程序中调用此函数,中断服务程序中使用的是xQueueSendFromISR
  350.                 //3.如果消息队列已经满且第三个参数为 0,那么此函数会立即返回。
  351.                 //4.如果用户将 FreeRTOSConfig.h 文件中的宏定义 INCLUDE_vTaskSuspend 配置为1且第三个参数配
  352.                 //        置为portMAX_DELAY,那么此发送函数会永久等待直到消息队列有空间可以使用。
  353.                 //5.消息队列还有两个函数 xQueueSendToBack 和 xQueueSendToFront,函数 xQueueSendToBack
  354.                 //        实现的是 FIFO 方式的存取,函数 xQueueSendToFront 实现的是 LIFO 方式的读写。我们这里说的函
  355.                 //数 xQueueSend 等效于 xQueueSendToBack,即实现的是 FIFO 方式的存取。        
  356.                 /* 向消息队列发数据,如果消息队列满了,等待10个时钟节拍 */
  357.                 Buffer[0] = 3;        //Func
  358.                 Buffer[1] = DI1_1;
  359.                 Buffer[2] = DI1_2;
  360.                
  361.         if( xQueueSend(xQueue, (void *) &Buffer , xTicksToWait) != pdPASS )
  362.                 {
  363.                          //发送失败,即使等待了10个时钟节拍
  364.                 }
  365.                 else
  366.                 {
  367.                         // 发送成功
  368.                 }                       
  369.                 vTaskDelay(100 / portTICK_RATE_MS);      
  370.     }
  371. }



  372. void SD2405ALPI_Task(void *pvParameters)
  373. {       
  374.         const TickType_t xTicksToWait = pdMS_TO_TICKS( 100UL );       
  375.         uint8_t Buffer[10];
  376.         CLOCK_Type Clock;
  377.        

  378.         memset(Buffer , 0x00 , 10);
  379.         bsp_InitSD2405();
  380.        
  381.     while(1)
  382.     {       
  383.                 SD2405_I2CReadTime(&Clock.second , &Clock.minute , &Clock.hour , &Clock.week , &Clock.day , &Clock.month , &Clock.year);
  384.                
  385.                 //函数 xQueueSend 用于任务中消息发送
  386.                 //第1个参数是消息队列句柄。
  387.                 //第2个参数要传递数据地址, 每次发送都是将消息队列创建函数 xQueueCreate 所指定的单个消息大小复制到消息队列空间中
  388.                 //第3个参数是当消息队列已经满时,等待消息队列有空间时的最大等待时间,单位系统时钟节拍。
  389.                 //返回值,如果消息成功发送返回 pdTRUE,否则返回 errQUEUE_FULL。
  390.                 //使用这个函数要注意以下问题:
  391.                 //1.FreeRTOS 的消息传递是数据的复制,而不是传递的数据地址。
  392.                 //2.此函数是用于任务代码中调用的,故不可以在中断服务程序中调用此函数,中断服务程序中使用的是xQueueSendFromISR
  393.                 //3.如果消息队列已经满且第三个参数为 0,那么此函数会立即返回。
  394.                 //4.如果用户将 FreeRTOSConfig.h 文件中的宏定义 INCLUDE_vTaskSuspend 配置为1且第三个参数配
  395.                 //        置为portMAX_DELAY,那么此发送函数会永久等待直到消息队列有空间可以使用。
  396.                 //5.消息队列还有两个函数 xQueueSendToBack 和 xQueueSendToFront,函数 xQueueSendToBack
  397.                 //        实现的是 FIFO 方式的存取,函数 xQueueSendToFront 实现的是 LIFO 方式的读写。我们这里说的函
  398.                 //数 xQueueSend 等效于 xQueueSendToBack,即实现的是 FIFO 方式的存取。        
  399.                 /* 向消息队列发数据,如果消息队列满了,等待10个时钟节拍 */
  400.                 Buffer[0] = 2;        //Func
  401.                 Buffer[1] = Clock.year;
  402.                 Buffer[2] = Clock.month;
  403.                 Buffer[3] = Clock.day;
  404.                 Buffer[4] = Clock.hour;
  405.                 Buffer[5] = Clock.minute;
  406.                 Buffer[6] = Clock.second;
  407.                 Buffer[7] = Clock.week;
  408.                
  409.         if( xQueueSend(xQueue, (void *) &Buffer , xTicksToWait) != pdPASS )
  410.                 {
  411.                          //发送失败,即使等待了10个时钟节拍
  412.                 }
  413.                 else
  414.                 {
  415.                         // 发送成功
  416.                 }                               
  417.         vTaskDelay(1000 / portTICK_RATE_MS);        
  418.     }
  419. }



  420. void MODBUS_Task(void *pvParameters)
  421. {
  422.         //BaseType_t xResult;
  423.         BaseType_t xStatus;       
  424.         const TickType_t xTicksToWait = pdMS_TO_TICKS( 100UL );
  425.         uint8_t Buffer[10];
  426.        
  427.        
  428.         USART1_Configuration();
  429.         USART1_DMA_Tx_Configuration();
  430.         USART1_DMA_Rx_Configuration();       
  431.     while(1)
  432.     {       
  433.                 //xResult = xSemaphoreTake(xSemaphore, portMAX_DELAY);
  434.                 //if(xResult == pdTRUE)
  435.                 //{
  436.                         if( xQueue != NULL )
  437.                         {
  438.                         //函数 xQueueReceive 用于接收消息队列中的数据。
  439.                         //第 1 个参数是消息队列句柄。
  440.                         //第 2 个参数是从消息队列中复制出数据后所储存的缓冲地址,缓冲区空间要大于等于消息队列创建函
  441.                         //数xQueueCreate 所指定的单个消息大小,否则取出的数据无法全部存储到缓冲区,从而造成内存溢出。
  442.                         //第 3 个参数是消息队列为空时,等待消息队列有数据的最大等待时间,单位系统时钟节拍。
  443.                         //返回值,如果接到到消息返回 pdTRUE,否则返回 pdFALSE。
  444.                         //使用这个函数要注意以下问题:
  445.                         //1. 此函数是用于任务代码中调用的,故不可以在中断服务程序中调用此函数,中断服务程序使用的是xQueueReceiveFromISR。
  446.                         //2. 如果消息队列为空且第三个参数为 0,那么此函数会立即返回。
  447.                         //3. 如果用户将 FreeRTOSConfig.h 文件中的宏定义 INCLUDE_vTaskSuspend 配置为 1 且第三个参数配
  448.                         //置为 portMAX_DELAY,那么此函数会永久等待直到消息队列有数据。
  449.                                 xStatus = xQueueReceive( xQueue, &Buffer, xTicksToWait );
  450.                                 if( xStatus == pdPASS )
  451.                                 {                       
  452.                                         switch (Buffer[0])
  453.                                         {
  454.                                                 case 1:
  455.                                                         USART1_Modbus_Slave_APP();
  456.                                                         break;
  457.                                                 case 2:
  458.                                                         MODBUS_Clock.year = Buffer[1];
  459.                                                         MODBUS_Clock.month = Buffer[2];
  460.                                                         MODBUS_Clock.day = Buffer[3];
  461.                                                         MODBUS_Clock.hour = Buffer[4];
  462.                                                         MODBUS_Clock.minute = Buffer[5];
  463.                                                         MODBUS_Clock.second = Buffer[6];
  464.                                                         MODBUS_Clock.week = Buffer[7];
  465.                                                         break;
  466.                                                 case 3:
  467.                                                         MODBUS_DI1_1 = Buffer[1];
  468.                                                         MODBUS_DI1_2 = Buffer[2];
  469.                                                         break;
  470.                                                 case 4:
  471.                                                         MODBUS_DI2_1 = Buffer[1];
  472.                                                         MODBUS_DI2_2 = Buffer[2];                                                       
  473.                                                         break;
  474.                                         }//switch (Buffer[0])
  475.                                        
  476.                                 }//if( xStatus == pdPASS )
  477.                         }//if( xQueue != NULL )
  478.                 //}
  479.     }//while(1)
  480. }




  481. int main(void)
  482. {
  483.         /*
  484.           在启动调度前,为了防止初始化STM32外设时有中断服务程序执行,这里禁止全局中断(除了NMI和HardFault)。
  485.           这样做的好处是:
  486.           1. 防止执行的中断服务程序中有FreeRTOS的API函数。
  487.           2. 保证系统正常启动,不受别的中断影响。
  488.           3. 关于是否关闭全局中断,大家根据自己的实际情况设置即可。
  489.           在移植文件port.c中的函数prvStartFirstTask中会重新开启全局中断。通过指令cpsie i开启,__set_PRIMASK(1)
  490.           和cpsie i是等效的。
  491.      */
  492.         __set_PRIMASK(1);         
  493.         MB_USART1_Init();
  494.         MB_USART2_Init();
  495.         GPIO_Configuration();
  496.         NVIC_Configuration();       
  497.         //创建二值信号量创建二值信号量(创建二值信号量,首次创建信号量计数值是0)
  498.     xSemaphore = xSemaphoreCreateBinary();
  499.         if( xSemaphore != NULL )
  500.         {       
  501.             // 创建消息队列
  502.                 //第1个参数是消息队列支持的消息个数
  503.                 //第2个参数是每个消息的大小,单位字节。
  504.                 //返回值, 如果创建成功会返回消息队列的句柄,否则返回NULL
  505.                 //使用这个函数要注意以下问题:
  506.                 //1. FreeRTOS 的消息传递是数据的复制,而不是传递的数据地址,这点要特别注意。
  507.                 xQueue = xQueueCreate(20 , 10);        //可以装载5个消息,每个消息10个字节
  508.                 if( xQueue != NULL )
  509.                 {                       
  510.                         //xTaskCreate(LED1_Task, "GPIO_LED1", configMINIMAL_STACK_SIZE, NULL, tskIDLE_PRIORITY + 3 , &xHandleTaskLED1);
  511.                        
  512.                         taskENTER_CRITICAL();
  513.                         xTaskCreate(LED1_Task, "GPIO_LED1", configMINIMAL_STACK_SIZE, NULL, tskIDLE_PRIORITY + 3 , &xHandleTaskLED1);
  514.                         xTaskCreate(LED2_Task, "GPIO_LED2", configMINIMAL_STACK_SIZE, NULL, tskIDLE_PRIORITY + 3 , &xHandleTaskLED2);
  515.                         xTaskCreate(DI1_Task, "DI1", configMINIMAL_STACK_SIZE, NULL, tskIDLE_PRIORITY + 5, &xHandleTaskDI1);
  516.                         xTaskCreate(DI2_Task, "DI2", configMINIMAL_STACK_SIZE, NULL, tskIDLE_PRIORITY + 5, &xHandleTaskDI2);
  517.                         xTaskCreate(SD2405ALPI_Task, "SD2405ALPI", configMINIMAL_STACK_SIZE, NULL, tskIDLE_PRIORITY + 7, &xHandleTaskSD2405ALPI);
  518.                         xTaskCreate(MODBUS_Task, "MODBUS" , configMINIMAL_STACK_SIZE, NULL, tskIDLE_PRIORITY + 9, &xHandleTaskMODBUS);
  519.                         taskEXIT_CRITICAL();
  520.                        
  521.                         //启动调度器
  522.                         vTaskStartScheduler();
  523.                 }
  524.         }
  525.     while (1);       
  526. }



  527. extern QueueHandle_t xQueue;
  528. extern uint8_t USART1_DMA_RX_Buffer[USART1_DMA_RX_BUFFER_MAX_LENGTH];
  529. USART_DataType MB_USART1;
  530.   


  531. //DMA中断方式
  532. void USART1_IRQHandler(void)
  533. {
  534.         uint16_t ch;
  535.         uint8_t Buffer[10];
  536.         BaseType_t xHigherPriorityTaskWoken = pdFALSE;
  537.        
  538.        
  539.         if (USART_GetITStatus(USART1,USART_IT_IDLE) != RESET)
  540.         {               
  541.                 USART_ClearITPendingBit(USART1 , USART_IT_IDLE);        //必须先清除总线空闲中断标识,然后读一下数据寄存器,DMA接收才会正确(先读SR,然后读DR才能清除空闲中断标识)注意:这句必须要,否则不能够清除中断标志位。
  542.                 ch =  USART_ReceiveData(USART1);                                        //必须先清除总线空闲中断标识,然后读一下数据寄存器,DMA接收才会正确(先读SR,然后读DR才能清除空闲中断标识)注意:这句必须要,否则不能够清除中断标志位。                               
  543.                 DMA_Cmd(DMA1_Channel5 , DISABLE);                                         //关闭DMA,防止处理其间有数据
  544.                 DMA_ClearFlag(DMA1_FLAG_GL5 | DMA1_FLAG_TC5 | DMA1_FLAG_HT5 | DMA1_FLAG_TE5);
  545.                 ch = USART1_DMA_RX_BUFFER_MAX_LENGTH - DMA_GetCurrDataCounter(DMA1_Channel5);
  546.                 if (ch > 0)
  547.                 {                       
  548.                         MB_USART1.receCount        = ch;
  549.                         memcpy(MB_USART1.mscomm_buffer , USART1_DMA_RX_Buffer , MB_USART1.receCount);                       
  550.                 }
  551.                 DMA_SetCurrDataCounter(DMA1_Channel5 , USART1_DMA_RX_BUFFER_MAX_LENGTH);
  552.                 DMA_Cmd(DMA1_Channel5, ENABLE);       
  553.                
  554.        
  555.                 Buffer[0] = 1;
  556.                 xQueueSendToBackFromISR(xQueue , Buffer , &xHigherPriorityTaskWoken);
  557.                 portYIELD_FROM_ISR(xHigherPriorityTaskWoken);   
  558.         }
  559.        
  560.         else if (USART_GetITStatus(USART1,USART_IT_TC)!= RESET)
  561.         {
  562.                 USART_ClearITPendingBit(USART1, USART_IT_TC);                               

  563.                 DMA_ClearFlag(DMA1_FLAG_GL4 | DMA1_FLAG_TC4 | DMA1_FLAG_HT4 | DMA1_FLAG_TE4);
  564.                 DMA_SetCurrDataCounter(DMA1_Channel4 , 0);

  565.         }       
  566. }
复制代码




回复

使用道具 举报

19

主题

334

帖子

0

精华

金牌会员

Rank: 6Rank: 6

积分
1108
金钱
1108
注册时间
2018-11-6
在线时间
240 小时
 楼主| 发表于 2020-1-21 11:02:23 | 显示全部楼层
切记:
    由于任务3、任务4、任务5会分别给任务6(MODBUS任务)发送消息队列,因此任务6对应的通信串口接收上位机请求报文成功后,不能在串口中断服务程序中发送信号量同步任务6,必须在串口中断中发送消息队列同步任务6.
     如果你坚持在串口中断服务程序发送信号同步任务6,那么程序执行会有问题。
  
回复

使用道具 举报

19

主题

334

帖子

0

精华

金牌会员

Rank: 6Rank: 6

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

今天我对9#楼的问题进行验证,发现我在9#楼发表的观点是错误的, 验证代码如下:
      一、
          创建6个任务,
     1、任务1为:LED1_Task任务,闪烁2个指示灯,为周期性任务,延时周期为750毫秒
     2、任务2为:LED2_Task任务,闪烁3个指示灯,为周期性任务,延时周期为1000毫秒
     3、任务3为:DI1_Task任务,采集2路行程开关(然后给MODBUS_Task任务发送消息队列将2路行程开关发送出去),为周期性任务,延时周期为100毫秒
     4、任务4为:DI2_Task任务,采集2路行程开关(然后给MODBUS_Task任务发送消息队列将2路行程开关发送出去),为周期性任务,延时周期为100毫秒
     5、任务5为:SD2405ALPI_Task任务,读取时钟时间(年、月、日、时、分、秒)(然后给MODBUS_Task任务发送消息队列将时钟时间发送出去),为周期性任务,延时周期为1000毫秒
     6、任务6为:MODBUS_Task任务,为阻塞任务,当接收到串口中断服务程序发送的信号量后(接收上位机的MODBUS主站请求报文),任务唤醒,将采集到的2路行程开关状态以及时钟时间(年、月、日、时、分、秒)发送给上位机。

      二、规划消息队列格式:
                MSG[0]=2   ---> 表示收到SD2405ALPI_Task任务的时钟时间(年、月、日、时、分、秒)
                MSG[0]=3   ---> 表示收到DI1_Task任务的2路行程开关
                MSG[0]=4   ---> 表示收到DI2_Task任务的2路行程开关

      三、单片机串口接收到上位机的MODBUS主站请求报文后,发送信号量同步MODBUS_Task任务

      四、单片机串口中断服务程序中发送信号量
  1. void USART1_IRQHandler(void)
  2. {
  3.         uint16_t ch;
  4.         BaseType_t xHigherPriorityTaskWoken = pdFALSE;
  5.         
  6.         
  7.         if (USART_GetITStatus(USART1,USART_IT_IDLE) != RESET)
  8.         {               
  9.                 USART_ClearITPendingBit(USART1 , USART_IT_IDLE);        //必须先清除总线空闲中断标识,然后读一下数据寄存器,DMA接收才会正确(先读SR,然后读DR才能清除空闲中断标识)注意:这句必须要,否则不能够清除中断标志位。
  10.                 ch =  USART_ReceiveData(USART1);                                        //必须先清除总线空闲中断标识,然后读一下数据寄存器,DMA接收才会正确(先读SR,然后读DR才能清除空闲中断标识)注意:这句必须要,否则不能够清除中断标志位。                                
  11.                 DMA_Cmd(DMA1_Channel5 , DISABLE);                                         //关闭DMA,防止处理其间有数据
  12.                 DMA_ClearFlag(DMA1_FLAG_GL5 | DMA1_FLAG_TC5 | DMA1_FLAG_HT5 | DMA1_FLAG_TE5);
  13.                 ch = USART1_DMA_RX_BUFFER_MAX_LENGTH - DMA_GetCurrDataCounter(DMA1_Channel5);
  14.                 if (ch > 0)
  15.                 {
  16.                         MB_USART1.receCount        = ch;
  17.                         memcpy(MB_USART1.mscomm_buffer , USART1_DMA_RX_Buffer , MB_USART1.receCount);                        
  18.                 }
  19.                 DMA_SetCurrDataCounter(DMA1_Channel5 , USART1_DMA_RX_BUFFER_MAX_LENGTH);
  20.                 DMA_Cmd(DMA1_Channel5, ENABLE);        
  21.                
  22.                 xSemaphoreGiveFromISR(xSemaphore, &xHigherPriorityTaskWoken);//发送同步信号
  23.                 //如果xHigherPriorityTaskWoken = pdTRUE,那么退出中断后切到当前最高优先级任务执行
  24.                 portYIELD_FROM_ISR(xHigherPriorityTaskWoken);

  25.         }
  26.         
  27.         else if (USART_GetITStatus(USART1,USART_IT_TC)!= RESET)
  28.         {
  29.                 USART_ClearITPendingBit(USART1, USART_IT_TC);        
  30.                 DMA_ClearFlag(DMA1_FLAG_GL4 | DMA1_FLAG_TC4 | DMA1_FLAG_HT4 | DMA1_FLAG_TE4);
  31.                 DMA_SetCurrDataCounter(DMA1_Channel4 , 0);
  32.         }        
  33. }
复制代码


      五、MODBUS_Task任务
           任务代码大致框架如下:
           void MODBUS_Task()
           {
                   uint8_t Over = 0;
                while (1)                           //  第1个死循环
                {
                        等待信号量;
                        if (收到信号量成功)
                       {
                             while (1)              //  第2个死循环
                             {
                                  等待消息队列;
                                   if (收到消息队列成功)
                                  {
                                       switch (MSG[0])
                                       {
                                            case 2:
                                                Over |= 0x01;     //bit0=1,表示收到SD2405ALPI_Task任务的消息队列数据
                                                存储时钟时间(年、月、日、时、分、秒)
                                                break;
                                            case 3:
                                                Over |= 0x02;    //bit1=1,表示收到DI1_Task任务的消息队列数据
                                                 存储DI1的2路行程开关状态
                                                break;
                                            case 4:
                                                Over |= 0x04;    //bit2=1,表示收到DI2_Task任务的消息队列数据
                                                 存储DI2的2路行程开关状态
                                                break;

                                          }//switch (MSG[0])

                                         if (Over == 7)
                                         {
                                                Over = 0;
                                                将时钟时间(年、月、日、时、分、秒)、DI1的2路行程开关状态、DI2的2路行程开关状态发送给上位机
                                                break;
                                         } // if (Over == 7)
                                }// if (收到消息队列成功)                             
                       }// while (1)              //  第2个死循环
                } // if (收到信号量成功)
           } //while (1)                           //  第1个死循环
     }

  1. void MODBUS_Task(void *pvParameters)
  2. {
  3.         BaseType_t xResult;
  4.         BaseType_t xStatus;        
  5.         const TickType_t xTicksToWait = pdMS_TO_TICKS( 100UL );
  6.         uint8_t Buffer[10];
  7.         uint8_t Over = 0x00;
  8.         
  9.         
  10.         USART1_Configuration();
  11.         USART1_DMA_Tx_Configuration();
  12.         USART1_DMA_Rx_Configuration();        
  13.     while(1)
  14.     {        
  15.                 xResult = xSemaphoreTake(xSemaphore, portMAX_DELAY);
  16.                 if(xResult == pdTRUE)
  17.                 {
  18.                         //if( xQueue != NULL )
  19.                         //{
  20.                         //函数 xQueueReceive 用于接收消息队列中的数据。
  21.                         //第 1 个参数是消息队列句柄。
  22.                         //第 2 个参数是从消息队列中复制出数据后所储存的缓冲地址,缓冲区空间要大于等于消息队列创建函
  23.                         //数xQueueCreate 所指定的单个消息大小,否则取出的数据无法全部存储到缓冲区,从而造成内存溢出。
  24.                         //第 3 个参数是消息队列为空时,等待消息队列有数据的最大等待时间,单位系统时钟节拍。
  25.                         //返回值,如果接到到消息返回 pdTRUE,否则返回 pdFALSE。
  26.                         //使用这个函数要注意以下问题:
  27.                         //1. 此函数是用于任务代码中调用的,故不可以在中断服务程序中调用此函数,中断服务程序使用的是xQueueReceiveFromISR。
  28.                         //2. 如果消息队列为空且第三个参数为 0,那么此函数会立即返回。
  29.                         //3. 如果用户将 FreeRTOSConfig.h 文件中的宏定义 INCLUDE_vTaskSuspend 配置为 1 且第三个参数配
  30.                         //置为 portMAX_DELAY,那么此函数会永久等待直到消息队列有数据。
  31.                                 while (1)
  32.                                 {
  33.                                         xStatus = xQueueReceive( xQueue, &Buffer, xTicksToWait );
  34.                                         if( xStatus == pdPASS )
  35.                                         {                        
  36.                                                 switch (Buffer[0])
  37.                                                 {
  38.                                                         case 2:
  39.                                                                 Over |= 0x01;        //bit0=1,表示收到SD2405ALPI_Task任务的消息队列数据
  40.                                                                 MODBUS_Clock.year = Buffer[1];
  41.                                                                 MODBUS_Clock.month = Buffer[2];
  42.                                                                 MODBUS_Clock.day = Buffer[3];
  43.                                                                 MODBUS_Clock.hour = Buffer[4];
  44.                                                                 MODBUS_Clock.minute = Buffer[5];
  45.                                                                 MODBUS_Clock.second = Buffer[6];
  46.                                                                 MODBUS_Clock.week = Buffer[7];
  47.                                                                 break;
  48.                                                         case 3:
  49.                                                                 Over |= 0x02;        //bit1=1,表示收到DI1_Task任务的消息队列数据
  50.                                                                 MODBUS_DI1_1 = Buffer[1];
  51.                                                                 MODBUS_DI1_2 = Buffer[2];
  52.                                                                 break;
  53.                                                         case 4:
  54.                                                                 Over |= 0x04;        //bit2=1,表示收到DI2_Task任务的消息队列数据
  55.                                                                 MODBUS_DI2_1 = Buffer[1];
  56.                                                                 MODBUS_DI2_2 = Buffer[2];                                                        
  57.                                                                 break;
  58.                                                 }//switch (Buffer[0])
  59.                                        
  60.                                                 if (Over == 0x07)                //bit0=1,bit1=1,bit2=1,表示3个任务的消息队列数据都已经收到
  61.                                                 {
  62.                                                         Over = 0x00;
  63.                                                         USART1_Modbus_Slave_APP();
  64.                                                         break;
  65.                                                 }
  66.                                         }
  67.                                 }//if( xStatus == pdPASS )
  68.                         //}//if( xQueue != NULL )
  69.                 }//if(xResult == pdTRUE)
  70.     }//while(1)
  71. }
复制代码






回复

使用道具 举报

19

主题

334

帖子

0

精华

金牌会员

Rank: 6Rank: 6

积分
1108
金钱
1108
注册时间
2018-11-6
在线时间
240 小时
 楼主| 发表于 2020-2-17 15:00:39 | 显示全部楼层
下面附完整的源程序

V4-006_基础例程 FreeRTOS DMA USART1 消息队列 信号量 MODBUS通信OK.zip

3.11 MB, 下载次数: 45

回复

使用道具 举报

19

主题

334

帖子

0

精华

金牌会员

Rank: 6Rank: 6

积分
1108
金钱
1108
注册时间
2018-11-6
在线时间
240 小时
 楼主| 发表于 2020-2-17 16:38:51 | 显示全部楼层
运行过程中发现一个现象:

    当单片机运行一段时间,然后再和上位机连接,发现上位机会先显示一会【很久】之前的数据。
     原因是由于单片机和上位机没有连接之前,消息队列已经装满,而且你也不知道上位机什么时候和单片机连接,如何解决这个问题呢?

回复

使用道具 举报

19

主题

334

帖子

0

精华

金牌会员

Rank: 6Rank: 6

积分
1108
金钱
1108
注册时间
2018-11-6
在线时间
240 小时
 楼主| 发表于 2020-2-17 16:40:43 | 显示全部楼层
正点原子,帮忙分析和解决一下,好吗?
回复

使用道具 举报

4

主题

65

帖子

0

精华

中级会员

Rank: 3Rank: 3

积分
461
金钱
461
注册时间
2016-8-4
在线时间
192 小时
发表于 2020-2-18 09:30:07 | 显示全部楼层
霸王猫 发表于 2020-2-17 16:38
运行过程中发现一个现象:

     当单片机运行一段时间,然后再和上位机连接,发现上位机会先显示一会【 ...

这个应该是你发送buf没清空吧,发送之前不需要先把发送缓存清空吗?
回复

使用道具 举报

19

主题

334

帖子

0

精华

金牌会员

Rank: 6Rank: 6

积分
1108
金钱
1108
注册时间
2018-11-6
在线时间
240 小时
 楼主| 发表于 2020-2-21 15:18:38 | 显示全部楼层
小施 发表于 2020-2-18 09:30
这个应该是你发送buf没清空吧,发送之前不需要先把发送缓存清空吗?

不是buf没有清空的问题。

    是使用消息队列造成的。
   RTOS中本身就要求如果使用消息队列的话,必须保证生产者的生产速度必须小于消费者速度。
本例中由于本设备需要和上位机通信,本设备为生产者,上位机为消费者。
    当本机上电运行时,你不知道上位机什么时候运行(有可能上位机在本设备开机1个小时后才开机运行),因此本设备会将消息队列装满。
   当消息队列已经装满后,这个时候上位机才开始运行,因此会将之前的【老数据】发送给上位机,这才是为什么上位机会先显示一会【老数据】的真正原因。
回复

使用道具 举报

19

主题

334

帖子

0

精华

金牌会员

Rank: 6Rank: 6

积分
1108
金钱
1108
注册时间
2018-11-6
在线时间
240 小时
 楼主| 发表于 2020-2-21 15:21:48 | 显示全部楼层
因此设计主从通信(例如:MODBUS通信)时(本设备为从【生产者】,上位机为主【消费者】),就不能使用消息队列传输数据,只能使用互斥信号量共享访问全局变量来传输数据。
回复

使用道具 举报

19

主题

334

帖子

0

精华

金牌会员

Rank: 6Rank: 6

积分
1108
金钱
1108
注册时间
2018-11-6
在线时间
240 小时
 楼主| 发表于 2021-12-9 09:17:33 | 显示全部楼层
霸王猫 发表于 2020-2-21 15:21
因此设计主从通信(例如:MODBUS通信)时(本设备为从【生产者】,上位机为主【消费者】),就不能使用消息 ...

参见:

    FreeRTOS如何解决使用消息队列时,生产者可以随时生产消息,但是无法确定消费者何时消费消息的问题-OpenEdv-开源电子网

回复

使用道具 举报

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

本版积分规则



关闭

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

正点原子公众号

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

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

Powered by OpenEdv-开源电子网

© 2001-2030 OpenEdv-开源电子网

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