使用Platformio平台的libopencm3开发框架来开发STM32G0,以下为FreeRTOS和FreeModbus库使用。
1 新建项目
在PIO的Home页面新建项目,项目名称freertos_modbus,选择开发板为 MonkeyPi_STM32_G070RB,开发框架选择libopencm3;
项目建立完成后在src目录下新建main.c主程序文件;
修改下载和调试方式,这里开发板使用的是DAPLink仿真器,因此修改platformio.ini文件如下:
1 2 upload_protocol = cmsis-dap debug_tool = cmsis-dap
2 编写程序 直接在之前的FreeRTOS工程上进行添加;
2.1 添加 freeModbus 库 从git仓库下载源码: https://github.com/cwalter-at/freemodbus
将下载的源码中的mobus文件夹放置到工程的lib目录下,然后在modbus目录新建library.json文件,内容如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 { "name" : "FreeModbus" , "version" : "master" , "repository" :{ "type" :"git" , "url" :"https://github.com/cwalter-at/freemodbus" }, "build" : { "flags" : [ "-Iascii" , "-Ifunctions" , "-Iinclude" , "-Irtu" , "-Itcp" ], "srcFilter" : [ "+<*>" ] } }
然后从FreeModbus源码中的 demo\BARE\port中复制文件到工程的src\modbus_port文件夹下,最后的文件夹结构如下:
2.2 移植
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 #include "mb.h" #include "mbport.h" #include "FreeRTOS.h" #include "task.h" static eMBEventType eQueuedEvent;static BOOL xEventInQueue;static uint32_t modbus_last_active_time = 0 ;uint32_t get_modbus_last_active_time (void ) { return modbus_last_active_time; } BOOL xMBPortEventInit ( void ) { xEventInQueue = FALSE; return TRUE; } BOOL xMBPortEventPost ( eMBEventType eEvent ) { xEventInQueue = TRUE; eQueuedEvent = eEvent; if (eEvent == EV_EXECUTE) { modbus_last_active_time = xTaskGetTickCount(); } return TRUE; } BOOL xMBPortEventGet ( eMBEventType * eEvent ) { BOOL xEventHappened = FALSE; if ( xEventInQueue ) { *eEvent = eQueuedEvent; xEventInQueue = FALSE; xEventHappened = TRUE; } return xEventHappened; }
这里使用RS485,因此需要对RS485使能端口进行配置,其他为串口的配置,然后在发送和接收中断时候调用modbus相关接口进行处理:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 #include "port.h" #include "FreeRTOS.h" #include "queue.h" #include <libopencm3/cm3/nvic.h> #include <libopencm3/stm32/usart.h> #include <libopencm3/stm32/rcc.h> #include <libopencm3/stm32/gpio.h> #include "mb.h" #include "mbport.h" xQueueHandle uart_queue; #define RS485_1_CLOCK RCC_GPIOB #define RS485_1_EN_PORT GPIOB #define RS485_1_EN_PIN GPIO8 static void rs485_delay (int n) { while (--n) { __asm__ volatile ("nop" ) ; } } static inline void rs485_1_rx_mode (void ) { gpio_clear(RS485_1_EN_PORT, RS485_1_EN_PIN); } static inline void rs485_1_tx_mode (void ) { gpio_set(RS485_1_EN_PORT, RS485_1_EN_PIN); } static inline void rs485_gpio_init (void ) { rcc_periph_clock_enable(RS485_1_CLOCK); gpio_mode_setup(RS485_1_EN_PORT, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, RS485_1_EN_PIN); rs485_1_rx_mode(); } void vMBPortSerialEnable ( BOOL xRxEnable, BOOL xTxEnable ) { if (xRxEnable) { rs485_delay(10000 ); rs485_1_rx_mode(); rs485_delay(10000 ); usart_enable_rx_interrupt(USART1); } else { usart_disable_rx_interrupt(USART1); } if (xTxEnable) { rs485_delay(10000 ); rs485_1_tx_mode(); rs485_delay(10000 ); usart_enable_tx_interrupt(USART1); } else { usart_disable_tx_interrupt(USART1); } } BOOL xMBPortSerialInit ( UCHAR ucPORT, ULONG ulBaudRate, UCHAR ucDataBits, eMBParity eParity ) { nvic_enable_irq(NVIC_USART1_IRQ); rcc_periph_clock_enable(RCC_GPIOB); gpio_mode_setup(GPIOB, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO6 | GPIO7); gpio_set_af(GPIOB, GPIO_AF0, GPIO6 | GPIO7); rcc_periph_clock_enable(RCC_USART1); usart_set_baudrate(USART1, ulBaudRate); usart_set_databits(USART1, ucDataBits); usart_set_stopbits(USART1, USART_STOPBITS_1); usart_set_mode(USART1, USART_MODE_TX_RX); switch (eParity) { case MB_PAR_ODD: usart_set_parity(USART1, USART_PARITY_ODD); break ; case MB_PAR_EVEN: usart_set_parity(USART1, USART_PARITY_EVEN); break ; default : usart_set_parity(USART1, USART_PARITY_NONE); break ; } usart_set_flow_control(USART1, USART_FLOWCONTROL_NONE); usart_enable(USART1); rs485_gpio_init(); return TRUE; } BOOL xMBPortSerialPutByte ( CHAR ucByte ) { usart_send_blocking(USART1, (uint16_t ) ucByte); return TRUE; } BOOL xMBPortSerialGetByte ( CHAR * pucByte ) { *pucByte = usart_recv(USART1); return TRUE; } uint32_t uart1_isr, uart1_icr;void usart1_isr (void ) { if (((USART_CR1(USART1) & USART_CR1_RXNEIE) != 0 ) && ((USART_ISR(USART1) & USART_ISR_RXNE) != 0 )) { pxMBFrameCBByteReceived(); } if (((USART_CR1(USART1) & USART_CR1_TXEIE) != 0 ) && ((USART_ISR(USART1) & USART_ISR_TXE) != 0 )) { pxMBFrameCBTransmitterEmpty(); } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 #include "port.h" #include <libopencm3/cm3/nvic.h> #include <libopencm3/stm32/rcc.h> #include <libopencm3/stm32/timer.h> #include "mb.h" #include "mbport.h" static void prvvTIMERExpiredISR ( void ) ;BOOL xMBPortTimersInit ( USHORT usTim1Timerout50us ) { rcc_periph_clock_enable(RCC_TIM2); nvic_enable_irq(NVIC_TIM2_IRQ); rcc_periph_reset_pulse(RST_TIM2); timer_set_mode(TIM2, TIM_CR1_CKD_CK_INT, TIM_CR1_CMS_EDGE, TIM_CR1_DIR_UP); timer_set_prescaler(TIM2, (rcc_apb1_frequency/ 20000 )); timer_disable_preload(TIM2); timer_continuous_mode(TIM2); timer_set_period(TIM2, usTim1Timerout50us); timer_enable_counter(TIM2); timer_enable_irq(TIM2, TIM_DIER_UIE); return TRUE; } inline void vMBPortTimersEnable ( ) { timer_set_counter(TIM2, 0 ); timer_enable_counter(TIM2); } inline void vMBPortTimersDisable ( ) { timer_disable_counter(TIM2); } static void prvvTIMERExpiredISR ( void ) { ( void )pxMBPortCBTimerExpired( ); } void vMBPortTimersDelay ( USHORT usTimeOutMS ) { vTaskDelay(pdMS_TO_TICKS(usTimeOutMS)); } void tim2_isr (void ) { if (timer_get_flag(TIM2, TIM_SR_UIF)) { timer_clear_flag(TIM2, TIM_SR_UIF); prvvTIMERExpiredISR(); } }
开启定时器和中断,用于modbus时序控制;
2.3 使用 在src目录新建 modbus_cb.h 和 modbus_cb.c 两个文件,实现寄存器、线圈的读写回调:
1 2 3 4 5 6 7 8 9 10 11 eMBErrorCode eMBRegInputCB ( UCHAR * pucRegBuffer, USHORT usAddress, USHORT usNRegs ) ; eMBErrorCode eMBRegHoldingCB ( UCHAR * pucRegBuffer, USHORT usAddress, USHORT usNRegs, eMBRegisterMode eMode ) ; eMBErrorCode eMBRegCoilsCB ( UCHAR * pucRegBuffer, USHORT usAddress, USHORT usNCoils, eMBRegisterMode eMode ) ; eMBErrorCode eMBRegDiscreteCB ( UCHAR * pucRegBuffer, USHORT usAddress, USHORT usNDiscrete ) ;
基本的实现示例如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 #include "modbus_cb.h" #include "stdbool.h" extern log (const char * fmt, ...) ;#define REG_INPUT_SIZE 32 uint16_t REG_INPUT_BUF[REG_INPUT_SIZE];#define REG_HOLD_SIZE 32 uint16_t REG_HOLD_BUF[REG_HOLD_SIZE];#define REG_COILS_SIZE 16 uint8_t REG_COILS_BUF[REG_COILS_SIZE];#define REG_DISC_SIZE 8 uint8_t REG_DISC_BUF[REG_DISC_SIZE];eMBErrorCode eMBRegInputCB ( UCHAR * pucRegBuffer, USHORT usAddress, USHORT usNRegs ) { USHORT usRegIndex = usAddress - 1 ; if ((usRegIndex + usNRegs) > REG_INPUT_SIZE) { return MB_ENOREG; } log (" CMD4, 寄存器输入." ); REG_INPUT_BUF[0 ] = 0x01 ; REG_INPUT_BUF[1 ] = 0x02 ; while ( usNRegs > 0 ) { *pucRegBuffer++ = ( unsigned char )( REG_INPUT_BUF[usRegIndex] >> 8 ); *pucRegBuffer++ = ( unsigned char )( REG_INPUT_BUF[usRegIndex] & 0xFF ); usRegIndex++; usNRegs--; } return MB_ENOERR; } eMBErrorCode eMBRegHoldingCB ( UCHAR * pucRegBuffer, USHORT usAddress, USHORT usNRegs, eMBRegisterMode eMode ) { USHORT usRegIndex = usAddress - 1 ; if ((usRegIndex + usNRegs) > REG_HOLD_SIZE) { return MB_ENOREG; } log (" CMD3,6,16, 保持寄存器读写." ); if (eMode == MB_REG_WRITE) { while ( usNRegs > 0 ) { uint16_t value; value = (pucRegBuffer[0 ] << 8 ) | pucRegBuffer[1 ]; log (" 写寄存器值:%d" , value); pucRegBuffer += 2 ; usRegIndex++; usNRegs--; } } else { log (" 读寄存器." ); REG_HOLD_BUF[0 ] = 0x32 ; REG_HOLD_BUF[1 ] = 0x33 ; while ( usNRegs > 0 ) { *pucRegBuffer++ = ( unsigned char )( REG_HOLD_BUF[usRegIndex] >> 8 ); *pucRegBuffer++ = ( unsigned char )( REG_HOLD_BUF[usRegIndex] & 0xFF ); usRegIndex++; usNRegs--; } } return MB_ENOERR; } eMBErrorCode eMBRegCoilsCB ( UCHAR * pucRegBuffer, USHORT usAddress, USHORT usNCoils, eMBRegisterMode eMode ) { USHORT usRegIndex = usAddress - 1 ; USHORT usCoilGroups = ((usNCoils - 1 ) / 8 + 1 ); UCHAR ucStatus = 0 ; UCHAR ucBits = 0 ; UCHAR ucDisp = 0 ; if ((usRegIndex + usNCoils) > REG_COILS_SIZE) { return MB_ENOREG; } log (" CMD1,5,15, 线圈读写." ); if (eMode == MB_REG_WRITE) { while (usCoilGroups--) { ucStatus = *pucRegBuffer++; ucBits = 8 ; while ((usNCoils) != 0 && (ucBits) != 0 ) { bool flag = ucStatus & 0x01 ; switch (usRegIndex) { case 0 : log (" 线圈0 : %d" , flag); break ; case 1 : log (" 线圈1 : %d" , flag); break ; default : break ; } usRegIndex++; ucStatus >>= 1 ; usNCoils--; ucBits--; } } } else { REG_COILS_BUF[0 ] = 1 ; REG_COILS_BUF[1 ] = 0 ; while (usCoilGroups--) { ucDisp = 0 ; ucBits = 8 ; ucStatus = 0 ; while ((usNCoils) != 0 && (ucBits) != 0 ) { ucStatus |= (REG_COILS_BUF[usRegIndex++] << (ucDisp++)); usNCoils--; ucBits--; } *pucRegBuffer++ = ucStatus; } } return MB_ENOERR; } eMBErrorCode eMBRegDiscreteCB ( UCHAR * pucRegBuffer, USHORT usAddress, USHORT usNDiscrete ) { USHORT usRegIndex = usAddress - 1 ; USHORT usCoilGroups = ((usNDiscrete - 1 ) / 8 + 1 ); UCHAR ucStatus = 0 ; UCHAR ucBits = 0 ; UCHAR ucDisp = 0 ; if ((usRegIndex + usNDiscrete) > REG_DISC_SIZE) { return MB_ENOREG; } log (" CMD4, 离散寄存器写入." ); while (usCoilGroups--) { ucDisp = 0 ; ucBits = 8 ; ucStatus = 0 ; while ((usNDiscrete != 0 ) && (ucBits != 0 )) { switch (usRegIndex) { case 0 : ucStatus = 0x10 ; break ; } usRegIndex++; ucDisp++; usNDiscrete--; ucBits--; } *pucRegBuffer++ = ucStatus; } return MB_ENOERR; }
在main中创建modbus任务:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 static void task_modbus_handle (void *param) { eMBErrorCode eStatus; log (" task modbus start." ); eStatus = eMBInit( MB_RTU, 0x01 , 0 , 9600 , MB_PAR_NONE ); eStatus = eMBEnable(); (void )eStatus; for ( ;; ) { ( void )eMBPoll(); vTaskDelay(pdMS_TO_TICKS(10 )); } }
3 烧写测试 将开发板连接到USB转485模块,然后使用modbus poll程序进行测试: