STM32 裸机 标准库 移植 Freemodbus RTU
https://download.csdn.net/download/weixin_41736398/19930460?spm=1001.2014.3001.5503
包含了源码,移植成功的代码,测试用的上位机
1、官网下载源码,解压得到文件夹如下
-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文件夹:
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文件夹:
|-- 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 )
void
vMBPortSerialEnable( BOOL xRxEnable, BOOL xTxEnable )
{
/* If xRXEnable enable serial receive interrupts. If xTxENable enable
* transmitter empty interrupts.
*/
/*
对下面要用到的宏定义进行解释
#define RSUP_EN PAout(0)
#define RSDOWN_EN PAout(1)
#define DEF_UP_UART USART3
#define DEF_DOWN_UART USART2
typedef enum {RS_RECV = 0,RS_SEND = !RS_RECV} RS485_Status;
*/
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();
}
3.1.2 修改BOOL xMBPortSerialInit( UCHAR ucPORT, ULONG ulBaudRate, UCHAR ucDataBits, eMBParity eParity )
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 )
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 )
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)
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 )
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( )
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( )
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)
void TIM4_IRQHandler(void)
{
if ( TIM_GetITStatus( TIM4, TIM_IT_Update) != RESET )
{
prvvTIMERExpiredISR();
TIM_ClearITPendingBit(TIM4 , TIM_FLAG_Update);
}
}
3.3 添加port.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
#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判断中,将引脚使能为发送。
if(xTxEnable)
{
RSUP_EN = RS_SEND; // 发送
USART_ITConfig(DEF_UP_UART,USART_IT_TXE,ENABLE);
}
解决办法:
先对引脚状态进行改变,再对串口标志位进行使能/失能。
bug时代码为:
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后的代码为:
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();
}
https://gitee.com/lyj1940696668/stm32-f10x_-std-periph_-lib_bare_metal_-freemodbus_-rtu