STM32基于HAL库带FreeRTOS系统的Freemodbus移植

Georgia ·
更新时间:2024-09-20
· 983 次阅读

STM32基于HAL库移植带FreeRTOS系统的Freemodbus移植移植前提下载所需源码可能的win10 IAR设置从站注意定义寄存器数量大小效果查询报文效果回复报文移植事件、定时器、串口事件移植串口移植定时器移植线程中调用Master移植类似参考从机协议portxx.c文件修改修改名称即可,问题不大 移植前提 下载所需源码

github地址
本项目地址

可能的win10 IAR设置

设置快捷键
ctrl+shift+l变为find in file,原ctrl+shift+f与win10输入法冲突会切换繁体输入

从站注意定义寄存器数量大小

在这里插入图片描述

效果查询报文

![在这里插入图片描述](https://img-blog.csdnimg.cn/20200406184232910.png

效果回复报文

在这里插入图片描述

移植事件、定时器、串口

对应实现
在这里插入图片描述
开关临界区
e_port.c

void EnterCriticalSection(void) { taskENTER_CRITICAL(); } void ExitCriticalSection(void) { taskEXIT_CRITICAL(); } 事件移植

portevent.c

/* ----------------------- Modbus includes ----------------------------------*/ #include "mb.h" #include "mbport.h" volatile uint32_t Modbus_Event_ALL = 0; void Set_Event_Port(void); /* ----------------------- Variables ----------------------------------------*/ static StaticEventGroup_t xSlaveOsEvent; /* 事件存储,事件组 */ static EventGroupHandle_t slave_event_Handle; /* 事件标志组句柄 */ /* ----------------------- Start implementation -----------------------------*/ BOOL xMBPortEventInit( void ) { /*创建事件组,成功返回句柄*/ printf("创建事件组,成功返回句柄\n"); slave_event_Handle = xEventGroupCreateStatic(&xSlaveOsEvent); if(slave_event_Handle == NULL) { printf("创建事件组 失败!\n"); } return TRUE; } BOOL xMBPortEventPost( eMBEventType eEvent ) { /*设置事件标志组 eEvent 置1*/ printf("设置事件标志组 eEvent:0x%04X\n",eEvent); Modbus_Event_ALL |= eEvent; return TRUE; } BOOL xMBPortEventGet( eMBEventType * eEvent ) { /* waiting forever OS event */ uint32_t recvedEvent; recvedEvent = xEventGroupGetBits(slave_event_Handle); switch (recvedEvent) { case EV_READY: *eEvent = EV_READY; printf("读取事件标志组 EV_READY\n"); break; case EV_FRAME_RECEIVED: *eEvent = EV_FRAME_RECEIVED; printf("读取事件标志组 EV_FRAME_RECEIVED\n"); break; case EV_EXECUTE: *eEvent = EV_EXECUTE; printf("读取事件标志组 EV_EXECUTE\n"); break; case EV_FRAME_SENT: *eEvent = EV_FRAME_SENT; printf("读取事件标志组 EV_FRAME_SENT\n"); break; } xEventGroupClearBits(slave_event_Handle ,recvedEvent); return TRUE; } /** ****************************************************************** * @brief 循环设置事件接口,原中断中设置事件存在问题 * @author aron566 * @version v1.0 * @date 2020/4/5 ****************************************************************** */ void Set_Event_Port(void) { uint32_t ret = 0; if(Modbus_Event_ALL) { ret = xEventGroupSetBits(slave_event_Handle, Modbus_Event_ALL); if(ret != Modbus_Event_ALL) { printf("设置事件失败\n"); } else { Modbus_Event_ALL = 0; } } } 串口移植

串口使用的是带有环形缓冲区,实现方法参考博文
portserial.c

/* ----------------------- Static variables ---------------------------------*/ /* software simulation serial transmit IRQ handler thread stack */ static uint32_t serial_soft_trans_irq_stack[128]; /* software simulation serial transmit IRQ handler thread */ static osThreadId SlaveSerial_soft_trans_irq_Handle; static osStaticThreadDef_t slave_transControlBlock; /* serial event */ static StaticEventGroup_t Slaveevent_serial; /*事件存储,事件组*/ static EventGroupHandle_t Slaveevent_serial_Handle; /*事件标志组句柄*/ /* modbus slave serial device */ static UART_HandleTypeDef *huart_x; /* ----------------------- Defines ------------------------------------------*/ /* serial transmit event */ #define EVENT_SERIAL_TRANS_START (1<<0) /* ----------------------- static functions ---------------------------------*/ static void prvvUARTTxReadyISR(void); static void prvvUARTRxISR(void); static BOOL serial_rx_ind(UART_HandleTypeDef *dev, uint16_t size); static void serial_soft_trans_irq(void const *parameter); void Slave_Serial_rx_ind(uint16_t size); /* ----------------------- Start implementation -----------------------------*/ BOOL xMBPortSerialInit(UCHAR ucPORT, ULONG ulBaudRate, UCHAR ucDataBits, eMBParity eParity) { /** * set 485 mode receive and transmit control IO * @note MODBUS_SLAVE_RT_CONTROL_PIN_INDEX need be defined by user */ /* set serial name */ switch (ucPORT) { case 1: huart_x = &huart1; break; case 2: huart_x = &huart2; break; case 3: break; default: return FALSE; break; } /*创建事件组,成功返回句柄*/ Slaveevent_serial_Handle = xEventGroupCreateStatic(&Slaveevent_serial); if(Slaveevent_serial_Handle == NULL) { printf("创建事件组 失败!\n"); } printf("创建串口事件组 Slaveevent_serial_Handle\n"); osThreadStaticDef(slave_trans, serial_soft_trans_irq, osPriorityNormal, 0, 128, serial_soft_trans_irq_stack, &slave_transControlBlock); SlaveSerial_soft_trans_irq_Handle = osThreadCreate(osThread(slave_trans), NULL); return TRUE; } void vMBPortSerialEnable(BOOL xRxEnable, BOOL xTxEnable) { // uint32_t recvedEvent; if (xRxEnable) { /* enable RX interrupt */ // __HAL_UART_ENABLE_IT(huart_x, UART_FLAG_IDLE); printf("开启串口接收中断 UART_IT_RXNE\n"); /* switch 485 to receive mode */ /*拉低RS485_SEL脚,RS485为接收状态*/ HAL_GPIO_WritePin(RS485_SEL_GPIO_Port, RS485_SEL_Pin, GPIO_PIN_RESET); } else { /* switch 485 to transmit mode */ /*拉高RS485_SEL脚,RS485为发送状态*/ HAL_GPIO_WritePin(RS485_SEL_GPIO_Port, RS485_SEL_Pin, GPIO_PIN_SET); /* disable RX interrupt */ printf("关闭串口接收中断 UART_FLAG_IDLE\n"); // HAL_UART_AbortReceive_IT(huart_x); // __HAL_UART_DISABLE_IT(huart_x ,UART_FLAG_IDLE); } if (xTxEnable) { /* start serial transmit */ // __HAL_UART_ENABLE_IT(huart_x ,UART_IT_TXE); printf("开启串口发送中断 UART_IT_TXE\n"); /*设置事件标志组 EVENT_SERIAL_TRANS_START 置1*/ xEventGroupSetBits(Slaveevent_serial_Handle, EVENT_SERIAL_TRANS_START); } else { /* stop serial transmit */ // recvedEvent = xEventGroupGetBits(Slaveevent_serial_Handle); printf("关闭串口发送中断 UART_IT_TXE\n"); // HAL_UART_AbortTransmit_IT(huart_x); // __HAL_UART_DISABLE_IT(huart_x ,UART_IT_TXE); } } void vMBPortClose(void) { HAL_UART_Abort(huart_x); } BOOL xMBPortSerialPutByte(CHAR ucByte) { HAL_UART_Transmit_IT(huart_x,(uint8_t *)&ucByte, 1); return TRUE; } BOOL xMBPortSerialGetByte(CHAR * pucByte) { // HAL_UART_Receive_IT(huart_x, (uint8_t*)pucByte, 1); /*这里使用环形缓冲区,作为数据的提取*/ CQ_getData(cb, (uint8_t*)pucByte, 1); return TRUE; } /* * Create an interrupt handler for the transmit buffer empty interrupt * (or an equivalent) for your target processor. This function should then * call pxMBFrameCBTransmitterEmpty( ) which tells the protocol stack that * a new character can be sent. The protocol stack will then call * xMBPortSerialPutByte( ) to send the character. */ void prvvUARTTxReadyISR(void) { pxMBFrameCBTransmitterEmpty(); } /* * Create an interrupt handler for the receive interrupt for your target * processor. This function should then call pxMBFrameCBByteReceived( ). The * protocol stack will then call xMBPortSerialGetByte( ) to retrieve the * character. */ void prvvUARTRxISR(void) { pxMBFrameCBByteReceived(); } /** * Software simulation serial transmit IRQ handler. * * @param parameter parameter */ static void serial_soft_trans_irq(void const *parameter) { uint32_t recved_event; while (1) { /* waiting for serial transmit start */ recved_event = xEventGroupGetBits(Slaveevent_serial_Handle); /* execute modbus callback */ if(recved_event == EVENT_SERIAL_TRANS_START) { prvvUARTTxReadyISR(); /*这里本应清除事件位,由于协议栈使用的是单字节发送,需要检测此事件标志位否则发送完一字节就不发了,偷懒没有清除,后期可以优化成发完清除标志*/ // xEventGroupClearBits(Slaveevent_serial_Handle ,EVENT_SERIAL_TRANS_START); } osDelay(1); } } /** * This function is serial receive callback function * * @param dev the device of serial * @param size the data size that receive * * @return return RT_EOK */ static BOOL serial_rx_ind(UART_HandleTypeDef *dev, uint16_t size) { prvvUARTRxISR(); return TRUE; } void Slave_Serial_rx_ind(uint16_t size) { serial_rx_ind(huart_x ,size); } 定时器移植

porttimer.c
硬件设置:50us定时
在这里插入图片描述

/* ----------------------- static functions ---------------------------------*/ static TIM_HandleTypeDef timer; static void prvvTIMERExpiredISR(void); void Slave_timer_timeout_ind(void* parameter); uint32_t slave_timer_tick; static USHORT RT_TICK_PER_SECOND = 50; extern uint32_t TIM2CurrentTicks; /* ----------------------- Start implementation -----------------------------*/ BOOL xMBPortTimersInit(USHORT usTim1Timerout50us) { timer = htim2; slave_timer_tick = (50 * usTim1Timerout50us) / RT_TICK_PER_SECOND; return TRUE; } void vMBPortTimersEnable() { printf("开启定时器\n"); TIM2CurrentTicks = 0; __HAL_TIM_SET_COUNTER(&timer, 0); HAL_TIM_Base_Start_IT(&timer); } void vMBPortTimersDisable() { printf("关闭定时器\n"); HAL_TIM_Base_Stop_IT(&timer); } void prvvTIMERExpiredISR(void) { (void) pxMBPortCBTimerExpired(); } void Slave_timer_timeout_ind(void* parameter) { prvvTIMERExpiredISR(); }

中断
在这里插入图片描述

线程中调用

eMBInit和eMBEnable调用顺序别搞反

eMBInit(MB_RTU, 1, 1, 115200, MB_PAR_NONE); eMBEnable(); /* Infinite loop */ for(;;) { eMBPoll(); Set_Event_Port(); /*缓冲区数据不为空,则调用协议栈取数据*/ if(CQ_getLength(cb)) { Slave_Serial_rx_ind(1); } osDelay(1); } Master移植类似参考从机协议portxx.c文件修改修改名称即可,问题不大
作者:aron566



hal freertos stm32

需要 登录 后方可回复, 如果你还没有账号请 注册新账号