你的浏览器版本过低,可能导致网站不能正常访问!
为了你能正常使用网站功能,请使用这些浏览器。

STM32G0 FreeRTOS和FreeModbus库使用

[复制链接]
STMCU小助手 发布时间:2023-2-22 16:08
1.新建项目

在PIO的Home页面新建项目,项目名称freertos_modbus,选择开发板为 MonkeyPi_STM32_G070RB,开发框架选择libopencm3;
  • 项目建立完成后在src目录下新建main.c主程序文件;
  • 修改下载和调试方式,这里开发板使用的是DAPLink仿真器,因此修改platformio.ini文件如下:

  1. 1upload_protocol = cmsis-dap
  2. 2debug_tool = cmsis-dap
复制代码


2 编写程序
2.1 添加 freeModbus 库
然后从FreeModbus源码中的 demo\BARE\port中复制文件到工程的src\modbus_port文件夹下,最后的文件夹结构如下:
这里使用RS485,因此需要对RS485使能端口进行配置,其他为串口的配置,然后在发送和接收中断时候调用modbus相关接口进行处理:

  1. 1{
  2. 2    "name": "FreeModbus",
  3. 3    "version": "master",
  4. 4    "repository":{
  5. 5        "type":"git",
  6. 6        "url":"https://github.com/cwalter-at/freemodbus"
  7. 7    },
  8. 8    "build": {
  9. 9        "flags": [
  10. 10            "-Iascii",
  11. 11            "-Ifunctions",
  12. 12            "-Iinclude",
  13. 13            "-Irtu",
  14. 14            "-Itcp"
  15. 15        ],
  16. 16        "srcFilter": [
  17. 17            "+<*>"
  18. 18        ]
  19. 19    }
  20. 20}
复制代码


然后从FreeModbus源码中的 demo\BARE\port中复制文件到工程的src\modbus_port文件夹下,最后的文件夹结构如下:


微信图片_20230222160807.png

2.2移植
portevent:

  1. 1/* ----------------------- Modbus includes ----------------------------------*/
  2. 2#include "mb.h"
  3. 3#include "mbport.h"
  4. 4#include "FreeRTOS.h"
  5. 5#include "task.h"
  6. 6
  7. 7/* ----------------------- Variables ----------------------------------------*/
  8. 8static eMBEventType eQueuedEvent;
  9. 9static BOOL     xEventInQueue;
  10. 10static uint32_t modbus_last_active_time = 0;
  11. 11
  12. 12uint32_t get_modbus_last_active_time(void)
  13. 13{
  14. 14    return modbus_last_active_time;
  15. 15}
  16. 16
  17. 17/* ----------------------- Start implementation -----------------------------*/
  18. 18BOOL
  19. 19xMBPortEventInit( void )
  20. 20{
  21. 21    xEventInQueue = FALSE;
  22. 22    return TRUE;
  23. 23}
  24. 24
  25. 25BOOL
  26. 26xMBPortEventPost( eMBEventType eEvent )
  27. 27{
  28. 28    xEventInQueue = TRUE;
  29. 29    eQueuedEvent = eEvent;
  30. 30
  31. 31    if (eEvent == EV_EXECUTE) {
  32. 32        modbus_last_active_time = xTaskGetTickCount();
  33. 33    }
  34. 34    return TRUE;
  35. 35}
  36. 36
  37. 37BOOL
  38. 38xMBPortEventGet( eMBEventType * eEvent )
  39. 39{
  40. 40    BOOL            xEventHappened = FALSE;
  41. 41
  42. 42    if( xEventInQueue )
  43. 43    {
  44. 44        *eEvent = eQueuedEvent;
  45. 45        xEventInQueue = FALSE;
  46. 46        xEventHappened = TRUE;
  47. 47    }
  48. 48    return xEventHappened;
  49. 49}
复制代码


