1.新建项目
在PIO的Home页面新建项目,项目名称freertos_modbus,选择开发板为 MonkeyPi_STM32_G070RB,开发框架选择libopencm3; - 1upload_protocol = cmsis-dap
- 2debug_tool = cmsis-dap
复制代码
2 编写程序2.1 添加 freeModbus 库然后从FreeModbus源码中的 demo\BARE\port中复制文件到工程的src\modbus_port文件夹下,最后的文件夹结构如下: 这里使用RS485,因此需要对RS485使能端口进行配置,其他为串口的配置,然后在发送和接收中断时候调用modbus相关接口进行处理:
- 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文件夹下,最后的文件夹结构如下:
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 <libopencm3/cm3/nvic.h>
- 7#include <libopencm3/stm32/usart.h>
- 8#include <libopencm3/stm32/rcc.h>
- 9#include <libopencm3/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 <libopencm3/cm3/nvic.h>
- 4#include <libopencm3/stm32/rcc.h>
- 5#include <libopencm3/stm32/timer.h>
- 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.2使用
在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程序进行测试:
转载自: MakerInChina.cn
|