页面加载中,请稍候
来源:嵌入式大杂烩发布时间:2022-05-261684浏览
询问 AImodbus是一个非常好的串口协议(当然也能用在网口上),它简洁、规范、强大。可以满足大部分的工业、嵌入式需求。
这里详细说下如何将freemodbus移植到stm32平台。我之前下载的版本是1.5,当前官网最新的版本是1.6。两者差别不大,这里以1.5版本做演示。
下载好之后,解压得到如下内容:
我们需要的是modbus这个文件夹,和demo->BARE下的port文件夹。
在工程文件夹下新建一个文件夹:FreeModbus。将第一步获取的两个文件夹放到里面。
打开工程,添加两个group,名字分别为modbus和port。将这两个文件夹下的C文件都添加进来,tcp相关的除外。
文件包含路径,也添加这几个文件夹的位置:
3完善portserial.c文件该文件就是modbus通信中用到的串口的初始化配置文件。我这里选择usart1,波特率9600.
第一次打开这个文件,内容如下:
voidvMBPortSerialEnable( BOOL xRxEnable, BOOL xTxEnable ){ }
BOOLxMBPortSerialInit( UCHAR ucPORT, ULONG ulBaudRate, UCHAR ucDataBits, eMBParity eParity ){ return FALSE;}
BOOLxMBPortSerialPutByte( CHAR ucByte ){ return TRUE;}
认真看一下函数名字,你会发现这些函数分别是:串口使能、串口初始化、发送一个字节、接收一个字节等等。
完善后代码如下:
voidvMBPortSerialEnable( BOOL xRxEnable, BOOL xTxEnable ){ if(xRxEnable == TRUE) { USART_ITConfig(USART1, USART_IT_RXNE, ENABLE); } else { USART_ITConfig(USART1, USART_IT_RXNE, DISABLE); } if(xTxEnable == TRUE) { USART_ITConfig(USART1, USART_IT_TC, ENABLE); } else { USART_ITConfig(USART1, USART_IT_TC, DISABLE); }}
BOOLxMBPortSerialInit( UCHAR ucPORT, ULONG ulBaudRate, UCHAR ucDataBits, eMBParity eParity ){ USART1_Config((uint16_t)ulBaudRate); USART_NVIC(); return TRUE;}
BOOLxMBPortSerialPutByte( CHAR ucByte ){ USART_SendData(USART1, ucByte); return TRUE;}
BOOLxMBPortSerialGetByte( CHAR * pucByte ){ *pucByte = USART_ReceiveData(USART1); return TRUE;}
static void prvvUARTTxReadyISR( void ){ pxMBFrameCBTransmitterEmpty( );}
static void prvvUARTRxISR( void ){ pxMBFrameCBByteReceived( );}
void USART1_IRQHandler(void){ if(USART_GetITStatus(USART1, USART_IT_RXNE) == SET) { prvvUARTRxISR(); USART_ClearITPendingBit(USART1, USART_IT_RXNE); } if(USART_GetITStatus(USART1, USART_IT_ORE) == SET) { USART_ClearITPendingBit(USART1, USART_IT_ORE); prvvUARTRxISR(); } if(USART_GetITStatus(USART1, USART_IT_TC) == SET) { prvvUARTTxReadyISR(); USART_ClearITPendingBit(USART1, USART_IT_TC); }}
其中USART1_Config((uint16_t)ulBaudRate);和 USART_NVIC();是串口初始化的代码,如下:
void USART1_Config(uint16_t buad){ GPIO_InitTypeDef GPIO_InitStructure; USART_InitTypeDef USART_InitStructure; RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1 | RCC_APB2Periph_GPIOA, ENABLE); GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOA, &GPIO_InitStructure); GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; GPIO_Init(GPIOA, &GPIO_InitStructure); USART_InitStructure.USART_BaudRate = buad; USART_InitStructure.USART_WordLength = USART_WordLength_8b; USART_InitStructure.USART_StopBits = USART_StopBits_1; USART_InitStructure.USART_Parity = USART_Parity_No ; USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None; USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx; USART_Init(USART1, &USART_InitStructure); USART_Cmd(USART1, ENABLE);}
void USART_NVIC(void){ NVIC_InitTypeDef NVIC_InitStructure; NVIC_PriorityGroupConfig(NVIC_PriorityGroup_0); NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn; NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0; NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0; NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; NVIC_Init(&NVIC_InitStructure);}
4完善porttimer.c文件
modbus工作时需要一个定时器,所以这里配置一个定时器。定时器时基是50us,周期做为参数输入。代码如下:
BOOLxMBPortTimersInit( USHORT usTim1Timerout50us ){ timer2_init(usTim1Timerout50us); timer2_nvic(); return TRUE;}
voidvMBPortTimersEnable( ){ TIM_ClearITPendingBit(TIM2, TIM_IT_Update); TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE); TIM_SetCounter(TIM2,0x0000); TIM_Cmd(TIM2, ENABLE);}
voidvMBPortTimersDisable( ){ TIM_ClearITPendingBit(TIM2, TIM_IT_Update); TIM_ITConfig(TIM2, TIM_IT_Update, DISABLE); TIM_SetCounter(TIM2,0x0000); TIM_Cmd(TIM2, DISABLE);}
static void prvvTIMERExpiredISR( void ){ ( void )pxMBPortCBTimerExpired( );}
void TIM2_IRQHandler(void){ if(TIM_GetITStatus(TIM2, TIM_IT_Update) != RESET) { prvvTIMERExpiredISR(); TIM_ClearITPendingBit(TIM2, TIM_IT_Update); }}
其中 timer2_init(usTim1Timerout50us) 和 timer2_nvic() 是timer2初始化函数,内容如下:
void timer2_init(uint16_t period){ TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure; RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE); TIM_DeInit(TIM2); TIM_TimeBaseStructure.TIM_Period = period; TIM_TimeBaseStructure.TIM_Prescaler = (1800 - 1); TIM_TimeBaseStructure.TIM_ClockDivision = 0; TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure); TIM_Cmd(TIM2, ENABLE);
}
void timer2_nvic(void){ NVIC_InitTypeDef NVIC_InitStructure; NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQn; NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0; NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2; NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; NVIC_Init(&NVIC_InitStructure); }
5、在main.c文件中,定义各个模拟寄存器的地址和大小。
#define REG_INPUT_START 0x00006补全输入寄存器操作函数、保持寄存器操作函数
#define REG_INPUT_NREGS 8
#define REG_HOLDING_START 0x0000
#define REG_HOLDING_NREGS 8
#define REG_COILS_START 0x0000
#define REG_COILS_SIZE 16
#define REG_DISCRETE_START 0x0000
#define REG_DISCRETE_SIZE 16
modbus功能进行初始化,设置地址和波特率。这部分内容可以参考官方资料里的例程,也可以直接复制别人写好的。我这里放别人写好的代码:
int main(void){ usRegInputBuf[0] = 'I'; usRegInputBuf[1] = ' '; usRegInputBuf[2] = 'a'; usRegInputBuf[3] = 'm'; usRegInputBuf[4] = ' '; usRegInputBuf[5] = 'I';
RCC_Config(); eMBInit(MB_RTU, 0x01, 0x01, 9600, MB_PAR_NONE); eMBEnable(); for(;;) { (void)eMBPoll(); }}
eMBErrorCodeeMBRegInputCB( 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 - usRegInputStart ); while( usNRegs > 0 ) { *pucRegBuffer++ = ( UCHAR )( usRegInputBuf[iRegIndex] >> 8 ); *pucRegBuffer++ = ( UCHAR )( usRegInputBuf[iRegIndex] & 0xFF ); iRegIndex++; usNRegs--; } } else { eStatus = MB_ENOREG; }
return eStatus;}
eMBErrorCodeeMBRegHoldingCB( UCHAR * pucRegBuffer, USHORT usAddress, USHORT usNRegs, eMBRegisterMode eMode ){ eMBErrorCode eStatus = MB_ENOERR; int iRegIndex;
if((usAddress >= REG_HOLDING_START)&&\ ((usAddress+usNRegs) <= (REG_HOLDING_START + REG_HOLDING_NREGS))) { iRegIndex = (int)(usAddress - usRegHoldingStart); switch(eMode) { case MB_REG_READ: while(usNRegs > 0) { *pucRegBuffer++ = (u8)(usRegHoldingBuf[iRegIndex] >> 8); *pucRegBuffer++ = (u8)(usRegHoldingBuf[iRegIndex] & 0xFF); iRegIndex++; usNRegs--; } break; case MB_REG_WRITE: while(usNRegs > 0) { usRegHoldingBuf[iRegIndex] = *pucRegBuffer++ << 8; usRegHoldingBuf[iRegIndex] |= *pucRegBuffer++; iRegIndex++; usNRegs--; } } } else { eStatus = MB_ENOREG; } return eStatus;}
eMBErrorCodeeMBRegCoilsCB( UCHAR * pucRegBuffer, USHORT usAddress, USHORT usNCoils, eMBRegisterMode eMode ){ eMBErrorCode eStatus = MB_ENOERR; int iRegIndex;
if((usAddress >= REG_HOLDING_START)&&\ ((usAddress+usNCoils) <= (REG_HOLDING_START + REG_HOLDING_NREGS))) { iRegIndex = (int)(usAddress - usRegHoldingStart); switch(eMode) { case MB_REG_READ: while(usNCoils > 0) {
iRegIndex++; usNCoils--; } break; case MB_REG_WRITE: while(usNCoils > 0) {
iRegIndex++; usNCoils--; } } } else { eStatus = MB_ENOREG; } return eStatus;}
eMBErrorCodeeMBRegDiscreteCB( UCHAR * pucRegBuffer, USHORT usAddress, USHORT usNDiscrete ){ ( void )pucRegBuffer; ( void )usAddress; ( void )usNDiscrete; return MB_ENOREG;}
7修改mbrtu.c文件
否则modbus从机收到命令后,只会返回一次数据。在函数“eMBRTUSend”中。
eMBErrorCodeeMBRTUSend( UCHAR ucSlaveAddress, const UCHAR * pucFrame, USHORT usLength ){ eMBErrorCode eStatus = MB_ENOERR; USHORT usCRC16;
ENTER_CRITICAL_SECTION( );
if( eRcvState == STATE_RX_IDLE ) { pucSndBufferCur = ( UCHAR * ) pucFrame - 1; usSndBufferCount = 1;
pucSndBufferCur[MB_SER_PDU_ADDR_OFF] = ucSlaveAddress; usSndBufferCount += usLength;
usCRC16 = usMBCRC16( ( UCHAR * ) pucSndBufferCur, usSndBufferCount ); ucRTUBuf[usSndBufferCount++] = ( UCHAR )( usCRC16 & 0xFF ); ucRTUBuf[usSndBufferCount++] = ( UCHAR )( usCRC16 >> 8 );
eSndState = STATE_TX_XMIT; xMBPortSerialPutByte( ( CHAR )*pucSndBufferCur ); pucSndBufferCur++; usSndBufferCount--; vMBPortSerialEnable( FALSE, TRUE ); } else { eStatus = MB_EIO; } EXIT_CRITICAL_SECTION( ); return eStatus;}
8 修改mbconfig.h文件
取消对ASCII的支持。
#define MB_ASCII_ENABLED ( 0 )9保存,编译,下载。使用专用的modbus工具测试
工具配置如下:
modbus指令格式如下:
咱们这里设置如下:01 04 00 00 00 02,功能码04,起始地址0,数据长度2.校验码没有写怎么办?
这就是这个工具的便利之处!我们不用管,它会自动计算!直接点击发送即可。得到结果如下:
可以看到下面的框里,绿色的是我们发送的内容,最后两位是工具自动补上的。蓝色内容是单片机(也就是modbus从机)返回给我们的。
没有问题,打完收工!
新闻来源:嵌入式大杂烩,文中所述为作者独立观点,不代表icspec立场。更多精彩资讯请下载icspec App。如对本稿件有异议,请联系微信客服specltkj。
暂无评论哦,快来评论一下吧!

2026-07-13

2026-07-08

2026-07-10