portserial
这里使用RS485,因此需要对RS485使能端口进行配置,其他为串口的配置,然后在发送和接收中断时候调用modbus相关接口进行处理:

  1. 1#include "port.h"
  2.   2
  3.   3#include "FreeRTOS.h"
  4.   4#include "queue.h"
  5.   5
  6.   6#include <libopencm3/cm3/nvic.h>
  7.   7#include <libopencm3/stm32/usart.h>
  8.   8#include <libopencm3/stm32/rcc.h>
  9.   9#include <libopencm3/stm32/gpio.h>
  10. 10
  11. 11/* ----------------------- Modbus includes ----------------------------------*/
  12. 12#include "mb.h"
  13. 13#include "mbport.h"
  14. 14
  15. 15/* ----------------------- static functions ---------------------------------*/
  16. 16
  17. 17xQueueHandle uart_queue;
  18. 18
  19. 19#define RS485_1_CLOCK        RCC_GPIOB
  20. 20#define RS485_1_EN_PORT        GPIOB
  21. 21#define RS485_1_EN_PIN        GPIO8
  22. 22
  23. 23static void rs485_delay(int n)
  24. 24{
  25. 25    while (--n) {
  26. 26        __asm__ volatile ("nop");
  27. 27    }
  28. 28}
  29. 29
  30. 30static inline void rs485_1_rx_mode(void)
  31. 31{
  32. 32    gpio_clear(RS485_1_EN_PORT, RS485_1_EN_PIN);
  33. 33}
  34. 34
  35. 35static inline void rs485_1_tx_mode(void)
  36. 36{
  37. 37    gpio_set(RS485_1_EN_PORT, RS485_1_EN_PIN);
  38. 38}
  39. 39
  40. 40static inline void rs485_gpio_init(void)
  41. 41{
  42. 42    rcc_periph_clock_enable(RS485_1_CLOCK);
  43. 43    gpio_mode_setup(RS485_1_EN_PORT, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, RS485_1_EN_PIN);
  44. 44
  45. 45    rs485_1_rx_mode();
  46. 46}
  47. 47
  48. 48/* ----------------------- Start implementation -----------------------------*/
  49. 49void
  50. 50vMBPortSerialEnable( BOOL xRxEnable, BOOL xTxEnable )
  51. 51{
  52. 52    /* If xRXEnable enable serial receive interrupts. If xTxENable enable
  53. 53     * transmitter empty interrupts.
  54. 54     */
  55. 55    if (xRxEnable) {
  56. 56        rs485_delay(10000);
  57. 57        rs485_1_rx_mode();
  58. 58        rs485_delay(10000);
  59. 59        usart_enable_rx_interrupt(USART1);
  60. 60    }
  61. 61    else {
  62. 62        usart_disable_rx_interrupt(USART1);
  63. 63    }
  64. 64
  65. 65    if (xTxEnable) {
  66. 66        rs485_delay(10000);
  67. 67        rs485_1_tx_mode();
  68. 68        rs485_delay(10000);
  69. 69        usart_enable_tx_interrupt(USART1);
  70. 70    }
  71. 71    else {
  72. 72        usart_disable_tx_interrupt(USART1);
  73. 73
  74. 74    }
  75. 75}
  76. 76
  77. 77BOOL
  78. 78xMBPortSerialInit( UCHAR ucPORT, ULONG ulBaudRate, UCHAR ucDataBits, eMBParity eParity )
  79. 79{
  80. 80    nvic_enable_irq(NVIC_USART1_IRQ);
  81. 81
  82. 82    rcc_periph_clock_enable(RCC_GPIOB);
  83. 83    gpio_mode_setup(GPIOB, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO6 | GPIO7);
  84. 84    gpio_set_af(GPIOB, GPIO_AF0, GPIO6 | GPIO7);
  85. 85
  86. 86    rcc_periph_clock_enable(RCC_USART1);
  87. 87
  88. 88    /* Set up USART/UART parameters using the libopencm3 helper functions */
  89. 89    usart_set_baudrate(USART1, ulBaudRate);
  90. 90    usart_set_databits(USART1, ucDataBits);
  91. 91    usart_set_stopbits(USART1, USART_STOPBITS_1);
  92. 92    usart_set_mode(USART1, USART_MODE_TX_RX);
  93. 93
  94. 94    switch (eParity) {
  95. 95        case MB_PAR_ODD:
  96. 96            usart_set_parity(USART1, USART_PARITY_ODD);
  97. 97            break;
  98. 98        case MB_PAR_EVEN:
  99. 99            usart_set_parity(USART1, USART_PARITY_EVEN);
  100. 100            break;
  101. 101        default:
  102. 102            usart_set_parity(USART1, USART_PARITY_NONE);
  103. 103            break;
  104. 104    }
  105. 105
  106. 106    usart_set_flow_control(USART1, USART_FLOWCONTROL_NONE);
  107. 107
  108. 108    usart_enable(USART1);
  109. 109
  110. 110    rs485_gpio_init();
  111. 111
  112. 112    return TRUE;
  113. 113}
  114. 114
  115. 115BOOL
  116. 116xMBPortSerialPutByte( CHAR ucByte )
  117. 117{
  118. 118
  119. 119    usart_send_blocking(USART1, (uint16_t) ucByte);   
  120. 120
  121. 121    return TRUE;
  122. 122}
  123. 123
  124. 124BOOL
  125. 125xMBPortSerialGetByte( CHAR * pucByte )
  126. 126{
  127. 127    *pucByte = usart_recv(USART1);
  128. 128
  129. 129    return TRUE;
  130. 130}
  131. 131
  132. 132
  133. 133uint32_t uart1_isr, uart1_icr;
  134. 134
  135. 135void usart1_isr(void)
  136. 136{
  137. 137
  138. 138    /* Check if we were called because of RXNE. */
  139. 139    if (((USART_CR1(USART1) & USART_CR1_RXNEIE) != 0) &&
  140. 140        ((USART_ISR(USART1) & USART_ISR_RXNE) != 0)) {
  141. 141
  142. 142        /* Retrieve the data from the peripheral. */
  143. 143        // usart_recv(USART1);
  144. 144
  145. 145        pxMBFrameCBByteReceived();
  146. 146
  147. 147    }
  148. 148
  149. 149
  150. 150    /* Check if we were called because of TXE. */
  151. 151    if (((USART_CR1(USART1) & USART_CR1_TXEIE) != 0) &&
  152. 152        ((USART_ISR(USART1) & USART_ISR_TXE) != 0)) {
  153. 153
  154. 154        /* Put data into the transmit register. */
  155. 155        //usart_send(USART1, data);
  156. 156
  157. 157        pxMBFrameCBTransmitterEmpty();
  158. 158
  159. 159    }
  160. 160
  161. 161}
