# STM32F10x_StdPeriph_Lib_bare_metal_Freemodbus_RTU **Repository Path**: tsingtsi/stm32-f10x_-std-periph_-lib_bare_metal_-freemodbus_-rtu ## Basic Information - **Project Name**: STM32F10x_StdPeriph_Lib_bare_metal_Freemodbus_RTU - **Description**: STM32F10x 标准库 裸机 移植Freemodbus RTU - **Primary Language**: Unknown - **License**: MulanPSL-1.0 - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 1 - **Created**: 2021-04-27 - **Last Updated**: 2021-04-27 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # STM32 裸机 标准库 移植 Freemodbus RTU 0.STM32F103_Demo 未移植modbus 的基础工程,包含串口初始化,按键,led 1.FreeModbus 移植完成代码,并测试完成03 10命令,可做参考用 2.freemodbus-v1.5.0 modbus官方下载文件 3.ModbusPollSetup.7z modbus 主机测试软件 4.ModbusSlaveSetup.7z modbus从机测试软件 ## 1、官网下载,解压得到文件夹如下 ```c -rw-r--r-- 1 EDZ 197121 1436 十二月 8 2006 bsd.txt -rw-r--r-- 1 EDZ 197121 14448 六月 5 2010 Changelog.txt drwxr-xr-x 1 EDZ 197121 0 六月 6 2010 demo/ drwxr-xr-x 1 EDZ 197121 0 六月 6 2010 doc/ -rw-r--r-- 1 EDZ 197121 18351 二月 19 2006 gpl.txt -rw-r--r-- 1 EDZ 197121 26936 二月 19 2006 lgpl.txt drwxr-xr-x 1 EDZ 197121 0 六月 6 2010 modbus/ drwxr-xr-x 1 EDZ 197121 0 六月 6 2010 tools/ ``` 文件夹demo是官方针对不同平台移植的测试代码 文件夹doc是一些说明文档 文件夹modbus是实现功能的源码 文佳佳tool是上位机 ## 2、文件夹复制 新建STM32 工程 mobus文件夹: ```c drwxr-xr-x 1 EDZ 197121 0 六月 6 2010 ascii/ drwxr-xr-x 1 EDZ 197121 0 六月 6 2010 functions/ drwxr-xr-x 1 EDZ 197121 0 六月 6 2010 include/ -rw-r--r-- 1 EDZ 197121 12908 六月 6 2010 mb.c drwxr-xr-x 1 EDZ 197121 0 六月 6 2010 rtu/ drwxr-xr-x 1 EDZ 197121 0 六月 6 2010 tcp/ ``` demo文件夹: ```c |-- Makefile |-- demo.c `-- port |-- port.h |-- portevent.c |-- portserial.c `-- porttimer.c ``` 需要复制modbus文件夹,和demo中的port文件夹。 ## 3、修改代码 ### 3.1 修改portserial.c 这个文件是用来移植串口的,不管是ASCII模式还是RTU模式都需要串口的支持。 #### 3.1.1 修改void vMBPortSerialEnable( BOOL xRxEnable, BOOL xTxEnable ) ```c void vMBPortSerialEnable( BOOL xRxEnable, BOOL xTxEnable ) { /* If xRXEnable enable serial receive interrupts. If xTxENable enable * transmitter empty interrupts. */ ENTER_CRITICAL_SECTION(); if(xRxEnable) { RSUP_EN = RS_RECV; // 接收 USART_ITConfig(RSUP_USART,USART_IT_RXNE,ENABLE); } else { RSUP_EN = RS_SEND; // 恢复发送 USART_ITConfig(RSUP_USART,USART_IT_RXNE,DISABLE); } if(xTxEnable) { RSUP_EN = RS_SEND; // 发送 USART_ITConfig(RSUP_USART,USART_IT_TXE,ENABLE); } else { RSUP_USART = RS_RECV; // 接收 USART_ITConfig(RSUP_USART,USART_IT_TXE,DISABLE); } EXIT_CRITICAL_SECTION(); } ``` #### 3.1.2 修改BOOL xMBPortSerialInit( UCHAR *ucPORT*, ULONG *ulBaudRate*, UCHAR *ucDataBits*, eMBParity *eParity* ) ```c BOOL xMBPortSerialInit( UCHAR ucPORT, ULONG ulBaudRate, UCHAR ucDataBits, eMBParity eParity ) { (void) ucPORT; (void) ucDataBits; (void) eParity; UP_USART_Config(ulBaudRate); return TRUE; } ``` #### 3.1.3 修改BOOL xMBPortSerialPutByte( CHAR *ucByte* ) ```c BOOL xMBPortSerialPutByte( CHAR ucByte ) { /* Put a byte in the UARTs transmit buffer. This function is called * by the protocol stack if pxMBFrameCBTransmitterEmpty( ) has been * called. */ USART_SendData(RSUP_USART,ucByte); while (USART_GetFlagStatus(RSUP_USART, USART_FLAG_TC) == RESET); return TRUE; } ``` #### 3.1.4 修改BOOL xMBPortSerialGetByte( CHAR * *pucByte* ) ```c BOOL xMBPortSerialGetByte( CHAR * pucByte ) { /* Return the byte in the UARTs receive buffer. This function is called * by the protocol stack after pxMBFrameCBByteReceived( ) has been called. */ *pucByte = USART_ReceiveData(RSUP_USART); return TRUE; } ``` #### 3.1.5 添加 UART4_IRQHandler(void) ```c void UART4_IRQHandler(void) { if(USART_GetITStatus(RSUP_USART,USART_IT_RXNE) == SET) { prvvUARTRxISR(); USART_ClearFlag(RSUP_USART,USART_IT_RXNE); } if(USART_GetITStatus(RSUP_USART,USART_IT_TXE) == SET) { prvvUARTTxReadyISR(); USART_ClearFlag(RSUP_USART,USART_IT_TXE); } } ``` ### 3.2 修改porttimer.c文件 #### 3.2.1 修改BOOL xMBPortTimersInit( USHORT usTim1Timerout50us ) ```c BOOL xMBPortTimersInit( USHORT usTim1Timerout50us ) { TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure; NVIC_InitTypeDef NVIC_InitStructure; RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM4, ENABLE); TIM_TimeBaseStructure.TIM_Period = (uint16_t) usTim1Timerout50us; TIM_TimeBaseStructure.TIM_Prescaler = (uint16_t) (72000000 / 20000) - 1; TIM_TimeBaseStructure.TIM_ClockDivision = 0; TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; TIM_TimeBaseInit(TIM4, &TIM_TimeBaseStructure); TIM_ARRPreloadConfig(TIM4,ENABLE); NVIC_InitStructure.NVIC_IRQChannel = TIM4_IRQn; NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0; NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3; NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; NVIC_Init(&NVIC_InitStructure); TIM_ClearITPendingBit(TIM4,TIM_IT_Update); TIM_ITConfig(TIM4, TIM_IT_Update, DISABLE); TIM_Cmd(TIM4,DISABLE); return TRUE; } ``` #### 3.2.2 修改inline void vMBPortTimersEnable( ) ```c void vMBPortTimersEnable( ) { /* Enable the timer with the timeout passed to xMBPortTimersInit( ) */ /* Enable the timer with the timeout passed to xMBPortTimersInit( ) */ TIM_ClearITPendingBit(TIM4,TIM_IT_Update); TIM_ITConfig(TIM4,TIM_IT_Update,ENABLE); TIM_SetCounter(TIM4,0x0000); TIM_Cmd(TIM4,ENABLE); } ``` #### 3.2.3 修改inline void vMBPortTimersDisable( ) ```c void vMBPortTimersDisable( ) { /* Disable any pending timers. */ TIM_Cmd(TIM4, DISABLE); TIM_SetCounter(TIM4,0x0000); TIM_ITConfig(TIM4, TIM_IT_Update, DISABLE); TIM_ClearITPendingBit(TIM4, TIM_IT_Update); } ``` #### 3.2.4 修改void TIM4_IQRHandler(void) ```c void TIM4_IRQHandler(void) { if ( TIM_GetITStatus( TIM4, TIM_IT_Update) != RESET ) { prvvTIMERExpiredISR(); TIM_ClearITPendingBit(TIM4 , TIM_FLAG_Update); } } ``` ### 3.3 添加port.c文件 ```c #include "port.h" #include "mb.h" #include "mbport.h" #include "mbutils.h" uint16_t usRegInputBuf[REG_INPUT_NREGS] = {0x8001,0x7002,0x6003,0x5004,0x4005,0x3006,0x2007,0x1008}; uint16_t usRegHoldingBuf[REG_HOLDING_NREGS]={0x0001,0x0002,0x0003,0x0004,0x0005,0x0006,0x0007,0x0008}; uint8_t ucRegCoilsBuf[REG_COILS_SIZE]={0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08}; uint8_t ucRegDiscreteBuf[REG_DISCRETE_SIZE]={0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08}; void EnterCriticalSection(void) { __disable_irq(); } void ExitCriticalSection(void) { __enable_irq(); } eMBErrorCode eMBRegInputCB( UCHAR * pucRegBuffer, USHORT usAddress, USHORT usNRegs ) { eMBErrorCode eStatus = MB_ENOERR; int iRegIndex; if( ( usAddress >= REG_INPUT_START ) && ( usAddress + usNRegs <= REG_INPUT_START + REG_INPUT_NREGS ) ) { iRegIndex = ( int )( usAddress - REG_INPUT_START ); while( usNRegs > 0 ) { *pucRegBuffer++ = ( unsigned char )( usRegInputBuf[iRegIndex] >> 8 ); *pucRegBuffer++ = ( unsigned char )( usRegInputBuf[iRegIndex] & 0xFF ); iRegIndex++; usNRegs--; } } else { eStatus = MB_ENOREG; } return eStatus; } eMBErrorCode eMBRegHoldingCB( UCHAR * pucRegBuffer, USHORT usAddress, USHORT usNRegs, eMBRegisterMode eMode ) { eMBErrorCode eStatus = MB_ENOERR; int iRegIndex; if( ( (int16_t)usAddress >= REG_HOLDING_START ) && ( usAddress + usNRegs <= REG_HOLDING_START + REG_HOLDING_NREGS ) ) { iRegIndex = ( int )( usAddress - REG_HOLDING_START ); switch ( eMode ) { case MB_REG_READ: while( usNRegs > 0 ) { *pucRegBuffer++ = ( unsigned char )( usRegHoldingBuf[iRegIndex] >> 8 ); *pucRegBuffer++ = ( unsigned char )( usRegHoldingBuf[iRegIndex] & 0xFF ); iRegIndex++; usNRegs--; } break; case MB_REG_WRITE: while( usNRegs > 0 ) { usRegHoldingBuf[iRegIndex] = *pucRegBuffer++ << 8; usRegHoldingBuf[iRegIndex] |= *pucRegBuffer++; iRegIndex++; usNRegs--; } break; } } else { eStatus = MB_ENOREG; } return eStatus; } eMBErrorCode eMBRegCoilsCB( UCHAR * pucRegBuffer, USHORT usAddress, USHORT usNCoils, eMBRegisterMode eMode ) { eMBErrorCode eStatus = MB_ENOERR; int16_t iNCoils = ( int16_t )usNCoils; int16_t usBitOffset; if( ( (int16_t)usAddress >= REG_COILS_START ) && ( usAddress + usNCoils <= REG_COILS_START + REG_COILS_SIZE ) ) { usBitOffset = ( int16_t )( usAddress - REG_COILS_START ); switch ( eMode ) { case MB_REG_READ: while( iNCoils > 0 ) { *pucRegBuffer++ = xMBUtilGetBits( ucRegCoilsBuf, usBitOffset, ( uint8_t )( iNCoils > 8 ? 8 : iNCoils ) ); iNCoils -= 8; usBitOffset += 8; } break; case MB_REG_WRITE: while( iNCoils > 0 ) { xMBUtilSetBits( ucRegCoilsBuf, usBitOffset, ( uint8_t )( iNCoils > 8 ? 8 : iNCoils ), *pucRegBuffer++ ); iNCoils -= 8; } break; } } else { eStatus = MB_ENOREG; } return eStatus; } eMBErrorCode eMBRegDiscreteCB( UCHAR * pucRegBuffer, USHORT usAddress, USHORT usNDiscrete ) { eMBErrorCode eStatus = MB_ENOERR; int16_t iNDiscrete = ( int16_t )usNDiscrete; uint16_t usBitOffset; if( ( (int16_t)usAddress >= REG_DISCRETE_START ) && ( usAddress + usNDiscrete <= REG_DISCRETE_START + REG_DISCRETE_SIZE ) ) { usBitOffset = ( uint16_t )( usAddress - REG_DISCRETE_START ); while( iNDiscrete > 0 ) { *pucRegBuffer++ = xMBUtilGetBits( ucRegDiscreteBuf, usBitOffset, ( uint8_t)( iNDiscrete > 8 ? 8 : iNDiscrete ) ); iNDiscrete -= 8; usBitOffset += 8; } } else { eStatus = MB_ENOREG; } return eStatus; } ``` ### 3.4 修改port.h ```c #include "stm32f10x.h" #define REG_INPUT_START 0 #define REG_INPUT_NREGS 8 #define REG_HOLDING_START 0 #define REG_HOLDING_NREGS 8 #define REG_COILS_START 0 #define REG_COILS_SIZE 8 #define REG_DISCRETE_START 0 #define REG_DISCRETE_SIZE 8 extern uint16_t usRegInputBuf[REG_INPUT_NREGS]; extern uint16_t usRegHoldingBuf[REG_HOLDING_NREGS]; extern uint8_t ucRegCoilsBuf[REG_COILS_SIZE]; extern uint8_t ucRegDiscreteBuf[REG_DISCRETE_SIZE]; void EnterCriticalSection(void); void ExitCriticalSection(void); ``` ## 4、调试bug记录 正常进行一次通讯后(从机接收完成并返回),不能再正常进入到中断。 仿真查询后发现485使能引脚状态转为发送状态。 仿真时现象: 一次正常通讯后,完成485使能引脚转换,没有立刻退出vMBPortSerialEnable函数,而是再次进入到 xTxEnable = TURE判断中,将引脚使能为发送。 ````c if(xTxEnable) { RSUP_EN = RS_SEND; // 发送 USART_ITConfig(DEF_UP_UART,USART_IT_TXE,ENABLE); } ```` 解决办法: 先对引脚状态进行改变,再对串口标志位进行使能/失能。 bug时代码为: ```c void vMBPortSerialEnable( BOOL xRxEnable, BOOL xTxEnable ) { /* If xRXEnable enable serial receive interrupts. If xTxENable enable * transmitter empty interrupts. */ ENTER_CRITICAL_SECTION(); if(xRxEnable) { USART_ITConfig(DEF_UP_UART,USART_IT_RXNE,ENABLE); RSUP_EN = RS_RECV; // 接收 } else { USART_ITConfig(DEF_UP_UART,USART_IT_RXNE,DISABLE); RSUP_EN = RS_SEND; // 恢复发送 } if(xTxEnable) { USART_ITConfig(DEF_UP_UART,USART_IT_TXE,ENABLE); RSUP_EN = RS_SEND; // 发送 } else { USART_ITConfig(DEF_UP_UART,USART_IT_TXE,DISABLE); RSUP_EN = RS_RECV; // 接收 } EXIT_CRITICAL_SECTION(); } ``` 解决bug后的代码为: ```c void vMBPortSerialEnable( BOOL xRxEnable, BOOL xTxEnable ) { /* If xRXEnable enable serial receive interrupts. If xTxENable enable * transmitter empty interrupts. */ ENTER_CRITICAL_SECTION(); if(xRxEnable) { RSUP_EN = RS_RECV; // 接收 USART_ITConfig(DEF_UP_UART,USART_IT_RXNE,ENABLE); } else { RSUP_EN = RS_SEND; // 恢复发送 USART_ITConfig(DEF_UP_UART,USART_IT_RXNE,DISABLE); } if(xTxEnable) { RSUP_EN = RS_SEND; // 发送 USART_ITConfig(DEF_UP_UART,USART_IT_TXE,ENABLE); } else { RSUP_EN = RS_RECV; // 接收 USART_ITConfig(DEF_UP_UART,USART_IT_TXE,DISABLE); } EXIT_CRITICAL_SECTION(); } ```