使用Platformio平臺的libopencm3開發框架來開發STM32G0,以下為FreeRTOS和FreeModbus庫使用。
1 新建項目
- 建立freertos_modbus項目
在PIO的Home頁面新建項目,項目名稱freertos_modbus,選擇開發板為 MonkeyPi_STM32_G070RB,開發框架選擇libopencm3;
1upload_protocol = cmsis-dap
2debug_tool = cmsis-dap
2 編寫程序
直接在之前的FreeRTOS工程上進行添加;
2.1 添加 freeModbus 庫
從git倉庫下載源碼: https://github.com/cwalter-at/freemodbus
將下載的源碼中的mobus文件夾放置到工程的lib目錄下,然后在modbus目錄新建library.json文件,內容如下:
1{
2 "name": "FreeModbus",
3 "version": "master",
4 "repository":{
5 "type":"git",
6 "url":"https://github.com/cwalter-at/freemodbus"
7 },
8 "build": {
9 "flags": [
10 "-Iascii",
11 "-Ifunctions",
12 "-Iinclude",
13 "-Irtu",
14 "-Itcp"
15 ],
16 "srcFilter": [
17 "+<*>"
18 ]
19 }
20}
然后從FreeModbus源碼中的 demo\\BARE\\port中復制文件到工程的src\\modbus_port文件夾下,最后的文件夾結構如下:
image-20220928231915800
2.2 移植
- portevent:
1/* ----------------------- Modbus includes ----------------------------------*/
2#include "mb.h"
3#include "mbport.h"
4#include "FreeRTOS.h"
5#include "task.h"
6
7/* ----------------------- Variables ----------------------------------------*/
8static eMBEventType eQueuedEvent;
9static BOOL xEventInQueue;
10static uint32_t modbus_last_active_time = 0;
11
12uint32_t get_modbus_last_active_time(void)
13{
14 return modbus_last_active_time;
15}
16
17/* ----------------------- Start implementation -----------------------------*/
18BOOL
19xMBPortEventInit( void )
20{
21 xEventInQueue = FALSE;
22 return TRUE;
23}
24
25BOOL
26xMBPortEventPost( eMBEventType eEvent )
27{
28 xEventInQueue = TRUE;
29 eQueuedEvent = eEvent;
30
31 if (eEvent == EV_EXECUTE) {
32 modbus_last_active_time = xTaskGetTickCount();
33 }
34 return TRUE;
35}
36
37BOOL
38xMBPortEventGet( eMBEventType * eEvent )
39{
40 BOOL xEventHappened = FALSE;
41
42 if( xEventInQueue )
43 {
44 *eEvent = eQueuedEvent;
45 xEventInQueue = FALSE;
46 xEventHappened = TRUE;
47 }
48 return xEventHappened;
49}
- portserial
這里使用RS485,因此需要對RS485使能端口進行配置,其他為串口的配置,然后在發送和接收中斷時候調用modbus相關接口進行處理:
1#include "port.h"
2
3#include "FreeRTOS.h"
4#include "queue.h"
5
6#include 3/cm3/nvic.h>
7#include 3/stm32/usart.h>
8#include 3/stm32/rcc.h>
9#include 3/stm32/gpio.h>
10
11/* ----------------------- Modbus includes ----------------------------------*/
12#include "mb.h"
13#include "mbport.h"
14
15/* ----------------------- static functions ---------------------------------*/
16
17xQueueHandle uart_queue;
18
19#define RS485_1_CLOCK RCC_GPIOB
20#define RS485_1_EN_PORT GPIOB
21#define RS485_1_EN_PIN GPIO8
22
23static void rs485_delay(int n)
24{
25 while (--n) {
26 __asm__ volatile ("nop");
27 }
28}
29
30static inline void rs485_1_rx_mode(void)
31{
32 gpio_clear(RS485_1_EN_PORT, RS485_1_EN_PIN);
33}
34
35static inline void rs485_1_tx_mode(void)
36{
37 gpio_set(RS485_1_EN_PORT, RS485_1_EN_PIN);
38}
39
40static inline void rs485_gpio_init(void)
41{
42 rcc_periph_clock_enable(RS485_1_CLOCK);
43 gpio_mode_setup(RS485_1_EN_PORT, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, RS485_1_EN_PIN);
44
45 rs485_1_rx_mode();
46}
47
48/* ----------------------- Start implementation -----------------------------*/
49void
50vMBPortSerialEnable( BOOL xRxEnable, BOOL xTxEnable )
51{
52 /* If xRXEnable enable serial receive interrupts. If xTxENable enable
53 * transmitter empty interrupts.
54 */
55 if (xRxEnable) {
56 rs485_delay(10000);
57 rs485_1_rx_mode();
58 rs485_delay(10000);
59 usart_enable_rx_interrupt(USART1);
60 }
61 else {
62 usart_disable_rx_interrupt(USART1);
63 }
64
65 if (xTxEnable) {
66 rs485_delay(10000);
67 rs485_1_tx_mode();
68 rs485_delay(10000);
69 usart_enable_tx_interrupt(USART1);
70 }
71 else {
72 usart_disable_tx_interrupt(USART1);
73
74 }
75}
76
77BOOL
78xMBPortSerialInit( UCHAR ucPORT, ULONG ulBaudRate, UCHAR ucDataBits, eMBParity eParity )
79{
80 nvic_enable_irq(NVIC_USART1_IRQ);
81
82 rcc_periph_clock_enable(RCC_GPIOB);
83 gpio_mode_setup(GPIOB, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO6 | GPIO7);
84 gpio_set_af(GPIOB, GPIO_AF0, GPIO6 | GPIO7);
85
86 rcc_periph_clock_enable(RCC_USART1);
87
88 /* Set up USART/UART parameters using the libopencm3 helper functions */
89 usart_set_baudrate(USART1, ulBaudRate);
90 usart_set_databits(USART1, ucDataBits);
91 usart_set_stopbits(USART1, USART_STOPBITS_1);
92 usart_set_mode(USART1, USART_MODE_TX_RX);
93
94 switch (eParity) {
95 case MB_PAR_ODD:
96 usart_set_parity(USART1, USART_PARITY_ODD);
97 break;
98 case MB_PAR_EVEN:
99 usart_set_parity(USART1, USART_PARITY_EVEN);
100 break;
101 default:
102 usart_set_parity(USART1, USART_PARITY_NONE);
103 break;
104 }
105
106 usart_set_flow_control(USART1, USART_FLOWCONTROL_NONE);
107
108 usart_enable(USART1);
109
110 rs485_gpio_init();
111
112 return TRUE;
113}
114
115BOOL
116xMBPortSerialPutByte( CHAR ucByte )
117{
118
119 usart_send_blocking(USART1, (uint16_t) ucByte);
120
121 return TRUE;
122}
123
124BOOL
125xMBPortSerialGetByte( CHAR * pucByte )
126{
127 *pucByte = usart_recv(USART1);
128
129 return TRUE;
130}
131
132
133uint32_t uart1_isr, uart1_icr;
134
135void usart1_isr(void)
136{
137
138 /* Check if we were called because of RXNE. */
139 if (((USART_CR1(USART1) & USART_CR1_RXNEIE) != 0) &&
140 ((USART_ISR(USART1) & USART_ISR_RXNE) != 0)) {
141
142 /* Retrieve the data from the peripheral. */
143 // usart_recv(USART1);
144
145 pxMBFrameCBByteReceived();
146
147 }
148
149
150 /* Check if we were called because of TXE. */
151 if (((USART_CR1(USART1) & USART_CR1_TXEIE) != 0) &&
152 ((USART_ISR(USART1) & USART_ISR_TXE) != 0)) {
153
154 /* Put data into the transmit register. */
155 //usart_send(USART1, data);
156
157 pxMBFrameCBTransmitterEmpty();
158
159 }
160
161}
- porttimer
1#include "port.h"
2
3#include
4#include
5#include
6
7/* ----------------------- Modbus includes ----------------------------------*/
8#include "mb.h"
9#include "mbport.h"
10
11/* ----------------------- static functions ---------------------------------*/
12static void prvvTIMERExpiredISR( void );
13
14/* ----------------------- Start implementation -----------------------------*/
15BOOL
16xMBPortTimersInit( USHORT usTim1Timerout50us )
17{
18 rcc_periph_clock_enable(RCC_TIM2);
19 nvic_enable_irq(NVIC_TIM2_IRQ);
20 rcc_periph_reset_pulse(RST_TIM2);
21
22 timer_set_mode(TIM2, TIM_CR1_CKD_CK_INT, TIM_CR1_CMS_EDGE, TIM_CR1_DIR_UP);
23
24 timer_set_prescaler(TIM2, (rcc_apb1_frequency/ 20000));
25
26 timer_disable_preload(TIM2);
27 timer_continuous_mode(TIM2);
28
29 timer_set_period(TIM2, usTim1Timerout50us);
30 timer_enable_counter(TIM2);
31
32 timer_enable_irq(TIM2, TIM_DIER_UIE);
33
34 return TRUE;
35}
36
37
38inline void
39vMBPortTimersEnable( )
40{
41 timer_set_counter(TIM2, 0);
42 timer_enable_counter(TIM2);
43}
44
45inline void
46vMBPortTimersDisable( )
47{
48 timer_disable_counter(TIM2);
49}
50
51/* Create an ISR which is called whenever the timer has expired. This function
52 * must then call pxMBPortCBTimerExpired( ) to notify the protocol stack that
53 * the timer has expired.
54 */
55static void prvvTIMERExpiredISR( void )
56{
57 ( void )pxMBPortCBTimerExpired( );
58}
59
60void
61vMBPortTimersDelay( USHORT usTimeOutMS )
62{
63 vTaskDelay(pdMS_TO_TICKS(usTimeOutMS));
64}
65
66void tim2_isr(void)
67{
68 if (timer_get_flag(TIM2, TIM_SR_UIF)) {
69
70 /* Clear compare interrupt flag. */
71 timer_clear_flag(TIM2, TIM_SR_UIF);
72
73 prvvTIMERExpiredISR();
74
75 }
76}
開啟定時器和中斷,用于modbus時序控制;
2.3 使用
在src目錄新建 modbus_cb.h 和 modbus_cb.c 兩個文件,實現寄存器、線圈的讀寫回調:
1/// CMD4
2eMBErrorCode eMBRegInputCB( UCHAR * pucRegBuffer, USHORT usAddress, USHORT usNRegs );
3
4/// CMD6、3、16
5eMBErrorCode eMBRegHoldingCB( UCHAR * pucRegBuffer, USHORT usAddress, USHORT usNRegs, eMBRegisterMode eMode );
6
7/// CMD1、5、15
8eMBErrorCode eMBRegCoilsCB( UCHAR * pucRegBuffer, USHORT usAddress, USHORT usNCoils, eMBRegisterMode eMode );
9
10/// CMD4
11eMBErrorCode eMBRegDiscreteCB( UCHAR * pucRegBuffer, USHORT usAddress, USHORT usNDiscrete );
基本的實現示例如下:
1#include "modbus_cb.h"
2#include "stdbool.h"
3
4extern log(const char* fmt, ...);
5
6// 輸入寄存器
7#define REG_INPUT_SIZE 32
8uint16_t REG_INPUT_BUF[REG_INPUT_SIZE];
9
10// 保持寄存器
11#define REG_HOLD_SIZE 32
12uint16_t REG_HOLD_BUF[REG_HOLD_SIZE];
13
14// 線圈寄存器
15#define REG_COILS_SIZE 16
16uint8_t REG_COILS_BUF[REG_COILS_SIZE];
17
18// 離散量
19#define REG_DISC_SIZE 8
20uint8_t REG_DISC_BUF[REG_DISC_SIZE];
21
22/// CMD4
23eMBErrorCode eMBRegInputCB( UCHAR * pucRegBuffer, USHORT usAddress, USHORT usNRegs )
24{
25 USHORT usRegIndex = usAddress - 1;
26
27 // 非法檢測
28 if((usRegIndex + usNRegs) > REG_INPUT_SIZE)
29 {
30 return MB_ENOREG;
31 }
32
33 log(" CMD4, 寄存器輸入.");
34
35 // 填充數據
36 REG_INPUT_BUF[0] = 0x01;
37 REG_INPUT_BUF[1] = 0x02;
38
39 // 循環讀取
40 while ( usNRegs > 0 ) {
41 *pucRegBuffer++ = ( unsigned char )( REG_INPUT_BUF[usRegIndex] >> 8 );
42 *pucRegBuffer++ = ( unsigned char )( REG_INPUT_BUF[usRegIndex] & 0xFF );
43 usRegIndex++;
44 usNRegs--;
45 }
46
47 return MB_ENOERR;
48}
49
50/// CMD6、3、16
51eMBErrorCode eMBRegHoldingCB( UCHAR * pucRegBuffer, USHORT usAddress, USHORT usNRegs, eMBRegisterMode eMode )
52{
53 USHORT usRegIndex = usAddress - 1;
54
55 // 非法檢測
56 if((usRegIndex + usNRegs) > REG_HOLD_SIZE) {
57 return MB_ENOREG;
58 }
59
60 log(" CMD3,6,16, 保持寄存器讀寫.");
61
62 // 寫寄存器
63 if (eMode == MB_REG_WRITE) {
64 while ( usNRegs > 0 ) {
65 uint16_t value;
66
67 value = (pucRegBuffer[0] << 8) | pucRegBuffer[1];
68
69 log(" 寫寄存器值:%d", value);
70
71 pucRegBuffer += 2;
72 usRegIndex++;
73 usNRegs--;
74
75 }
76
77 }
78 // 讀寄存器
79 else {
80
81 log(" 讀寄存器.");
82
83 REG_HOLD_BUF[0] = 0x32;
84 REG_HOLD_BUF[1] = 0x33;
85
86 while ( usNRegs > 0 ) {
87 *pucRegBuffer++ = ( unsigned char )( REG_HOLD_BUF[usRegIndex] >> 8 );
88 *pucRegBuffer++ = ( unsigned char )( REG_HOLD_BUF[usRegIndex] & 0xFF );
89 usRegIndex++;
90 usNRegs--;
91 }
92 }
93
94 return MB_ENOERR;
95}
96
97/// CMD1、5、15
98eMBErrorCode eMBRegCoilsCB( UCHAR * pucRegBuffer, USHORT usAddress, USHORT usNCoils, eMBRegisterMode eMode )
99{
100
101 USHORT usRegIndex = usAddress - 1;
102 USHORT usCoilGroups = ((usNCoils - 1) / 8 + 1);
103 UCHAR ucStatus = 0;
104 UCHAR ucBits = 0;
105 UCHAR ucDisp = 0;
106
107 // 非法檢測
108 if ((usRegIndex + usNCoils) > REG_COILS_SIZE) {
109 return MB_ENOREG;
110 }
111
112 log(" CMD1,5,15, 線圈讀寫.");
113
114 // 寫線圈
115 if (eMode == MB_REG_WRITE) {
116
117 while (usCoilGroups--) {
118
119 ucStatus = *pucRegBuffer++;
120 ucBits = 8;
121
122 while((usNCoils) != 0 && (ucBits) != 0) {
123 bool flag = ucStatus & 0x01;
124
125 switch (usRegIndex) {
126
127 case 0:
128 log(" 線圈0 : %d", flag);//
129 break;
130
131 case 1:
132 log(" 線圈1 : %d", flag);
133 break;
134
135 default:
136
137 break;
138
139 }
140
141 usRegIndex++;
142 ucStatus >>= 1;
143 usNCoils--;
144 ucBits--;
145 }
146
147 }
148 }
149 // 讀線圈
150 else {
151
152 REG_COILS_BUF[0] = 1;
153 REG_COILS_BUF[1] = 0;
154
155 while (usCoilGroups--) {
156 ucDisp = 0;
157 ucBits = 8;
158 ucStatus = 0;
159
160 while((usNCoils) != 0 && (ucBits) != 0) {
161 ucStatus |= (REG_COILS_BUF[usRegIndex++] << (ucDisp++));
162 usNCoils--;
163 ucBits--;
164 }
165
166 *pucRegBuffer++ = ucStatus;
167 }
168 }
169
170 return MB_ENOERR;
171}
172
173/// CMD4
174eMBErrorCode eMBRegDiscreteCB( UCHAR * pucRegBuffer, USHORT usAddress, USHORT usNDiscrete )
175{
176 USHORT usRegIndex = usAddress - 1;
177 USHORT usCoilGroups = ((usNDiscrete - 1) / 8 + 1);
178 UCHAR ucStatus = 0;
179 UCHAR ucBits = 0;
180 UCHAR ucDisp = 0;
181
182 // 非法檢測
183 if ((usRegIndex + usNDiscrete) > REG_DISC_SIZE) {
184 return MB_ENOREG;
185 }
186
187 log(" CMD4, 離散寄存器寫入.");
188
189 // 讀離散輸入
190 while (usCoilGroups--) {
191 ucDisp = 0;
192 ucBits = 8;
193 ucStatus = 0;
194
195 while((usNDiscrete != 0) && (ucBits != 0))
196 {
197 switch (usRegIndex) {
198 case 0:
199 ucStatus = 0x10;
200 break;
201 }
202
203 usRegIndex++;
204 ucDisp++;
205 usNDiscrete--;
206 ucBits--;
207 }
208 *pucRegBuffer++ = ucStatus;
209 }
210
211 return MB_ENOERR;
212}
在main中創建modbus任務:
1static void task_modbus_handle(void *param)
2{
3
4 eMBErrorCode eStatus;
5
6 log(" task modbus start.");
7
8 eStatus = eMBInit( MB_RTU, 0x01, 0, 9600, MB_PAR_NONE );
9
10 /* Enable the Modbus Protocol Stack. */
11 eStatus = eMBEnable();
12
13 (void)eStatus;
14
15 for( ;; ) {
16 ( void )eMBPoll();
17 vTaskDelay(pdMS_TO_TICKS(10));
18 }
19
20}
3 燒寫測試
將開發板連接到USB轉485模塊,然后使用modbus poll程序進行測試:
image-20220928232358657
-
MODBUS
+關注
關注
28文章
1805瀏覽量
76986 -
STM32
+關注
關注
2270文章
10900瀏覽量
355926 -
開發板
+關注
關注
25文章
5047瀏覽量
97440 -
FreeRTOS
+關注
關注
12文章
484瀏覽量
62166 -
FreeModbus
+關注
關注
0文章
16瀏覽量
4467
發布評論請先 登錄
相關推薦
評論