复制代码
porttimer
  1. 1#include "port.h"
  2. 2
  3. 3#include <libopencm3/cm3/nvic.h>
  4. 4#include <libopencm3/stm32/rcc.h>
  5. 5#include <libopencm3/stm32/timer.h>
  6. 6
  7. 7/* ----------------------- Modbus includes ----------------------------------*/
  8. 8#include "mb.h"
  9. 9#include "mbport.h"
  10. 10
  11. 11/* ----------------------- static functions ---------------------------------*/
  12. 12static void prvvTIMERExpiredISR( void );
  13. 13
  14. 14/* ----------------------- Start implementation -----------------------------*/
  15. 15BOOL
  16. 16xMBPortTimersInit( USHORT usTim1Timerout50us )
  17. 17{
  18. 18    rcc_periph_clock_enable(RCC_TIM2);
  19. 19    nvic_enable_irq(NVIC_TIM2_IRQ);
  20. 20    rcc_periph_reset_pulse(RST_TIM2);
  21. 21
  22. 22    timer_set_mode(TIM2, TIM_CR1_CKD_CK_INT, TIM_CR1_CMS_EDGE, TIM_CR1_DIR_UP);
  23. 23
  24. 24    timer_set_prescaler(TIM2, (rcc_apb1_frequency/ 20000));
  25. 25
  26. 26    timer_disable_preload(TIM2);
  27. 27    timer_continuous_mode(TIM2);
  28. 28
  29. 29    timer_set_period(TIM2, usTim1Timerout50us);
  30. 30    timer_enable_counter(TIM2);
  31. 31
  32. 32    timer_enable_irq(TIM2, TIM_DIER_UIE);
  33. 33
  34. 34    return TRUE;
  35. 35}
  36. 36
  37. 37
  38. 38inline void
  39. 39vMBPortTimersEnable(  )
  40. 40{
  41. 41    timer_set_counter(TIM2, 0);
  42. 42    timer_enable_counter(TIM2);
  43. 43}
  44. 44
  45. 45inline void
  46. 46vMBPortTimersDisable(  )
  47. 47{
  48. 48    timer_disable_counter(TIM2);
  49. 49}
  50. 50
  51. 51/* Create an ISR which is called whenever the timer has expired. This function
  52. 52 * must then call pxMBPortCBTimerExpired( ) to notify the protocol stack that
  53. 53 * the timer has expired.
  54. 54 */
  55. 55static void prvvTIMERExpiredISR( void )
  56. 56{
  57. 57    ( void )pxMBPortCBTimerExpired(  );
  58. 58}
  59. 59
  60. 60void
  61. 61vMBPortTimersDelay( USHORT usTimeOutMS )
  62. 62{
  63. 63    vTaskDelay(pdMS_TO_TICKS(usTimeOutMS));
  64. 64}
  65. 65
  66. 66void tim2_isr(void)
  67. 67{
  68. 68    if (timer_get_flag(TIM2, TIM_SR_UIF)) {
  69. 69
  70. 70        /* Clear compare interrupt flag. */
  71. 71        timer_clear_flag(TIM2, TIM_SR_UIF);
  72. 72
  73. 73        prvvTIMERExpiredISR();
  74. 74
  75. 75    }
  76. 76}
