1
完善资料让更多小伙伴认识你,还能领取20积分哦, 立即完善>
|
|
相关推荐
2个回答
|
|
困惑了将近一年多的ModbusRTU在我昨天穷极无聊给自己定目标的情况下搞出来了,以前移植不出来主要原因就是基本功不扎实,没有进一步理解串口和定时器配置的原理,一通操作,移植完之后就Timeout,接下来就分享一下我是怎么从0开始移植这个协议的。
项目已上传码云,文章底部有链接! 1.需要的材料 STM32开发板一块,不限型号 freeModbus包可进入后方链接下载 STM32CubeMX 2.操作步骤 操作之前先讲两个主要问题 1.串口设置问题 MoubusRTU移植到stm32平台通信是通过串口进行通信,主要是需要串口进行收发,所以发送中断时必须的,在波特率设置问题上是和定时器相关联,在mbrtu.c文件的eMBRTUInit函数里具体说明了串口波特率和定时器设置的关系 eMBErrorCode eMBRTUInit( UCHAR ucSlaveAddress, UCHAR ucPort, ULONG ulBaudRate, eMBParity eParity ) { eMBErrorCode eStatus = MB_ENOERR; ULONG usTimerT35_50us; ( void )ucSlaveAddress; ENTER_CRITICAL_SECTION( ); /* Modbus RTU uses 8 Databits. */ if( xMBPortSerialInit( ucPort, ulBaudRate, 8, eParity ) != TRUE ) { eStatus = MB_EPORTERR; } else { /* If baudrate 》 19200 then we should use the fixed timer values * t35 = 1750us. Otherwise t35 must be 3.5 times the character time. */ if( ulBaudRate 》 19200 ) { usTimerT35_50us = 35; /* 1750us. */ } else { /* The timer reload value for a character is given by: * * ChTimeValue = Ticks_per_1s / ( Baudrate / 11 ) * = 11 * Ticks_per_1s / Baudrate * = 220000 / Baudrate * The reload for t3.5 is 1.5 times this value and similary * for t3.5. */ usTimerT35_50us = ( 7UL * 220000UL ) / ( 2UL * ulBaudRate ); } if( xMBPortTimersInit( ( USHORT ) usTimerT35_50us ) != TRUE ) { eStatus = MB_EPORTERR; } } EXIT_CRITICAL_SECTION( ); return eStatus; } 从上面代码的注释中可以看出,当波特率大于19200时,超时时间固定位为1750us,当波特率小于19200时,超时时间为3.5个字符时间,具体计算公式在代码注释里已经有了,这里我就不多赘述。本人波特率使用115200,所以按照1750us来。 2.定时器设置问题 ModbusRTU是通过定时器和串口配合来实现Modbus通信的,所以定时器是决定有没有超时的一大关键问题,由串口设置部分可知,定时器设置是要配合串口设置的波特率食用比较香,所以根据我使用的115200波特率可以得到我定时器设置。首先是APB1的主频率获取到,modbus要求通过预分配后得到的周期为50us,对应频率为20KHz。根据rtu初始化代码得到自动重载值设置为35。 具体操作: 熟悉stm32cubemx的老司机可以直接从15步看起 1.选择MCU型号 2.使能时钟源RCC为外部时钟 3.配置时钟树,记录APB1频率,我这里是72MHz 4.使能定时器4,预分频系数为3600-1,对应的分频频率为20KHz,不懂的回到上面去看定时器设置解析,自动重载值设置为35,得到超时时间1750us。 5.使能定时器中断 6.配置串口2,选择异步通信后参数设置为115200,8,NONE,1 7.使能串口中断 8.配置中断优先级,定时器中断优先级低于串口中断即可 9.配置项目参数并分离头文件和c文件后生成代码。 10.打开freeModbus代码包的demo文件夹,新建一个名为STM32MB的文件夹,之后将BARE文件夹内所有内容复制到STM32MB文件夹下,复制完成如图 11.回到freeModbus代码包,复制整个modbus文件夹也粘贴到STM32MB文件夹内,完成效果如图 12.将STM32MB文件夹移动到stm32cubeMX生成的工程目录下,如图 13.打开工程,引入STM32MB内的所有头文件,并新建名为MB和MB_Port的组,MB内添加STM32MB文件夹下modbus文件夹内所有c文件以及根目录的demo.c文件,MB_Port内添加STM32MB文件夹下port文件夹内所有c文件,如图所示 14.修改demo.c文件夹的main函数名为host,编译不报错即可开始修改,如图所示 以下为正式修改Modbus代码,上面比较繁琐,熟悉stm32cubemx的老司机可以直接从15步看起 15.修改MB_Port下的portserial.c文件(串口设置) 我直接贴代码,自己对比我的代码和源码差距,关键地方我会在后边标注 #include “port.h” #include “stm32f7xx_hal.h” #include “usart.h” /* ----------------------- Modbus includes ----------------------------------*/ #include “mb.h” #include “mbport.h” /* ----------------------- static functions ---------------------------------*/ //static void prvvUARTTxReadyISR( void ); //static void prvvUARTRxISR( void ); /* ----------------------- Start implementation -----------------------------*/ void vMBPortSerialEnable( BOOL xRxEnable, BOOL xTxEnable ) { /* If xRXEnable enable serial receive interrupts. If xTxENable enable * transmitter empty interrupts. */ if (xRxEnable) //将串口收发中断和modbus联系起来,下面的串口改为自己使能的串口 { __HAL_UART_ENABLE_IT(&huart2,UART_IT_RXNE); //我用的是串口2,故为&huart2 } else { __HAL_UART_DISABLE_IT(&huart2,UART_IT_RXNE); } if (xTxEnable) { __HAL_UART_ENABLE_IT(&huart2,UART_IT_TXE); } else { __HAL_UART_DISABLE_IT(&huart2,UART_IT_TXE); } } BOOL xMBPortSerialInit( UCHAR ucPORT, ULONG ulBaudRate, UCHAR ucDataBits, eMBParity eParity ) { return TRUE; //改为TURE,串口初始化在usart.c定义,mian函数已完成 } 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 (&huart2 ,(uint8_t *)&ucByte,1,0x01) != HAL_OK ) //添加发送一位代码 return FALSE ; else return TRUE; } 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 (&huart2 ,(uint8_t *)pucByte,1,0x01) != HAL_OK )//添加接收一位代码 return FALSE ; else 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. */ //static void prvvUARTTxReadyISR( void ) //删去前面的static,方便在串口中断使用 { 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. */ //static void prvvUARTRxISR( void ) //删去前面的static,方便在串口中断使用 { pxMBFrameCBByteReceived( ); } 16.修改MB_Port下的porttimer.c文件(定时器设置) 我直接贴代码,自己对比我的代码和源码差距,关键地方我会在后边标注 #include “port.h” #include “stm32f7xx_hal.h” #include “tim.h” /* ----------------------- Modbus includes ----------------------------------*/ #include “mb.h” #include “mbport.h” /* ----------------------- static functions ---------------------------------*/ //static void prvvTIMERExpiredISR( void ); /* ----------------------- Start implementation -----------------------------*/ BOOL xMBPortTimersInit( USHORT usTim1Timerout50us ) //定时器初始化直接返回TRUE,已经在mian函数初始化过 { return TRUE; } inline void vMBPortTimersEnable( ) //使能定时器中断,我用的是定时器4,所以为&htim4 { /* Enable the timer with the timeout passed to xMBPortTimersInit( ) */ __HAL_TIM_CLEAR_IT(&htim4,TIM_IT_UPDATE); __HAL_TIM_ENABLE_IT(&htim4,TIM_IT_UPDATE); __HAL_TIM_SET_COUNTER(&htim4,0); __HAL_TIM_ENABLE(&htim4); } inline void vMBPortTimersDisable( ) //取消定时器中断 { /* Disable any pending timers. */ __HAL_TIM_DISABLE(&htim4); __HAL_TIM_SET_COUNTER(&htim4,0); __HAL_TIM_DISABLE_IT(&htim4,TIM_IT_UPDATE); __HAL_TIM_CLEAR_IT(&htim4,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. */ //static void prvvTIMERExpiredISR( void ) //modbus定时器动作,需要在中断内使用 { ( void )pxMBPortCBTimerExpired( ); } 17.修改完Modbus与stm32的接口文件之后要在port.h文件内定义总中断 位置在port.h文件的32行和33行,修改为如下所示,并在port.h前包含上stm32的hal库,如图所示 #define ENTER_CRITICAL_SECTION( ) __set_PRIMASK(1) //关总中断 #define EXIT_CRITICAL_SECTION( ) __set_PRIMASK(0) //开总中断 #include “stm32f7xx_hal.h” modbus端口函数到此修改完成,接下来是中断函数 18.串口及定时器中断修改 打开工程内的中断文件,是在Application/User–》stm32f7xx_it.c 根据板子不同而不同,区别是stm32f后面的数字。知道是中断管理文件就行 在/* USER CODE BEGIN PFP */后添加以下代码,用于和modbus的串口和定时器功能代码联系 extern void prvvUARTTxReadyISR(void); extern void prvvUARTRxISR(void); extern void prvvTIMERExpiredISR( void ); 找到自己设置的串口中断处理函数,添加如下代码,用于将串口收到的内容移动到modbus功能函数进行处理 void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim) //定时器中断回调函数,用于连接porttimer.c文件的函数 { /* NOTE : This function Should not be modified, when the callback is needed, the __HAL_TIM_PeriodElapsedCallback could be implemented in the user file */ prvvTIMERExpiredISR( ); } 在Application/User–》stm32f7xx_it.c末尾的/* USER CODE BEGIN 1 */添加定时器中断回调函数如下: void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim) //定时器中断回调函数,用于连接porttimer.c文件的函数 { /* NOTE : This function Should not be modified, when the callback is needed, the __HAL_TIM_PeriodElapsedCallback could be implemented in the user file */ prvvTIMERExpiredISR( ); } 到此,串口和定时器的问题已经处理完毕,接下来是modbus的配置 19.modbus功能处理 硬件接口方面结束之后就可以开始写功能了,在MB–》demo.c中有功能示例,我们根据功能示例来修改对应的功能并使能modbus,这里我只说输入寄存器功能,其它的一次类推,就不多赘述。 这里也是直接贴代码,大概说一下,就是自己设置一个数组,将数据放到数组内,并在被读取时根据数据位置将数据返回去 /* ----------------------- Modbus includes ----------------------------------*/ #include “mb.h” #include “mbport.h” /* ----------------------- Defines ------------------------------------------*/ #define REG_INPUT_START 0 #define REG_INPUT_NREGS 5 /* ----------------------- Static variables ---------------------------------*/ static USHORT usRegInputStart = REG_INPUT_START; //static uint16_t usRegInputBuf[REG_INPUT_NREGS]; uint16_t InputBuff[5]; eMBErrorCode eMBRegInputCB( UCHAR * pucRegBuffer, USHORT usAddress, USHORT usNRegs ) { eMBErrorCode eStatus = MB_ENOERR; int iRegIndex; int i; InputBuff[0] = 0x11; InputBuff[1] = 0x22; InputBuff[2] = 0x33; InputBuff[3] = 0x44; if( ( usAddress 》= REG_INPUT_START ) && ( usAddress + usNRegs 《= REG_INPUT_START + REG_INPUT_NREGS ) ) { iRegIndex = ( int )( usAddress - usRegInputStart ); for(i=0;i《usNRegs;i++) { *pucRegBuffer=InputBuff[i+usAddress-1]》》8; pucRegBuffer++; *pucRegBuffer=InputBuff[i+usAddress-1]&0xff; pucRegBuffer++; } } else { eStatus = MB_ENOREG; } return eStatus; } eMBErrorCode eMBRegHoldingCB( UCHAR * pucRegBuffer, USHORT usAddress, USHORT usNRegs, eMBRegisterMode eMode ) { return MB_ENOREG; } eMBErrorCode eMBRegCoilsCB( UCHAR * pucRegBuffer, USHORT usAddress, USHORT usNCoils, eMBRegisterMode eMode ) { return MB_ENOREG; } eMBErrorCode eMBRegDiscreteCB( UCHAR * pucRegBuffer, USHORT usAddress, USHORT usNDiscrete ) { return MB_ENOREG; } 20.modbus启动 启动modbus需要在main函数进行初始化、开启侦听操作,需要添加以下代码,对应位置可在mian函数找到 /* USER CODE BEGIN Includes */ #include “mb.h” #include “mbport.h” /* USER CODE END Includes */ /* USER CODE BEGIN 2 */ eMBInit( MB_RTU, 0x01, 1, 115200, MB_PAR_NONE);//初始化modbus,走modbusRTU,从站地址为0x01,端口为1。 eMBEnable( );//使能modbus /* USER CODE END 2 */ /* USER CODE BEGIN WHILE */ while (1) { /* USER CODE END WHILE */ /* USER CODE BEGIN 3 */ ( void )eMBPoll( );//启动modbus侦听 } /* USER CODE END 3 */ 至此修改完毕,编译下载之后即可使用modbus poll进行连接测试。 |
|
|
|
21.modbus测试
将上述代码编译下载到板子,用TTL转USB接入PC,找到在PC的对应端口即可打开ModbusPoll进行通信测试 代码下载成功后打开ModbusPoll,打开读写定义并设置为从站地址1,功能04读输入寄存器,起始地址0,长度为4,如图所示 按F3进行连接,连接设置如图,串口所在位置会显示TTL转串口的芯片型号,按照如下设定后确定。 即可得到下图,由于我们输入寄存器存放的是16进制数,所以要将ModbusPoll显示模式改为16进制才能显示相同内容 最终效果如下图,ModbusPoll读取的值与STM32内寄存器内的值一致,读取成功! 至此,freeModbusRTU移植成功! |
|
|
|
只有小组成员才能发言,加入小组>>
调试STM32H750的FMC总线读写PSRAM遇到的问题求解?
1820 浏览 1 评论
X-NUCLEO-IHM08M1板文档中输出电流为15Arms,15Arms是怎么得出来的呢?
1634 浏览 1 评论
1104 浏览 2 评论
STM32F030F4 HSI时钟温度测试过不去是怎么回事?
740 浏览 2 评论
ST25R3916能否对ISO15693的标签芯片进行分区域写密码?
1692 浏览 2 评论
1951浏览 9评论
STM32仿真器是选择ST-LINK还是选择J-LINK?各有什么优势啊?
756浏览 4评论
STM32F0_TIM2输出pwm2后OLED变暗或者系统重启是怎么回事?
587浏览 3评论
605浏览 3评论
stm32cubemx生成mdk-arm v4项目文件无法打开是什么原因导致的?
569浏览 3评论
小黑屋| 手机版| Archiver| 德赢Vwin官网 ( 湘ICP备2023018690号 )
GMT+8, 2024-12-31 00:23 , Processed in 0.765796 second(s), Total 76, Slave 60 queries .
Powered by 德赢Vwin官网 网
© 2015 bbs.elecfans.com
关注我们的微信
下载发烧友APP
德赢Vwin官网 观察
版权所有 © 湖南华秋数字科技有限公司
德赢Vwin官网 (电路图) 湘公网安备 43011202000918 号 电信与信息服务业务经营许可证:合字B2-20210191 工商网监 湘ICP备2023018690号