完善资料让更多小伙伴认识你,还能领取20积分哦, 立即完善>
FreeModbus的移植,基于STM32F4+HAL库平台的MODBUS RTU从机
FreeModbus FreeMODBUS 是针对通用的Modbus协议栈在嵌入式系统中应用的一个实现。Modbus协议是一个在工业制造领域中得到广泛应用的一个网络协议。目前版本的FreeModbus支持如下的功能码:
然后就是把usart.c中和其他文件中冲突的代码都注释掉,因为这个文件里我们只用如下功能 //重定义fput() int fputc(int ch, FILE *f) { while((USART1->SR&0X40)==0);//循环发送 USART1->DR = (u8) ch; return ch; } 紧接着添加头文件
#define EXIT_CRITICAL_SECTION( ) __enable_irq() 补充完整这两个宏定义
/* ----------------------- Modbus includes ----------------------------------*/ #include "mb.h" #include "mbport.h" #include "sys.h" extern UART_HandleTypeDef huart1; /* ----------------------- static functions ---------------------------------*/ void prvvUARTTxReadyISR( void ); void prvvUARTRxISR( void ); 添加sys.h头文件,去掉*prvvUARTTxReadyISR( void )*和 prvvUARTRxISR( void ) 的static属性,这是方便在ISR中调用这两个函数。 添加huart1声明,目的是在这个文件中使用这个句柄。 void vMBPortSerialEnable( BOOL xRxEnable, BOOL xTxEnable ) { /* If xRXEnable enable serial receive interrupts. If xTxENable enable * transmitter empty interrupts. */ if(xRxEnable == TRUE) __HAL_UART_ENABLE_IT(&huart1, UART_IT_RXNE); else __HAL_UART_DISABLE_IT(&huart1, UART_IT_RXNE); if(xTxEnable == TRUE) __HAL_UART_ENABLE_IT(&huart1, UART_IT_TXE); else __HAL_UART_DISABLE_IT(&huart1, UART_IT_TXE); } 完成串口使能函数体,接收时使用RXNE中断,发送时使用TXE中断 这两个中断是在RM0090中USART状态寄存器 (USART_SR)定义的 BOOL xMBPortSerialInit( UCHAR ucPORT, ULONG ulBaudRate, UCHAR ucDataBits, eMBParity eParity ) { HAL_UART_DeInit(&huart1);//DEINIT cubeMX中的初始化配置 (void)ucPORT; huart1.Instance = USART1; huart1.Init.BaudRate = ulBaudRate; huart1.Init.StopBits = UART_STOPBITS_1; switch (eParity)//使用校验位,就需要将uart的数据位配置为9位 { case MB_PAR_ODD: huart1.Init.WordLength = UART_WORDLENGTH_9B; huart1.Init.Parity = UART_PARITY_ODD; break; case MB_PAR_EVEN: huart1.Init.WordLength = UART_WORDLENGTH_9B; huart1.Init.Parity = UART_PARITY_EVEN; break; default: huart1.Init.WordLength = UART_WORDLENGTH_8B; huart1.Init.Parity = UART_PARITY_NONE; break; } huart1.Init.Mode = UART_MODE_TX_RX; huart1.Init.HwFlowCtl = UART_HWCONTROL_NONE; huart1.Init.OverSampling = UART_OVERSAMPLING_16; if (HAL_UART_Init(&huart1) != HAL_OK) { return FALSE; } return TRUE; } 重新对串口1进行初始化,重新配置波特率和校验位,至于数据位,freemodbus中固定为8位,停止位按需配置,端口号暂时我没有用到。 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. */ if(HAL_UART_Transmit(&huart1,(uint8_t*)&ucByte,1,1) == HAL_OK) return TRUE; else return FALSE; } 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. */ if(HAL_UART_Receive(&huart1,(uint8_t*)pucByte,1,1) == HAL_OK) return TRUE; else return FALSE; } 发送和接收1个字节,调用的HAL库函数。 /* 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( ); } 这两个函数在串口1ISR中会调用,在这里只需要删掉函数前的static即可
/* ----------------------- Platform includes --------------------------------*/ #include "port.h" /* ----------------------- Modbus includes ----------------------------------*/ #include "mb.h" #include "mbport.h" /* ----------------------- static functions ---------------------------------*/ void prvvTIMERExpiredISR( void ); extern TIM_HandleTypeDef htim6; /* ----------------------- Start implementation -----------------------------*/ BOOL xMBPortTimersInit( USHORT usTim1Timerout50us ) { HAL_TIM_Base_DeInit(&htim6); TIM_MasterConfigTypeDef sMasterConfig = {0}; /* USER CODE BEGIN TIM6_Init 1 */ /* USER CODE END TIM6_Init 1 */ htim6.Instance = TIM6; htim6.Init.Prescaler = 4499;//50us分频,这里使用的timer PCLK频率是90Mhz htim6.Init.CounterMode = TIM_COUNTERMODE_UP; htim6.Init.Period = usTim1Timerout50us-1;//modbus 规定的TIMEOUT时间 htim6.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE; if (HAL_TIM_Base_Init(&htim6) != HAL_OK) { return FALSE; } sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET; sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE; if (HAL_TIMEx_MasterConfigSynchronization(&htim6, &sMasterConfig) != HAL_OK) { return FALSE; } return TRUE; } 定时器的初始化。这里是被eMBInit进行调用,调用时usTim1Timerout50us参数是用来计算50us的倍数的,这个值在eMBRtuInit()中根据波特率进行计算。 inline void vMBPortTimersEnable( ) { /* Enable the timer with the timeout passed to xMBPortTimersInit( ) */ __HAL_TIM_CLEAR_IT(&htim6,TIM_IT_UPDATE); __HAL_TIM_SetCounter(&htim6,0);//这里一定要清零计数器 HAL_TIM_Base_Start_IT(&htim6); } inline void vMBPortTimersDisable( ) { /* Disable any pending timers. */ HAL_TIM_Base_Stop_IT(&htim6); __HAL_TIM_SetCounter(&htim6,0); __HAL_TIM_CLEAR_IT(&htim6,TIM_IT_UPDATE); } 定时器的启停 /* 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. */ void prvvTIMERExpiredISR( void ) { ( void )pxMBPortCBTimerExpired( ); } 会在TIMER6的ISR中调用,去掉前面static即可
void USART1_IRQHandler(void) { /* USER CODE BEGIN USART1_IRQn 0 */ uint8_t tmp; if(__HAL_UART_GET_FLAG(&huart1,UART_FLAG_PE))//奇偶校验位判断 { HAL_UART_Receive(&huart1,&tmp,1,1); } else if(__HAL_UART_GET_FLAG(&huart1,UART_FLAG_RXNE)&&__HAL_UART_GET_IT_SOURCE(&huart1,UART_IT_RXNE)) { prvvUARTRxISR(); } if(__HAL_UART_GET_FLAG(&huart1,UART_FLAG_TXE)&&__HAL_UART_GET_IT_SOURCE(&huart1,UART_IT_TXE)) { prvvUARTTxReadyISR(); } /* USER CODE END USART1_IRQn 0 */ HAL_UART_IRQHandler(&huart1); /* USER CODE BEGIN USART1_IRQn 1 */ /* USER CODE END USART1_IRQn 1 */ } /** * @brief This function handles TIM6 global interrupt, DAC1 and DAC2 underrun error interrupts. */ void TIM6_DAC_IRQHandler(void) { /* USER CODE BEGIN TIM6_DAC_IRQn 0 */ /* USER CODE END TIM6_DAC_IRQn 0 */ HAL_TIM_IRQHandler(&htim6); /* USER CODE BEGIN TIM6_DAC_IRQn 1 */ prvvTIMERExpiredISR(); /* USER CODE END TIM6_DAC_IRQn 1 */ } 修改两个外设的ISR,在UART的ISR中加入了奇偶校验的判断 主函数main.c应用程序 #include "mb.h" #include "sys.h" //添加头文件 /* ----------------------- Defines ------------------------------------------*/ #define REG_INPUT_START 0x0001U //寻址地址是从1开始的 #define REG_INPUT_NREGS 4 #define REG_HOLDING_START ( 1 ) #define REG_HOLDING_NREGS ( 32 ) /* ----------------------- Static variables ---------------------------------*/ static uint16_t usRegInputStart = REG_INPUT_START; static uint16_t usRegInputBuf[REG_INPUT_NREGS]={0x01,0x02,0x03,0x04};//为了验证使用的初始化值 static USHORT usRegHoldingStart = REG_HOLDING_START; static USHORT usRegHoldingBuf[REG_HOLDING_NREGS]; |
|
|
|
添加输入寄存器和保持寄存器的起始地址,定义寄存器大小,定义数组
int main(void) { /* MCU Configuration--------------------------------------------------------*/ /* Reset of all peripherals, Initializes the Flash interface and the Systick. */ HAL_Init(); /* Configure the system clock */ SystemClock_Config(); /* Initialize all configured peripherals */ MX_GPIO_Init(); MX_USART1_UART_Init(); MX_TIM6_Init(); __HAL_TIM_CLEAR_FLAG(&htim6,TIM_FLAG_UPDATE);//是HAL库的一个BUG,初始化完成后会置位UIF eMBInit(MB_RTU, 0x01, 1, 9600, MB_PAR_ODD);//Modbus初始化 eMBEnable();//使能协议栈 /* Infinite loop */ while (1) { eMBPoll(); PBout(0) = !PBout(0);//这是我点亮LED的语句 PBout(1) = !PBout(1); HAL_Delay(100);//轮询时间为100ms } } void __aeabi_assert(const char * x1, const char * x2, int x3) { } freemodbus中使用了断言函数,这里添加函数体解决断言函数找不到的问题 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 - usRegInputStart ); 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( ( 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++ = ( 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--; } } } else { eStatus = MB_ENOREG; } return eStatus; } 在这里实现了输入寄存器和保持寄存器的相关功能函数。 试验0x04功能 试验0x06功能 试验0x03功能 试验0x10功能 0x17功能也可以实现,这里就偷懒不放图了 到这里就全部移植完成了 |
|
|
|
只有小组成员才能发言,加入小组>>
3323 浏览 9 评论
3000 浏览 16 评论
3498 浏览 1 评论
9073 浏览 16 评论
4093 浏览 18 评论
1198浏览 3评论
614浏览 2评论
const uint16_t Tab[10]={0}; const uint16_t *p; p = Tab;//报错是怎么回事?
603浏览 2评论
用NUC131单片机UART3作为打印口,但printf没有输出东西是什么原因?
2343浏览 2评论
NUC980DK61YC启动随机性出现Err-DDR是为什么?
1902浏览 2评论
小黑屋| 手机版| Archiver| 电子发烧友 ( 湘ICP备2023018690号 )
GMT+8, 2024-12-30 23:59 , Processed in 1.726210 second(s), Total 80, Slave 61 queries .
Powered by 电子发烧友网
© 2015 bbs.elecfans.com
关注我们的微信
下载发烧友APP
电子发烧友观察
版权所有 © 湖南华秋数字科技有限公司
电子发烧友 (电路图) 湘公网安备 43011202000918 号 电信与信息服务业务经营许可证:合字B2-20210191 工商网监 湘ICP备2023018690号