复制代码
开启定时器和中断,用于modbus时序控制;


2.2使用
在src目录新建 modbus_cb.h 和 modbus_cb.c 两个文件,实现寄存器、线圈的读写回调:
  1. 1/// CMD4
  2. 2eMBErrorCode eMBRegInputCB( UCHAR * pucRegBuffer, USHORT usAddress, USHORT usNRegs );
  3. 3
  4. 4/// CMD6、3、16
  5. 5eMBErrorCode eMBRegHoldingCB( UCHAR * pucRegBuffer, USHORT usAddress, USHORT usNRegs, eMBRegisterMode eMode );
  6. 6
  7. 7/// CMD1、5、15
  8. 8eMBErrorCode eMBRegCoilsCB( UCHAR * pucRegBuffer, USHORT usAddress, USHORT usNCoils, eMBRegisterMode eMode );
  9. 9
  10. 10/// CMD4
  11. 11eMBErrorCode eMBRegDiscreteCB( UCHAR * pucRegBuffer, USHORT usAddress, USHORT usNDiscrete );
复制代码



基本的实现示例如下:
  1.   1#include "modbus_cb.h"
  2.   2#include "stdbool.h"
  3.   3
  4.   4extern log(const char* fmt, ...);
  5.   5
  6.   6// 输入寄存器
  7.   7#define REG_INPUT_SIZE  32
  8.   8uint16_t REG_INPUT_BUF[REG_INPUT_SIZE];
  9.   9
  10. 10// 保持寄存器
  11. 11#define REG_HOLD_SIZE   32
  12. 12uint16_t REG_HOLD_BUF[REG_HOLD_SIZE];
  13. 13
  14. 14// 线圈寄存器
  15. 15#define REG_COILS_SIZE 16
  16. 16uint8_t REG_COILS_BUF[REG_COILS_SIZE];
  17. 17
  18. 18// 离散量
  19. 19#define REG_DISC_SIZE  8
  20. 20uint8_t REG_DISC_BUF[REG_DISC_SIZE];
  21. 21
  22. 22/// CMD4
  23. 23eMBErrorCode eMBRegInputCB( UCHAR * pucRegBuffer, USHORT usAddress, USHORT usNRegs )
  24. 24{
  25. 25    USHORT usRegIndex = usAddress - 1;
  26. 26
  27. 27    // 非法检测
  28. 28    if((usRegIndex + usNRegs) > REG_INPUT_SIZE)
  29. 29    {
  30. 30        return MB_ENOREG;
  31. 31    }
  32. 32
  33. 33    log(" CMD4, 寄存器输入.");
  34. 34
  35. 35    // 填充数据
  36. 36    REG_INPUT_BUF[0] = 0x01;
  37. 37    REG_INPUT_BUF[1] = 0x02;
  38. 38
  39. 39    // 循环读取
  40. 40    while ( usNRegs > 0 ) {
  41. 41        *pucRegBuffer++ = ( unsigned char )( REG_INPUT_BUF[usRegIndex] >> 8 );
  42. 42        *pucRegBuffer++ = ( unsigned char )( REG_INPUT_BUF[usRegIndex] & 0xFF );
  43. 43        usRegIndex++;
  44. 44        usNRegs--;
  45. 45    }
  46. 46
  47. 47    return MB_ENOERR;
  48. 48}
  49. 49
  50. 50/// CMD6、3、16
  51. 51eMBErrorCode eMBRegHoldingCB( UCHAR * pucRegBuffer, USHORT usAddress, USHORT usNRegs, eMBRegisterMode eMode )
  52. 52{
  53. 53    USHORT usRegIndex = usAddress - 1;  
  54. 54
  55. 55    // 非法检测
  56. 56    if((usRegIndex + usNRegs) > REG_HOLD_SIZE) {
  57. 57        return MB_ENOREG;
  58. 58    }
  59. 59
  60. 60    log(" CMD3,6,16, 保持寄存器读写.");
  61. 61
  62. 62    // 写寄存器
  63. 63    if (eMode == MB_REG_WRITE) {
  64. 64        while ( usNRegs > 0 ) {
  65. 65            uint16_t value;
  66. 66
  67. 67            value = (pucRegBuffer[0] << 8) | pucRegBuffer[1];
  68. 68
  69. 69            log("  写寄存器值:%d", value);
  70. 70
  71. 71            pucRegBuffer += 2;
  72. 72            usRegIndex++;
  73. 73            usNRegs--;
  74. 74
  75. 75        }
  76. 76
  77. 77    }
  78. 78    // 读寄存器
  79. 79    else {
  80. 80
  81. 81        log("  读寄存器.");
  82. 82
  83. 83        REG_HOLD_BUF[0] = 0x32;
  84. 84        REG_HOLD_BUF[1] = 0x33;
  85. 85
  86. 86        while ( usNRegs > 0 ) {
  87. 87            *pucRegBuffer++ = ( unsigned char )( REG_HOLD_BUF[usRegIndex] >> 8 );
  88. 88            *pucRegBuffer++ = ( unsigned char )( REG_HOLD_BUF[usRegIndex] & 0xFF );
  89. 89            usRegIndex++;
  90. 90            usNRegs--;
  91. 91        }
  92. 92    }
  93. 93
  94. 94    return MB_ENOERR;
  95. 95}
  96. 96
  97. 97/// CMD1、5、15
  98. 98eMBErrorCode eMBRegCoilsCB( UCHAR * pucRegBuffer, USHORT usAddress, USHORT usNCoils, eMBRegisterMode eMode )
  99. 99{
  100. 100
  101. 101    USHORT usRegIndex   = usAddress - 1;
  102. 102    USHORT usCoilGroups = ((usNCoils - 1) / 8 + 1);
  103. 103    UCHAR  ucStatus     = 0;
  104. 104    UCHAR  ucBits       = 0;
  105. 105    UCHAR  ucDisp       = 0;
  106. 106
  107. 107    // 非法检测
  108. 108    if ((usRegIndex + usNCoils) > REG_COILS_SIZE) {
  109. 109        return MB_ENOREG;
  110. 110    }
  111. 111
  112. 112    log("  CMD1,5,15, 线圈读写.");
  113. 113
  114. 114    // 写线圈
  115. 115    if (eMode == MB_REG_WRITE) {
  116. 116
  117. 117        while (usCoilGroups--) {
  118. 118
  119. 119            ucStatus = *pucRegBuffer++;
  120. 120            ucBits   = 8;
  121. 121
  122. 122            while((usNCoils) != 0 && (ucBits) != 0) {
  123. 123                bool flag = ucStatus & 0x01;
  124. 124
  125. 125                switch (usRegIndex) {
  126. 126
  127. 127                    case 0:
  128. 128                        log(" 线圈0 : %d", flag);//
  129. 129                    break;
  130. 130
  131. 131                    case 1:
  132. 132                        log(" 线圈1 : %d", flag);
  133. 133                    break;
  134. 134
  135. 135                    default:
  136. 136
  137. 137                    break;
  138. 138
  139. 139                }
  140. 140
  141. 141                usRegIndex++;
  142. 142                ucStatus >>= 1;
  143. 143                usNCoils--;
  144. 144                ucBits--;
  145. 145            }
  146. 146
  147. 147        }
  148. 148    }
  149. 149    // 读线圈
  150. 150    else {
  151. 151
  152. 152        REG_COILS_BUF[0]  = 1;
  153. 153        REG_COILS_BUF[1]  = 0;
  154. 154
  155. 155        while (usCoilGroups--) {
  156. 156            ucDisp = 0;
  157. 157            ucBits = 8;
  158. 158            ucStatus = 0;
  159. 159
  160. 160            while((usNCoils) != 0 && (ucBits) != 0) {
  161. 161                ucStatus |= (REG_COILS_BUF[usRegIndex++] << (ucDisp++));
  162. 162                usNCoils--;
  163. 163                ucBits--;
  164. 164            }
  165. 165
  166. 166            *pucRegBuffer++ = ucStatus;
  167. 167        }
  168. 168    }
  169. 169
  170. 170    return MB_ENOERR;
  171. 171}
  172. 172
  173. 173/// CMD4
  174. 174eMBErrorCode eMBRegDiscreteCB( UCHAR * pucRegBuffer, USHORT usAddress, USHORT usNDiscrete )
  175. 175{
  176. 176    USHORT usRegIndex   = usAddress - 1;
  177. 177    USHORT usCoilGroups = ((usNDiscrete - 1) / 8 + 1);
  178. 178    UCHAR  ucStatus     = 0;
  179. 179    UCHAR  ucBits       = 0;
  180. 180    UCHAR  ucDisp       = 0;
  181. 181
  182. 182    // 非法检测
  183. 183    if ((usRegIndex + usNDiscrete) > REG_DISC_SIZE) {
  184. 184        return MB_ENOREG;
  185. 185    }
  186. 186
  187. 187    log("  CMD4, 离散寄存器写入.");
  188. 188
  189. 189    // 读离散输入
  190. 190    while (usCoilGroups--) {
  191. 191        ucDisp = 0;
  192. 192        ucBits = 8;
  193. 193        ucStatus = 0;
  194. 194
  195. 195        while((usNDiscrete != 0) && (ucBits != 0))
  196. 196        {
  197. 197            switch (usRegIndex) {
  198. 198            case 0:
  199. 199                ucStatus = 0x10;
  200. 200                break;
  201. 201            }
  202. 202
  203. 203            usRegIndex++;
  204. 204            ucDisp++;
  205. 205            usNDiscrete--;
  206. 206            ucBits--;
  207. 207        }
  208. 208        *pucRegBuffer++ = ucStatus;
  209. 209    }
  210. 210
  211. 211        return MB_ENOERR;
  212. 212}
