STM32G0开发笔记-Platformio+libopencm3-FreeRTOS和FreeModbus库使用
创始人
2024-01-26 02:53:24
0

title: STM32G0开发笔记-Platformio+libopencm3-FreeRTOS和FreeModbus库使用
tags:

  • STM32
  • MCU
  • STM32G070
  • libopencm3
  • MonkeyPi
  • FreeRTOS
  • Modbus
    categories:
    • STM32
      date: 2022-9-11 19:52:05

[原文:makerinchina.cn]

使用Platformio平台的libopencm3开发框架来开发STM32G0,以下为FreeRTOS和FreeModbus库使用。

1 新建项目

  • 建立freertos_modbus项目

在PIO的Home页面新建项目,项目名称freertos_modbus,选择开发板为 MonkeyPi_STM32_G070RB,开发框架选择libopencm3;

  • 项目建立完成后在src目录下新建main.c主程序文件;
  • 修改下载和调试方式,这里开发板使用的是DAPLink仿真器,因此修改platformio.ini文件如下:
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文件,内容如下:

{"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 移植
  • portevent:
/* ----------------------- Modbus includes ----------------------------------*/
#include "mb.h"
#include "mbport.h"
#include "FreeRTOS.h"
#include "task.h"/* ----------------------- Variables ----------------------------------------*/
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;
}/* ----------------------- Start implementation -----------------------------*/
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;
}
  • portserial

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

#include "port.h"#include "FreeRTOS.h"
#include "queue.h"#include 
#include 
#include 
#include /* ----------------------- Modbus includes ----------------------------------*/
#include "mb.h"
#include "mbport.h"/* ----------------------- static functions ---------------------------------*/xQueueHandle uart_queue;#define RS485_1_CLOCK		RCC_GPIOB
#define RS485_1_EN_PORT		GPIOB
#define RS485_1_EN_PIN		GPIO8static 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();
}/* ----------------------- Start implementation -----------------------------*/
void
vMBPortSerialEnable( BOOL xRxEnable, BOOL xTxEnable )
{/* If xRXEnable enable serial receive interrupts. If xTxENable enable* transmitter empty interrupts.*/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);/* Set up USART/UART parameters using the libopencm3 helper functions */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)
{/* Check if we were called because of RXNE. */if (((USART_CR1(USART1) & USART_CR1_RXNEIE) != 0) &&((USART_ISR(USART1) & USART_ISR_RXNE) != 0)) {/* Retrieve the data from the peripheral. */// usart_recv(USART1);pxMBFrameCBByteReceived();}/* Check if we were called because of TXE. */if (((USART_CR1(USART1) & USART_CR1_TXEIE) != 0) &&((USART_ISR(USART1) & USART_ISR_TXE) != 0)) {/* Put data into the transmit register. *///usart_send(USART1, data);pxMBFrameCBTransmitterEmpty();}}
  • porttimer
#include "port.h"#include 
#include 
#include /* ----------------------- Modbus includes ----------------------------------*/
#include "mb.h"
#include "mbport.h"/* ----------------------- static functions ---------------------------------*/
static void prvvTIMERExpiredISR( void );/* ----------------------- Start implementation -----------------------------*/
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);
}/* Create an ISR which is called whenever the timer has expired. This function* must then call pxMBPortCBTimerExpired( ) to notify the protocol stack that* the timer has expired.*/
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)) {/* Clear compare interrupt flag. */timer_clear_flag(TIM2, TIM_SR_UIF);prvvTIMERExpiredISR();}
}

开启定时器和中断,用于modbus时序控制;

2.3 使用

在src目录新建 modbus_cb.h 和 modbus_cb.c 两个文件,实现寄存器、线圈的读写回调:

/// CMD4
eMBErrorCode eMBRegInputCB( UCHAR * pucRegBuffer, USHORT usAddress, USHORT usNRegs );/// CMD6、3、16
eMBErrorCode eMBRegHoldingCB( UCHAR * pucRegBuffer, USHORT usAddress, USHORT usNRegs, eMBRegisterMode eMode );/// CMD1、5、15
eMBErrorCode eMBRegCoilsCB( UCHAR * pucRegBuffer, USHORT usAddress, USHORT usNCoils, eMBRegisterMode eMode );/// CMD4
eMBErrorCode eMBRegDiscreteCB( UCHAR * pucRegBuffer, USHORT usAddress, USHORT usNDiscrete );

基本的实现示例如下:

#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];/// CMD4
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;
}/// CMD6、3、16
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;
}/// CMD1、5、15
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;
}/// CMD4
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任务:

static void task_modbus_handle(void *param)
{eMBErrorCode    eStatus;log("  task modbus start.");eStatus = eMBInit( MB_RTU, 0x01, 0, 9600, MB_PAR_NONE );/* Enable the Modbus Protocol Stack. */eStatus = eMBEnable();(void)eStatus;for( ;; ) {( void )eMBPoll();vTaskDelay(pdMS_TO_TICKS(10));}}

3 烧写测试

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

在这里插入图片描述

相关内容

热门资讯

喜欢穿一身黑的男生性格(喜欢穿... 今天百科达人给各位分享喜欢穿一身黑的男生性格的知识,其中也会对喜欢穿一身黑衣服的男人人好相处吗进行解...
发春是什么意思(思春和发春是什... 本篇文章极速百科给大家谈谈发春是什么意思,以及思春和发春是什么意思对应的知识点,希望对各位有所帮助,...
网络用语zl是什么意思(zl是... 今天给各位分享网络用语zl是什么意思的知识,其中也会对zl是啥意思是什么网络用语进行解释,如果能碰巧...
为什么酷狗音乐自己唱的歌不能下... 本篇文章极速百科小编给大家谈谈为什么酷狗音乐自己唱的歌不能下载到本地?,以及为什么酷狗下载的歌曲不是...
华为下载未安装的文件去哪找(华... 今天百科达人给各位分享华为下载未安装的文件去哪找的知识,其中也会对华为下载未安装的文件去哪找到进行解...
怎么往应用助手里添加应用(应用... 今天百科达人给各位分享怎么往应用助手里添加应用的知识,其中也会对应用助手怎么添加微信进行解释,如果能...
家里可以做假山养金鱼吗(假山能... 今天百科达人给各位分享家里可以做假山养金鱼吗的知识,其中也会对假山能放鱼缸里吗进行解释,如果能碰巧解...
四分五裂是什么生肖什么动物(四... 本篇文章极速百科小编给大家谈谈四分五裂是什么生肖什么动物,以及四分五裂打一生肖是什么对应的知识点,希...
一帆风顺二龙腾飞三阳开泰祝福语... 本篇文章极速百科给大家谈谈一帆风顺二龙腾飞三阳开泰祝福语,以及一帆风顺二龙腾飞三阳开泰祝福语结婚对应...
美团联名卡审核成功待激活(美团... 今天百科达人给各位分享美团联名卡审核成功待激活的知识,其中也会对美团联名卡审核未通过进行解释,如果能...