转载自:http://forum.eepw.com.cn/thread/334805/1 作者:zhuzhaokun1987
0. 为什么要移植free modbus
大家好,近期由于一个小项目的需要,要用到Modbus协议进行通信。相信各位工作的小伙伴们,或多或少都要跟Modbus打交道吧。那么,Modbus协议的重要性我自不必多言,相信大家都心知肚明。
现如今,我们在很多的工控设备上都会看到一个叫Modbus的协议,甚至你要读一个温度、电压、电流都要使用Modbus协议。因此在设备中移植Modbus协议变成了一个不得要做的事,因此使用Modbus协议已经成为了一种不可缺少的工作技能。
幸运的是,现在有很多的开源的modbus代码,可以方便我们快速的应用Modbus协议进行进一步的工作。例如,FreeModbus、LibModbus等。但移植Modbus也不是一件简单的事情,本系列就以FreeModbus为例进行移植,将其移植到STM32中使用。FreeModbus是一个优秀的应用层协议,它很简洁也相对完善,对于还没有接触过modbus的朋友来说也能很快上手,对于以前已经熟悉过Modbus的人来说,那简直更是轻而易举了。
简单科普一下,FreeModbus是一个奥地利人写的Modbus协议。它是一个针对嵌入式应用的一个免费(自由)的通用MODBUS协议的移植(摘自百度百科)。好了言归正传,我们一起来移植Freemodbus。
1. free modbus library 源码的获取
如果您下决心要研究一下Free Modbus的话,可以访问他们的官网:https://www.embedded-solutions.at/en/freemodbus/在freemodbus DownLoads界面中,可以免费下载freemodbus V1.6,以及freemodbus的源码,如有需要,还请大家点击上面的链接,动动发财手亲自下载,这里就暂且略过。
2. free modbus library 介绍
下载完毕后,不要心急,一看解压后一大堆文件,更不要怕,分类很重重要!free modbus -V1.6,主要包括demo modbus doc tools 四个文件夹,具体如下图所示:
Demo
文件夹中主要free modbus官方为我们新建好的各种平台的测试例程,加快我们的开发进度,其中包括
Win32平台、Linux平台、ARM平台等。我们需要移植到STM32单片机也属于ARM平台的范畴,因此,在我们移植的过程中可以参考ARM平台已经新建好的测试工程。Demo
文件夹下具体测试平台工程如下所示:
Modbus文件夹下,主要放一些关于Modbus自身协议的源码,其中包括Modbus-Rtu、Modbus-Ascii、Modbus-Tcp等,具体如下图所示,移植的过程中,可根据实际情况的需要对该文件夹进行适当的裁剪。
至于doc和tools文件夹就不再赘述,doc主要放一些帮助和说明文件,tools就是放置一些需要的工具。Free modbus library 就介绍这么多,下面开始从0到1完成在stm32平台上的移植。(---未完待续哦---)
3.从0到1移植freemodbus到stm32平台
移植之前需要准备:STM32基础工程(标准库、HAL库均可);FreeModbus Library V1.6;IDE(这里选择常用的MDK);如果需要添加操作系统的,可将STM32的基础工程改为带有操作系统的基础工程,比如常用的FreeRTOS、RT-Thread等。
01.复制正点原子战舰V3库函基础工程,在此工程上添加FreeModbus,先在工程中新建一个Modbus文件,并将freemodbus v1.6下的modbus文件夹下的文件全部复制过来,再将Demo文件夹下的BARE文件夹复制过来,Modbus文件内的具体内容如下:
02.使用MDK打开基础工程,向工程中添加Modbus分组,将Modbus文件夹下对应的文件都添加到Modbus分组中,主要包括:ascii文件夹的mbascii.c文件;function文件夹下的所有.c文件;rtu和tcp文件夹下的.c文件;mb.c文件。添加后的效果如下:
03.添加对应的头文件,工程需要添加以下文件夹:ascii文件夹、BARE/port文件夹、include文件夹、rtu文件夹、tcp文件夹;总之有.h的文件夹统统包含,具体如下:
04.需要对添加过来部分文件进行补充,以完成Modbus对串口和定时器的需要,第一个需要补充的是portserial.c文件,补充Modbus串口发送中断和接收中断使能函数、Modbus串口初始化函数xMBPortSerialInit,具体代码如下:(这里只实现了232功能,没有加485,其实两个代码几乎一致)
view plaincopy to clipboardprint?
- //该函数实现STM32串口发送中断和接收中断使能
- void vMBPortSerialEnable( BOOL xRxEnable, BOOL xTxEnable )
- {
- //STM32串口 接收中断使能
- if(xRxEnable==TRUE)
- {
- //使能接收和接收中断
- USART_ITConfig(MODBUS_USART, USART_IT_RXNE, ENABLE);
- }
- else if(xRxEnable == FALSE)
- {
- //禁止接收和接收中断
- USART_ITConfig(MODBUS_USART, USART_IT_RXNE, DISABLE);
- }
- //STM32串口 发送中断使能
- if(xTxEnable==TRUE)
- {
- //使能发送完成中断
- USART_ITConfig(MODBUS_USART, USART_IT_TXE, ENABLE);
- }
- else if(xTxEnable == FALSE)
- {
- //禁止发送完成中断
- USART_ITConfig(MODBUS_USART, USART_IT_TXE, DISABLE);
- }
- } else if(xTxEnable == FALSE)
- {
- MODBUS_RECIEVE();
- USART_ITConfig(MODBUS_USART, USART_IT_TC, DISABLE);
- }
- }
- /*******************************************************************/
- //对串口进行初始化由eMBRTUInt函数进行调用
- BOOL xMBPortSerialInit( UCHAR ucPORT, ULONG ulBaudRate, UCHAR ucDataBits, eMBParity eParity )
- {
- GPIO_InitTypeDef GPIO_InitStructure;
- USART_InitTypeDef USART_InitStructure;
- NVIC_InitTypeDef NVIC_InitStructure;
- (void)ucPORT; //不修改串口
- (void)ucDataBits; //不修改数据长度
- (void)eParity; //不许改效验格式
- /***引脚初始化*************************************/
-
- //时钟使能
- RCC_APB2PeriphClockCmd(MODBUS_USART_GPIO_CLK,ENABLE);
- RCC_APB1PeriphClockCmd(MODBUS_USART_CLK,ENABLE);
- //TX
- GPIO_InitStructure.GPIO_Pin = MODBUS_USART_TX_PIN;
- GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
- GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //复用推挽输出
- GPIO_Init(MODBUS_USART_TX_PORT, &GPIO_InitStructure);
- //RX
- GPIO_InitStructure.GPIO_Pin = MODBUS_USART_RX_PIN;
- GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;//浮空输入
- GPIO_Init(MODBUS_USART_RX_PORT, &GPIO_InitStructure);
-
- /***************串口初始化********************/
- USART_InitStructure.USART_BaudRate = ulBaudRate;
- USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
- USART_InitStructure.USART_Mode = USART_Mode_Rx|USART_Mode_Tx;
- USART_InitStructure.USART_Parity = USART_Parity_No;
- USART_InitStructure.USART_StopBits = USART_StopBits_1;
- USART_InitStructure.USART_WordLength = USART_WordLength_8b;
- USART_Init(MODBUS_USART, &USART_InitStructure);
- USART_Cmd(MODBUS_USART, ENABLE);
-
- /*****************************中断初始化*************************************/
- NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1);
- NVIC_InitStructure.NVIC_IRQChannel = MODBUS_USART_IRQ ;
- NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=0 ;//抢占优先级0
- NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0; //子优先级0
- NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ通道使能
- NVIC_Init(&NVIC_InitStructure); //根据指定的参数初始化VIC寄存器
-
- return TRUE;
- }
- /*******************************************************************/
|
05.补充串口发送函数和接收函数、中断处理函数,将STM32串口发送函数和接收函数进行封装,供协议栈使用。代码如下:
view plaincopy to clipboardprint?
- //串口发送
- 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(MODBUS_USART, ucByte); //??????
- while (USART_GetFlagStatus(MODBUS_USART, USART_FLAG_TC) == RESET){};
- 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.
- */
- *pucByte = USART_ReceiveData(MODBUS_USART);
- return TRUE;
- }
- /*******************************************************************/
- //串口中断处理函数
- void MODBUS_USART_IRQHandler(void)
- {
- if(USART_GetITStatus(MODBUS_USART, USART_IT_TXE) == SET)
- {
- prvvUARTTxReadyISR();
- USART_ClearITPendingBit(MODBUS_USART, USART_IT_TXE);
- }
-
- if(USART_GetITStatus(MODBUS_USART, USART_IT_RXNE) == SET)
- {
- prvvUARTRxISR();
- USART_ClearITPendingBit(MODBUS_USART, USART_IT_RXNE);
- }
- }
|
06.第二个需要补充的是porttimerl.c文件,需要补充的就是Modbus定时器初始化函数、Modbus定时器使能和失能函数,以及Modbus 定时器中断函数。这个对于熟悉stm32编程的就是分分中的事。具体代码如下:
view plaincopy to clipboardprint?
- //Modbus定时器初始化函数
- BOOL xMBPortTimersInit( USHORT usTim1Timerout50us )
- {
- TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
- NVIC_InitTypeDef NVIC_InitStructure;
- uint16_t PrescalerValue = 0;
- //使能定时器4的时钟
- RCC_APB1PeriphClockCmd(MODBUS_TIM_CLK, ENABLE); //时钟使能
- //定时器4时间配置说明
- //HCLK为72MHz,APB1经2分频为36MHz
- //TIM4时钟倍频后为72MHz(硬件自动倍频,达到最大)
- //TIM4的分频系数为3599,时间基频率为:72 / (1 + Prescaler) = 20KHz,基准为50us
- //TIM最大计数值为:usTim1Timerout50u
- PrescalerValue = (uint16_t) (SystemCoreClock / 20000) - 1;
- //定时器TIM4初始化
- TIM_TimeBaseStructure.TIM_Period = usTim1Timerout50us;
- TIM_TimeBaseStructure.TIM_Prescaler =PrescalerValue;
- TIM_TimeBaseStructure.TIM_ClockDivision = 0;
- TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
- TIM_TimeBaseInit(MODBUS_TIM, &TIM_TimeBaseStructure);
- //使能预装载
- TIM_ARRPreloadConfig(MODBUS_TIM, ENABLE);
-
- //中断优先级NVIC设置
- NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1);
- NVIC_InitStructure.NVIC_IRQChannel = MODBUS_TIM_IRQ ;
- NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
- NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3;
- NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
- NVIC_Init(&NVIC_InitStructure);
- //清除溢出中断标志位
- TIM_ClearITPendingBit(MODBUS_TIM, TIM_IT_Update);
- //定时器溢出中断关闭
- TIM_ITConfig(MODBUS_TIM,TIM_IT_Update,DISABLE);
- //失能定时器
- TIM_Cmd(MODBUS_TIM, DISABLE);
- return TRUE;
- }
- /*******************************************************************/
- //Modbus定时器使能函数
- void vMBPortTimersEnable()
- {
- /* Enable the timer with the timeout passed to xMBPortTimersInit( ) */
- TIM_ClearITPendingBit(MODBUS_TIM, TIM_IT_Update);
- TIM_ITConfig(MODBUS_TIM, TIM_IT_Update, ENABLE);
- //设置定时器的初值
- TIM_SetCounter(MODBUS_TIM,0x0000);
- TIM_Cmd(MODBUS_TIM, ENABLE);
- }
- /*******************************************************************/
- //Modbus定时器失能函数
- void vMBPortTimersDisable()
- {
- /* Disable any pending timers. */
- TIM_ClearITPendingBit(MODBUS_TIM, TIM_IT_Update);
- TIM_ITConfig(MODBUS_TIM, TIM_IT_Update, DISABLE);
- TIM_SetCounter(MODBUS_TIM,0x0000);
- //关闭定时器
- TIM_Cmd(MODBUS_TIM, DISABLE);
- }
- /*******************************************************************/
- //Modbus 定时器中断函数
- void MODBUS_TIM_IRQHandler( void )
- {
- if(TIM_GetITStatus(MODBUS_TIM, TIM_IT_Update) == SET)
- {
- TIM_ClearITPendingBit(MODBUS_TIM, TIM_IT_Update);
- prvvTIMERExpiredISR();
- }
- }
|
07.第三个需要修改的文件是port.h文件,这里文件我放置了一些需要定义的宏,方便后面修改,万一哪一天Modbus、串口2、定时器4打起来了,那我有的忙了,一个伟大程序员总是在考虑谁会打起来,不希望出现劝架的那一天。port.h文件中主要补充进入临界区和退出临界区的宏定义、portserial.c 文件中用到的宏、porttimer.c 文件中用到的宏。(注:是补充哦!不是全部的port.h文件内容,不要傻傻的去全部替换!)
view plaincopy to clipboardprint?
- #define ENTER_CRITICAL_SECTION( ) __set_PRIMASK(1); //关闭中断
- #define EXIT_CRITICAL_SECTION( ) __set_PRIMASK(0); //开启中断
- //Modbus串口
- #define MODBUS_USART USART2
- #define MODBUS_USART_CLK RCC_APB1Periph_USART2
- #define MODBUS_USART_GPIO_CLK RCC_APB2Periph_GPIOA
- #define MODBUS_USART_TX_PORT GPIOA
- #define MODBUS_USART_RX_PORT GPIOA
- #define MODBUS_USART_TX_PIN GPIO_Pin_2
- #define MODBUS_USART_RX_PIN GPIO_Pin_3
- #define MODBUS_USART_IRQ USART2_IRQn
- #define MODBUS_USART_IRQHandler USART2_IRQHandler
- //Modbus定时器
- #define MODBUS_TIM TIM4
- #define MODBUS_TIM_CLK RCC_APB1Periph_TIM4
- #define MODBUS_TIM_IRQ TIM4_IRQn
- #define MODBUS_TIM_IRQHandler TIM4_IRQHandler
|
08.编译之后,果然不出所料,有错误。学会修改错误,是一个程序猿的基本修养。如果连这点基本修养都没有,那只能说,路漫漫其修远兮,还得继续上下左右之求索。在这里我们尝试着去删除porttimer.c 中定时器使能和失能函数前的inline 字样,咦,奇怪的东西消失了呢。
09.当然困难不止一个,就像当前的新冠病毒,一波未平一波又起。再次编译,出现关于assert的错误...........,不要挠头皮,继续想法搞定吧!于是打开搜索引擎,开始找对应的解决办法的过程。
10. 搜索并思考了很久,偶然在CSDN上看到一位博主是这么解决的:在主函数下面添加以下代码(代码在下方),即可解决以上问题,硬着头皮先试一试(因为暂时不相信也没有其它办法),复制过来,直接编译,居然轻松解决,至于原因嘛,就留给各位读者自己思索咯。
view plaincopy to clipboardprint?
- #ifdef USE_FULL_ASSERT
- /**
- * @brief Reports the name of the source file and the source line number
- * where the assert_param error has occurred.
- * @param file: pointer to the source file name
- * @param line: assert_param error line source number
- * @retval None
- */
- void assert_failed(uint8_t* file, uint32_t line)
- {
- /* User can add his own implementation to report the file name and line number,
- ex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */
-
- /* Infinite loop */
- while (1)
- {
- }
- }
- #else
- void __aeabi_assert(const char * x1, const char * x2, int x3)
- {
- }
- #endif
|
好了各位小伙伴,到这里FreeModbus已经移植到STM32平台上了,下一步就是测试验证移植的FreeModbus是否可以正常通讯。(验证方式和全部代码下载,请见下一帖)
4.freemodbus在stm32平台测试与调试
一个不会打仗的士兵不是好士兵,整天在兵营里吹牛x,偶尔还放空枪,这样的还放下狠话,要灭人家一个师,就没见过这么厚脸皮的。我们要学习独立团团长李云龙,别扯那多没有用的,有真本事“亮剑”见识一下。So,我们今天就来试一试移植的效果,一起调一调主函数。
先来分析一下,想要能请得动FreeModbus这位大佬,要想让它为我们服务,首先需要给它伺候舒服了,也就是需要先对FreeModbus进行初始化。初始化的步骤其实就是调用eMBInit函数和eMBEnable函数,温馨补充一下照顾一下腼腆的孩子们:eMBInit函数函数是设置硬件的,有五个参数,分别是:
参数1.Modbus的模式:这里可以选择RTU、ASCII、TCP,这里我们选择RTU模式(MB_RTU);
参数2.设备的地址:这里可以直接写死(0x01),也可以通过拨码开关来设置;
参数3.Modbus选择的串口号:这里默认是串口2(0x02);
参数4.波特率:默认写115200,我们这里配置为9600;
参数5.校验位:默认不效验(MB_PAR_NONE)。
然后在主循环中调用eMBPoll函数不断查询。int main()函数具体代码如下:
view plaincopy to clipboardprint?
- int main(void)
- {
- delay_init(); //延时函数初始化
- //初始化 RTU模式 从机地址为1 USART2 9600 无效验
- eMBInit(MB_RTU, 0x01, 0x02, 9600, MB_PAR_NONE);
- //启动FreeModbus
- eMBEnable();
- while(1)
- {
- //FreeMODBUS不断查询
- eMBPoll();
- }
- }
|
01.当然,仅仅拿个main函数出来,是不能够证明FreeModbus就跑起来了,我们再来添加点测试部分代码,以观其效吧!测试代码部分,主要添加输入寄存器、保持寄存器、线圈的起始地址和定义的内容,以及输入寄存器、保持寄存器、线圈的处理函数,具体如下:
view plaincopy to clipboardprint?
- /* ----------------------- Defines ------------------------------------------*/
- //添加输入寄存器、保持寄存器、线圈的起始地址和定义的内容
- //保持寄存器起始地址
- #define REG_HOLDING_START 0x0001
- //保持寄存器数量
- #define REG_HOLDING_NREGS 8
- //保持寄存器内容
- uint16_t usRegHoldingBuf[REG_HOLDING_NREGS] ={0x147b,0x3f8e,0x147b,0x400e,0x1eb8,0x4055,0x147b,0x408e};
-
- //输入寄存器起始地址
- #define REG_INPUT_START 0x0001
- //输入寄存器数量
- #define REG_INPUT_NREGS 8
- //输入寄存器内容
- uint16_t usRegInputBuf[REG_INPUT_NREGS] ={0x1111,0x2222,0x3333,0x4444,0x5555,0x6666,0x7777,0x8888};
-
- //线圈寄存器起始地址
- #define REG_COILS_START 0x0001
- //线圈寄存器数量
- #define REG_COILS_SIZE 16
- //线圈寄存器内容
- uint8_t ucRegCoilsBuf[REG_COILS_SIZE/8] = {0x00,0xFF};
- /***********************************************************************/
- 输入寄存器、保持寄存器、线圈的处理函数
- // 保持寄存器的读写函数 支持的命令为读 0x03 和写0x06 可读可写
- eMBErrorCode
- eMBRegHoldingCB( UCHAR * pucRegBuffer, USHORT usAddress, USHORT usNRegs,eMBRegisterMode eMode )
- {
- //错误状态
- eMBErrorCode eStatus = MB_ENOERR;
- //偏移量
- int16_t iRegIndex;
-
- //判断寄存器是不是在范围内
- if( ( (int16_t)usAddress >= REG_HOLDING_START )&& ( usAddress + usNRegs <= REG_HOLDING_START + REG_HOLDING_NREGS ) )
- {
- //计算偏移量
- iRegIndex = ( int16_t )( usAddress - REG_HOLDING_START);
- switch ( eMode )
- {
- //读处理函数
- case MB_REG_READ:
- while( usNRegs > 0 )
- {
- *pucRegBuffer++ = ( uint8_t )( usRegHoldingBuf[iRegIndex] >> 8 );
- *pucRegBuffer++ = ( uint8_t )( 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;
- }
-
- //读输入寄存器函数 支持的命令为读 0x04
- 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;
- }
-
- //线圈处理函数 可读可写 支持的命令为读 0x01 0x05
- 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;
- return MB_ENOREG;
- }
|
02.好了,测试代码准备就绪,下面就是见证奇迹的时刻?我们使用ModbusPoll软件测试版来完成对移植FreeModbus rtu的测试,如下图,该代码能够成功的读出输入寄存器、保持寄存器、线圈的数据。
03. 通信正常,则可以看到USB转串口工具的TX和RX指示灯不停闪烁着。想象一下突然有一天,人类的眼睛可以分辨出100MHz以上的闪烁频率时,我们的世界将会如何?emmmm....这个脑洞可能开的有点大,留给大家去发挥想象力吧。如果我们能够分辨更高的频率,那么我们的电脑手机显示器的144Hz的刷新率,就再也不香了吧,哈哈~!
综上可以看出,本次对FreeModbus的移植工作已经完成了,该平台虽然是基于STM32F1xx,但是使用其他STM32的芯片也非常的方便,过程大致相同,简单改一下就好了。从通过ModbusPoll工具进行测试的结果看来,整个工程基本能够达到预期效果,当然本人技术有限,还请各位大佬们不吝赐教。最后贴上main测试完整代码供大家参考。
view plaincopy to clipboardprint?
- #include "sys.h"
- #include "delay.h"
- #include "usart.h"
- #include "led.h"
-
- #include "mb.h"
- #include "mbport.h"
-
- /* ----------------------- Defines ------------------------------------------*/
- //保持寄存器起始地址
- #define REG_HOLDING_START 0x0001
- //保持寄存器数量
- #define REG_HOLDING_NREGS 8
- //保持寄存器内容
- uint16_t usRegHoldingBuf[REG_HOLDING_NREGS] ={0x147b,0x3f8e,0x147b,0x400e,0x1eb8,0x4055,0x147b,0x408e};
-
- //输入寄存器起始地址
- #define REG_INPUT_START 0x0001
- //输入寄存器数量
- #define REG_INPUT_NREGS 8
- //输入寄存器内容
- uint16_t usRegInputBuf[REG_INPUT_NREGS] ={0x1111,0x2222,0x3333,0x4444,0x5555,0x6666,0x7777,0x8888};
-
- //线圈寄存器起始地址
- #define REG_COILS_START 0x0001
- //线圈寄存器数量
- #define REG_COILS_SIZE 16
- //线圈寄存器内容
- uint8_t ucRegCoilsBuf[REG_COILS_SIZE/8] = {0x00,0xFF};
-
- int main(void)
- {
- delay_init(); //延时函数初始化
- //初始化 RTU模式 从机地址为1 USART2 9600 无效验
- eMBInit(MB_RTU, 0x01, 0x02, 9600, MB_PAR_NONE);
- //启动FreeModbus
- eMBEnable();
- while(1)
- {
- //FreeMODBUS不断查询
- eMBPoll();
- }
- }
-
- // 保持寄存器的读写函数 支持的命令为读 0x03 和写0x06 可读可写
- eMBErrorCode
- eMBRegHoldingCB( UCHAR * pucRegBuffer, USHORT usAddress, USHORT usNRegs,eMBRegisterMode eMode )
- {
- //错误状态
- eMBErrorCode eStatus = MB_ENOERR;
- //偏移量
- int16_t iRegIndex;
-
- //判断寄存器是不是在范围内
- if( ( (int16_t)usAddress >= REG_HOLDING_START )&& ( usAddress + usNRegs <= REG_HOLDING_START + REG_HOLDING_NREGS ) )
- {
- //计算偏移量
- iRegIndex = ( int16_t )( usAddress - REG_HOLDING_START);
- switch ( eMode )
- {
- //读处理函数
- case MB_REG_READ:
- while( usNRegs > 0 )
- {
- *pucRegBuffer++ = ( uint8_t )( usRegHoldingBuf[iRegIndex] >> 8 );
- *pucRegBuffer++ = ( uint8_t )( 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;
- }
-
- //读输入寄存器函数 支持的命令为读 0x04
- 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;
- }
-
- //线圈处理函数 可读可写 支持的命令为读 0x01 0x05
- 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;
- return MB_ENOREG;
- }
-
-
- //开关输入寄存器处理函数 可读 支持的命令为读 0x02
- eMBErrorCode
- eMBRegDiscreteCB( UCHAR * pucRegBuffer, USHORT usAddress, USHORT usNDiscrete )
- {
- return MB_ENOREG;
- }
-
- //针对 assert错误添加的函数
- #ifdef USE_FULL_ASSERT
- /**
- * @brief Reports the name of the source file and the source line number
- * where the assert_param error has occurred.
- * @param file: pointer to the source file name
- * @param line: assert_param error line source number
- * @retval None
- */
- void assert_failed(uint8_t* file, uint32_t line)
- {
- /* User can add his own implementation to report the file name and line number,
- ex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */
-
- /* Infinite loop */
- while (1)
- {
- }
- }
- #else
- void __aeabi_assert(const char * x1, const char * x2, int x3)
- {
- }
- #endif
|
本系列到此就全部结束了,大家好,我是大Z,感谢大家支持~
标签:MODBUS,USART,Modbus,手把手,STM32,TIM,FreeModbus,寄存器,REG
来源: https://www.cnblogs.com/mangorange/p/14228119.html