复制代码

在main中创建modbus任务:
  1. 1static void task_modbus_handle(void *param)
  2. 2{
  3. 3
  4. 4    eMBErrorCode    eStatus;
  5. 5
  6. 6    log("  task modbus start.");
  7. 7
  8. 8    eStatus = eMBInit( MB_RTU, 0x01, 0, 9600, MB_PAR_NONE );
  9. 9
  10. 10    /* Enable the Modbus Protocol Stack. */
  11. 11    eStatus = eMBEnable();
  12. 12
  13. 13    (void)eStatus;
  14. 14
  15. 15    for( ;; ) {
  16. 16        ( void )eMBPoll();
  17. 17        vTaskDelay(pdMS_TO_TICKS(10));
  18. 18    }
  19. 19
  20. 20}
复制代码


3 烧写测试
将开发板连接到USB转485模块,然后使用modbus poll程序进行测试:

微信图片_20230222160741.png

转载自: MakerInChina.cn



收藏 评论0 发布时间:2023-2-22 16:08

举报

0个回答

所属标签

相似分享

官网相关资源

关于
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
意法半导体官网
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
官方最新发布
STM32N6 AI生态系统
STM32MCU,MPU高性能GUI
ST ACEPACK电源模块
意法半导体生物传感器
STM32Cube扩展软件包
关注我们
st-img 微信公众号
st-img 手机版