ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

手把手教你移植FreeModbus到STM32(一)

2021-01-04 09:35:43  阅读:892  来源: 互联网

标签:MODBUS USART Modbus 手把手 STM32 TIM FreeModbus 寄存器 REG


转载自: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的源码,如有需要,还请大家点击上面的链接,动动发财手亲自下载,这里就暂且略过。 

 

图片1.png

 

2. free modbus library 介绍

下载完毕后,不要心急,一看解压后一大堆文件,更不要怕,分类很重重要!free modbus -V1.6,主要包括demo  modbus doc tools 四个文件夹,具体如下图所示:

 

图片2.png

 

Demo 文件夹中主要free modbus官方为我们新建好的各种平台的测试例程,加快我们的开发进度,其中包括 Win32平台、Linux平台、ARM平台等。我们需要移植到STM32单片机也属于ARM平台的范畴,因此,在我们移植的过程中可以参考ARM平台已经新建好的测试工程。Demo 文件夹下具体测试平台工程如下所示:

 

图片3.png

Modbus文件夹下,主要放一些关于Modbus自身协议的源码,其中包括Modbus-Rtu、Modbus-Ascii、Modbus-Tcp等,具体如下图所示,移植的过程中,可根据实际情况的需要对该文件夹进行适当的裁剪。

图片4.png

至于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文件内的具体内容如下:

 

图片5.png

 

02.使用MDK打开基础工程,向工程中添加Modbus分组,将Modbus文件夹下对应的文件都添加到Modbus分组中,主要包括:ascii文件夹的mbascii.c文件;function文件夹下的所有.c文件;rtu和tcp文件夹下的.c文件;mb.c文件。添加后的效果如下:

 

图片6.png

 

03.添加对应的头文件,工程需要添加以下文件夹:ascii文件夹、BARE/port文件夹、include文件夹、rtu文件夹、tcp文件夹;总之有.h的文件夹统统包含,具体如下:

 

图片7.png

 

04.需要对添加过来部分文件进行补充,以完成Modbus对串口和定时器的需要,第一个需要补充的是portserial.c文件,补充Modbus串口发送中断和接收中断使能函数、Modbus串口初始化函数xMBPortSerialInit,具体代码如下:(这里只实现了232功能,没有加485,其实两个代码几乎一致)

 

 

 

view plaincopy to clipboardprint?
  1. //该函数实现STM32串口发送中断和接收中断使能  
  2. void vMBPortSerialEnable( BOOL xRxEnable, BOOL xTxEnable )  
  3. {  
  4. //STM32串口 接收中断使能  
  5. if(xRxEnable==TRUE)   
  6. {   
  7.  //使能接收和接收中断  
  8.      USART_ITConfig(MODBUS_USART, USART_IT_RXNE, ENABLE);  
  9.   }   
  10. else if(xRxEnable == FALSE)  
  11. {   
  12.      //禁止接收和接收中断    
  13.      USART_ITConfig(MODBUS_USART, USART_IT_RXNE, DISABLE);  
  14.   }  
  15.   //STM32串口 发送中断使能  
  16.    if(xTxEnable==TRUE)   
  17. {  
  18.      //使能发送完成中断  
  19.      USART_ITConfig(MODBUS_USART, USART_IT_TXE, ENABLE);  
  20.   }   
  21. else if(xTxEnable == FALSE)   
  22. {  
  23.      //禁止发送完成中断  
  24.      USART_ITConfig(MODBUS_USART, USART_IT_TXE, DISABLE);  
  25.   }  
  26. } else if(xTxEnable == FALSE)   
  27. {  
  28.      MODBUS_RECIEVE();  
  29.      USART_ITConfig(MODBUS_USART, USART_IT_TC, DISABLE);  
  30.   }  
  31. }  
  32. /*******************************************************************/  
  33. //对串口进行初始化由eMBRTUInt函数进行调用  
  34. BOOL xMBPortSerialInit( UCHAR ucPORT, ULONG ulBaudRate, UCHAR ucDataBits, eMBParity eParity )  
  35. {  
  36.  GPIO_InitTypeDef  GPIO_InitStructure;  
  37.  USART_InitTypeDef USART_InitStructure;  
  38.  NVIC_InitTypeDef  NVIC_InitStructure;  
  39. (void)ucPORT; //不修改串口  
  40.    (void)ucDataBits; //不修改数据长度  
  41.    (void)eParity; //不许改效验格式  
  42.    /***引脚初始化*************************************/  
  43.    
  44.    //时钟使能  
  45.  RCC_APB2PeriphClockCmd(MODBUS_USART_GPIO_CLK,ENABLE);  
  46.  RCC_APB1PeriphClockCmd(MODBUS_USART_CLK,ENABLE);  
  47.    //TX  
  48.    GPIO_InitStructure.GPIO_Pin = MODBUS_USART_TX_PIN;  
  49.  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;  
  50.    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //复用推挽输出  
  51.    GPIO_Init(MODBUS_USART_TX_PORT, &GPIO_InitStructure);  
  52.    //RX  
  53.    GPIO_InitStructure.GPIO_Pin = MODBUS_USART_RX_PIN;  
  54.  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;//浮空输入  
  55.    GPIO_Init(MODBUS_USART_RX_PORT, &GPIO_InitStructure);  
  56.    
  57.   /***************串口初始化********************/  
  58.    USART_InitStructure.USART_BaudRate    = ulBaudRate;  
  59.    USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;  
  60.    USART_InitStructure.USART_Mode        = USART_Mode_Rx|USART_Mode_Tx;  
  61.    USART_InitStructure.USART_Parity      = USART_Parity_No;  
  62.    USART_InitStructure.USART_StopBits    = USART_StopBits_1;  
  63.    USART_InitStructure.USART_WordLength  = USART_WordLength_8b;  
  64. USART_Init(MODBUS_USART, &USART_InitStructure);  
  65.    USART_Cmd(MODBUS_USART, ENABLE);  
  66.      
  67.    /*****************************中断初始化*************************************/  
  68.  NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1);  
  69.  NVIC_InitStructure.NVIC_IRQChannel = MODBUS_USART_IRQ ;  
  70.  NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=0 ;//抢占优先级0  
  71.  NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;  //子优先级0  
  72.  NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;   //IRQ通道使能  
  73.  NVIC_Init(&NVIC_InitStructure); //根据指定的参数初始化VIC寄存器  
  74.    
  75.  return TRUE;  
  76. }  
  77. /*******************************************************************/  

 

 

05.补充串口发送函数和接收函数、中断处理函数,将STM32串口发送函数和接收函数进行封装,供协议栈使用。代码如下:

 

 

 

view plaincopy to clipboardprint?
  1. //串口发送  
  2. BOOL xMBPortSerialPutByte( CHAR ucByte )  
  3. {  
  4.     /* Put a byte in the UARTs transmit buffer. This function is called 
  5.      * by the protocol stack if pxMBFrameCBTransmitterEmpty( ) has been 
  6.      * called. */  
  7.   USART_SendData(MODBUS_USART, ucByte);  //??????  
  8.   while (USART_GetFlagStatus(MODBUS_USART, USART_FLAG_TC) == RESET){};   
  9.     return TRUE;  
  10. }  
  11. /*******************************************************************/  
  12. //串口接收  
  13. BOOL xMBPortSerialGetByte( CHAR * pucByte )  
  14. {  
  15.     /* Return the byte in the UARTs receive buffer. This function is called 
  16.      * by the protocol stack after pxMBFrameCBByteReceived( ) has been called. 
  17.      */  
  18.  *pucByte = USART_ReceiveData(MODBUS_USART);    
  19.     return TRUE;  
  20. }  
  21. /*******************************************************************/  
  22. //串口中断处理函数  
  23. void MODBUS_USART_IRQHandler(void)  
  24. {  
  25.    if(USART_GetITStatus(MODBUS_USART, USART_IT_TXE) == SET)   
  26.  {  
  27.      prvvUARTTxReadyISR();  
  28.      USART_ClearITPendingBit(MODBUS_USART, USART_IT_TXE);  
  29.    }  
  30.    
  31.    if(USART_GetITStatus(MODBUS_USART, USART_IT_RXNE) == SET)   
  32.  {  
  33.      prvvUARTRxISR();  
  34.      USART_ClearITPendingBit(MODBUS_USART, USART_IT_RXNE);  
  35.    }  
  36. }  

 

 

06.第二个需要补充的是porttimerl.c文件,需要补充的就是Modbus定时器初始化函数、Modbus定时器使能和失能函数,以及Modbus 定时器中断函数。这个对于熟悉stm32编程的就是分分中的事。具体代码如下:

 

 

 

view plaincopy to clipboardprint?
  1. //Modbus定时器初始化函数  
  2. BOOL xMBPortTimersInit( USHORT usTim1Timerout50us )  
  3. {  
  4.   TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;  
  5. NVIC_InitTypeDef NVIC_InitStructure;  
  6.    uint16_t PrescalerValue = 0;  
  7. //使能定时器4的时钟  
  8. RCC_APB1PeriphClockCmd(MODBUS_TIM_CLK, ENABLE); //时钟使能  
  9. //定时器4时间配置说明  
  10. //HCLK为72MHz,APB1经2分频为36MHz  
  11.   //TIM4时钟倍频后为72MHz(硬件自动倍频,达到最大)  
  12.   //TIM4的分频系数为3599,时间基频率为:72 / (1 + Prescaler) = 20KHz,基准为50us  
  13.   //TIM最大计数值为:usTim1Timerout50u  
  14. PrescalerValue = (uint16_t) (SystemCoreClock / 20000) - 1;   
  15. //定时器TIM4初始化  
  16. TIM_TimeBaseStructure.TIM_Period = usTim1Timerout50us;   
  17. TIM_TimeBaseStructure.TIM_Prescaler =PrescalerValue;  
  18.   TIM_TimeBaseStructure.TIM_ClockDivision = 0;   
  19. TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;    
  20. TIM_TimeBaseInit(MODBUS_TIM, &TIM_TimeBaseStructure);   
  21. //使能预装载  
  22.   TIM_ARRPreloadConfig(MODBUS_TIM, ENABLE);  
  23.    
  24. //中断优先级NVIC设置  
  25. NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1);  
  26. NVIC_InitStructure.NVIC_IRQChannel = MODBUS_TIM_IRQ ;    
  27. NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;    
  28. NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3;    
  29. NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;   
  30. NVIC_Init(&NVIC_InitStructure);   
  31. //清除溢出中断标志位  
  32. TIM_ClearITPendingBit(MODBUS_TIM, TIM_IT_Update);  
  33. //定时器溢出中断关闭  
  34.   TIM_ITConfig(MODBUS_TIM,TIM_IT_Update,DISABLE);  
  35. //失能定时器  
  36. TIM_Cmd(MODBUS_TIM, DISABLE);    
  37. return TRUE;  
  38. }  
  39. /*******************************************************************/  
  40. //Modbus定时器使能函数  
  41. void vMBPortTimersEnable()  
  42. {  
  43.     /* Enable the timer with the timeout passed to xMBPortTimersInit( ) */  
  44.  TIM_ClearITPendingBit(MODBUS_TIM, TIM_IT_Update);  
  45.    TIM_ITConfig(MODBUS_TIM, TIM_IT_Update, ENABLE);  
  46.  //设置定时器的初值  
  47.  TIM_SetCounter(MODBUS_TIM,0x0000);  
  48.  TIM_Cmd(MODBUS_TIM, ENABLE);  
  49. }  
  50. /*******************************************************************/  
  51. //Modbus定时器失能函数  
  52. void vMBPortTimersDisable()  
  53. {  
  54.   /* Disable any pending timers. */  
  55. TIM_ClearITPendingBit(MODBUS_TIM, TIM_IT_Update);  
  56.   TIM_ITConfig(MODBUS_TIM, TIM_IT_Update, DISABLE);  
  57.   TIM_SetCounter(MODBUS_TIM,0x0000);   
  58.   //关闭定时器  
  59.   TIM_Cmd(MODBUS_TIM, DISABLE);  
  60. }  
  61. /*******************************************************************/  
  62. //Modbus 定时器中断函数  
  63. void MODBUS_TIM_IRQHandler( void )  
  64. {  
  65.   if(TIM_GetITStatus(MODBUS_TIM, TIM_IT_Update) == SET)   
  66. {  
  67.      TIM_ClearITPendingBit(MODBUS_TIM, TIM_IT_Update);  
  68.      prvvTIMERExpiredISR();  
  69.    }  
  70. }  

 

 

07.第三个需要修改的文件是port.h文件,这里文件我放置了一些需要定义的宏,方便后面修改,万一哪一天Modbus、串口2、定时器4打起来了,那我有的忙了,一个伟大程序员总是在考虑谁会打起来,不希望出现劝架的那一天。port.h文件中主要补充进入临界区和退出临界区的宏定义、portserial.c 文件中用到的宏、porttimer.c 文件中用到的宏。(注:是补充哦!不是全部的port.h文件内容,不要傻傻的去全部替换!)

 

 

 

view plaincopy to clipboardprint?
  1. #define ENTER_CRITICAL_SECTION( )    __set_PRIMASK(1); //关闭中断  
  2. #define EXIT_CRITICAL_SECTION( )     __set_PRIMASK(0); //开启中断  
  3. //Modbus串口   
  4. #define  MODBUS_USART            USART2  
  5. #define  MODBUS_USART_CLK        RCC_APB1Periph_USART2  
  6. #define  MODBUS_USART_GPIO_CLK   RCC_APB2Periph_GPIOA  
  7. #define  MODBUS_USART_TX_PORT    GPIOA  
  8. #define  MODBUS_USART_RX_PORT    GPIOA  
  9. #define  MODBUS_USART_TX_PIN     GPIO_Pin_2  
  10. #define  MODBUS_USART_RX_PIN     GPIO_Pin_3  
  11. #define  MODBUS_USART_IRQ           USART2_IRQn  
  12. #define  MODBUS_USART_IRQHandler    USART2_IRQHandler  
  13. //Modbus定时器  
  14. #define  MODBUS_TIM              TIM4        
  15. #define  MODBUS_TIM_CLK          RCC_APB1Periph_TIM4  
  16. #define  MODBUS_TIM_IRQ          TIM4_IRQn  
  17. #define  MODBUS_TIM_IRQHandler   TIM4_IRQHandler  

 

 

08.编译之后,果然不出所料,有错误。学会修改错误,是一个程序猿的基本修养。如果连这点基本修养都没有,那只能说,路漫漫其修远兮,还得继续上下左右之求索。在这里我们尝试着去删除porttimer.c 中定时器使能和失能函数前的inline 字样,咦,奇怪的东西消失了呢。

 

图片8.png

 

09.当然困难不止一个,就像当前的新冠病毒,一波未平一波又起。再次编译,出现关于assert的错误...........,不要挠头皮,继续想法搞定吧!于是打开搜索引擎,开始找对应的解决办法的过程。

 

图片9.png

 

10. 搜索并思考了很久,偶然在CSDN上看到一位博主是这么解决的:在主函数下面添加以下代码(代码在下方),即可解决以上问题,硬着头皮先试一试(因为暂时不相信也没有其它办法),复制过来,直接编译,居然轻松解决,至于原因嘛,就留给各位读者自己思索咯。

 

 

 

view plaincopy to clipboardprint?
  1. #ifdef  USE_FULL_ASSERT  
  2. /**  
  3.   * @brief  Reports the name of the source file and the source line number  
  4.   *         where the assert_param error has occurred.  
  5.   * @param  file: pointer to the source file name  
  6.   * @param  line: assert_param error line source number  
  7.   * @retval None  
  8.   */    
  9. void assert_failed(uint8_t* file, uint32_t line)    
  10. {    
  11.   /* User can add his own implementation to report the file name and line number,  
  12.      ex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */    
  13.      
  14.   /* Infinite loop */    
  15.   while (1)    
  16.   {    
  17.   }    
  18. }  
  19. #else  
  20. void __aeabi_assert(const char * x1, const char * x2, int x3)  
  21. {  
  22. }  
  23. #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?
  1. int main(void)  
  2. {   
  3.   delay_init();     //延时函数初始化       
  4.   //初始化 RTU模式 从机地址为1 USART2 9600 无效验  
  5.   eMBInit(MB_RTU, 0x01, 0x02, 9600, MB_PAR_NONE);  
  6.   //启动FreeModbus   
  7.   eMBEnable();  
  8.   while(1)  
  9.   {    
  10.    //FreeMODBUS不断查询  
  11.    eMBPoll();   
  12.   }  
  13. }  

 

 

 

 

01.当然,仅仅拿个main函数出来,是不能够证明FreeModbus就跑起来了,我们再来添加点测试部分代码,以观其效吧!测试代码部分,主要添加输入寄存器、保持寄存器、线圈的起始地址和定义的内容,以及输入寄存器、保持寄存器、线圈的处理函数,具体如下:

 

 

 

view plaincopy to clipboardprint?
  1. /* ----------------------- Defines ------------------------------------------*/  
  2. //添加输入寄存器、保持寄存器、线圈的起始地址和定义的内容  
  3. //保持寄存器起始地址  
  4. #define REG_HOLDING_START 0x0001  
  5. //保持寄存器数量  
  6. #define REG_HOLDING_NREGS 8  
  7. //保持寄存器内容  
  8. uint16_t usRegHoldingBuf[REG_HOLDING_NREGS] ={0x147b,0x3f8e,0x147b,0x400e,0x1eb8,0x4055,0x147b,0x408e};  
  9.    
  10. //输入寄存器起始地址  
  11. #define REG_INPUT_START 0x0001  
  12. //输入寄存器数量  
  13. #define REG_INPUT_NREGS 8  
  14. //输入寄存器内容  
  15. uint16_t usRegInputBuf[REG_INPUT_NREGS] ={0x1111,0x2222,0x3333,0x4444,0x5555,0x6666,0x7777,0x8888};  
  16.    
  17. //线圈寄存器起始地址  
  18. #define REG_COILS_START 0x0001  
  19. //线圈寄存器数量  
  20. #define REG_COILS_SIZE 16  
  21. //线圈寄存器内容  
  22. uint8_t ucRegCoilsBuf[REG_COILS_SIZE/8] = {0x00,0xFF};  
  23. /***********************************************************************/  
  24. 输入寄存器、保持寄存器、线圈的处理函数  
  25. // 保持寄存器的读写函数 支持的命令为读 0x03 和写0x06 可读可写    
  26. eMBErrorCode   
  27. eMBRegHoldingCB( UCHAR * pucRegBuffer, USHORT usAddress, USHORT usNRegs,eMBRegisterMode eMode )  
  28. {  
  29.   //错误状态  
  30.   eMBErrorCode eStatus = MB_ENOERR;  
  31.   //偏移量  
  32.   int16_t iRegIndex;  
  33.     
  34. //判断寄存器是不是在范围内  
  35.   if( ( (int16_t)usAddress >= REG_HOLDING_START )&& ( usAddress + usNRegs <= REG_HOLDING_START + REG_HOLDING_NREGS ) )  
  36.   {  
  37.     //计算偏移量  
  38.     iRegIndex = ( int16_t )( usAddress - REG_HOLDING_START);  
  39.     switch ( eMode )  
  40.    {  
  41.      //读处理函数  
  42.       case MB_REG_READ:  
  43. while( usNRegs > 0 )  
  44. {  
  45. *pucRegBuffer++ = ( uint8_t )( usRegHoldingBuf[iRegIndex] >> 8 );  
  46. *pucRegBuffer++ = ( uint8_t )( usRegHoldingBuf[iRegIndex] & 0xFF );  
  47. iRegIndex++;  
  48. usNRegs--;  
  49. }  
  50. break;  
  51.    
  52.     //写处理函数  
  53.      case MB_REG_WRITE:  
  54. while( usNRegs > 0 )  
  55. {  
  56. usRegHoldingBuf[iRegIndex] = *pucRegBuffer++ << 8;  
  57. usRegHoldingBuf[iRegIndex] |= *pucRegBuffer++;  
  58. iRegIndex++;  
  59. usNRegs--;  
  60. }  
  61.  break;  
  62.   }  
  63. }  
  64. else  
  65. {  
  66. //返回错误状态  
  67. eStatus = MB_ENOREG;  
  68. }  
  69.    
  70. return eStatus;  
  71. }  
  72.    
  73. //读输入寄存器函数 支持的命令为读 0x04   
  74. eMBErrorCode  
  75. eMBRegInputCB( UCHAR * pucRegBuffer, USHORT usAddress, USHORT usNRegs )  
  76. {  
  77.     eMBErrorCode    eStatus = MB_ENOERR;  
  78.     int             iRegIndex;  
  79.    
  80.     if( ( usAddress >= REG_INPUT_START )  
  81.         && ( usAddress + usNRegs <= REG_INPUT_START + REG_INPUT_NREGS ) )  
  82.     {  
  83.         iRegIndex = ( int )( usAddress - REG_INPUT_START );  
  84.         while( usNRegs > 0 )  
  85.         {  
  86.             *pucRegBuffer++ =  
  87.                 ( unsigned char )( usRegInputBuf[iRegIndex] >> 8 );  
  88.             *pucRegBuffer++ =  
  89.                 ( unsigned char )( usRegInputBuf[iRegIndex] & 0xFF );  
  90.             iRegIndex++;  
  91.             usNRegs--;  
  92.         }  
  93.     }  
  94.     else  
  95.     {  
  96.         eStatus = MB_ENOREG;  
  97.     }  
  98.    
  99.     return eStatus;  
  100. }  
  101.    
  102. //线圈处理函数 可读可写 支持的命令为读 0x01 0x05  
  103. eMBErrorCode  
  104. eMBRegCoilsCB( UCHAR * pucRegBuffer, USHORT usAddress, USHORT usNCoils,  
  105.                eMBRegisterMode eMode )  
  106. {  
  107.     //错误状态  
  108.     eMBErrorCode eStatus = MB_ENOERR;  
  109.     //寄存器个数  
  110.     int16_t iNCoils = ( int16_t )usNCoils;  
  111.     //寄存器偏移量  
  112.     int16_t usBitOffset;  
  113.    
  114.    //判断寄存器是不是在范围内  
  115.    if( ( (int16_t)usAddress >= REG_COILS_START ) &&( usAddress + usNCoils <= REG_COILS_START + REG_COILS_SIZE ) )  
  116.    {  
  117.      //计算寄存器偏移量  
  118.      usBitOffset = ( int16_t )( usAddress - REG_COILS_START );  
  119.      switch ( eMode )  
  120.      {  
  121. //读操作  
  122. case MB_REG_READ:  
  123. while( iNCoils > 0 )  
  124. {  
  125. *pucRegBuffer++ = xMBUtilGetBits( ucRegCoilsBuf, usBitOffset,  
  126. ( uint8_t )( iNCoils > 8 ? 8 : iNCoils ) );  
  127. iNCoils -= 8;  
  128. usBitOffset += 8;  
  129. }  
  130.   break;  
  131.    
  132. //写操作  
  133. case MB_REG_WRITE:  
  134. while( iNCoils > 0 )  
  135. {  
  136. xMBUtilSetBits( ucRegCoilsBuf, usBitOffset,  
  137. ( uint8_t )( iNCoils > 8 ? 8 : iNCoils ),  
  138. *pucRegBuffer++ );  
  139. iNCoils -= 8;  
  140. }  
  141. break;  
  142.     }  
  143.   }  
  144. else  
  145. {  
  146.    eStatus = MB_ENOREG;  
  147. }  
  148. return eStatus;   
  149.  return MB_ENOREG;  
  150. }  

 

 

 

 

02.好了,测试代码准备就绪,下面就是见证奇迹的时刻?我们使用ModbusPoll软件测试版来完成对移植FreeModbus rtu的测试,如下图,该代码能够成功的读出输入寄存器、保持寄存器、线圈的数据。

 

图片10.png

 

03. 通信正常,则可以看到USB转串口工具的TX和RX指示灯不停闪烁着。想象一下突然有一天,人类的眼睛可以分辨出100MHz以上的闪烁频率时,我们的世界将会如何?emmmm....这个脑洞可能开的有点大,留给大家去发挥想象力吧。如果我们能够分辨更高的频率,那么我们的电脑手机显示器的144Hz的刷新率,就再也不香了吧,哈哈~!

 

图片11.png

 

 

 

综上可以看出,本次对FreeModbus的移植工作已经完成了,该平台虽然是基于STM32F1xx,但是使用其他STM32的芯片也非常的方便,过程大致相同,简单改一下就好了。从通过ModbusPoll工具进行测试的结果看来,整个工程基本能够达到预期效果,当然本人技术有限,还请各位大佬们不吝赐教。最后贴上main测试完整代码供大家参考。

 

 

 

view plaincopy to clipboardprint?
  1. #include "sys.h"  
  2. #include "delay.h"  
  3. #include "usart.h"  
  4. #include "led.h"  
  5.    
  6. #include "mb.h"  
  7. #include "mbport.h"  
  8.    
  9. /* ----------------------- Defines ------------------------------------------*/  
  10. //保持寄存器起始地址  
  11. #define REG_HOLDING_START 0x0001  
  12. //保持寄存器数量  
  13. #define REG_HOLDING_NREGS 8  
  14. //保持寄存器内容  
  15. uint16_t usRegHoldingBuf[REG_HOLDING_NREGS] ={0x147b,0x3f8e,0x147b,0x400e,0x1eb8,0x4055,0x147b,0x408e};  
  16.    
  17. //输入寄存器起始地址  
  18. #define REG_INPUT_START 0x0001  
  19. //输入寄存器数量  
  20. #define REG_INPUT_NREGS 8  
  21. //输入寄存器内容  
  22. uint16_t usRegInputBuf[REG_INPUT_NREGS] ={0x1111,0x2222,0x3333,0x4444,0x5555,0x6666,0x7777,0x8888};  
  23.    
  24. //线圈寄存器起始地址  
  25. #define REG_COILS_START 0x0001  
  26. //线圈寄存器数量  
  27. #define REG_COILS_SIZE 16  
  28. //线圈寄存器内容  
  29. uint8_t ucRegCoilsBuf[REG_COILS_SIZE/8] = {0x00,0xFF};  
  30.    
  31.  int main(void)  
  32.  {   
  33. delay_init();     //延时函数初始化      
  34. //初始化 RTU模式 从机地址为1 USART2 9600 无效验  
  35. eMBInit(MB_RTU, 0x01, 0x02, 9600, MB_PAR_NONE);  
  36. //启动FreeModbus   
  37. eMBEnable();  
  38. while(1)  
  39. {    
  40.  //FreeMODBUS不断查询  
  41.     eMBPoll();   
  42. }  
  43. }  
  44.    
  45. // 保持寄存器的读写函数 支持的命令为读 0x03 和写0x06 可读可写    
  46. eMBErrorCode   
  47. eMBRegHoldingCB( UCHAR * pucRegBuffer, USHORT usAddress, USHORT usNRegs,eMBRegisterMode eMode )  
  48. {  
  49.   //错误状态  
  50.   eMBErrorCode eStatus = MB_ENOERR;  
  51.   //偏移量  
  52.   int16_t iRegIndex;  
  53.     
  54. //判断寄存器是不是在范围内  
  55.   if( ( (int16_t)usAddress >= REG_HOLDING_START )&& ( usAddress + usNRegs <= REG_HOLDING_START + REG_HOLDING_NREGS ) )  
  56.   {  
  57.     //计算偏移量  
  58.     iRegIndex = ( int16_t )( usAddress - REG_HOLDING_START);  
  59.     switch ( eMode )  
  60.    {  
  61.      //读处理函数  
  62.       case MB_REG_READ:  
  63. while( usNRegs > 0 )  
  64. {  
  65. *pucRegBuffer++ = ( uint8_t )( usRegHoldingBuf[iRegIndex] >> 8 );  
  66. *pucRegBuffer++ = ( uint8_t )( usRegHoldingBuf[iRegIndex] & 0xFF );  
  67. iRegIndex++;  
  68. usNRegs--;  
  69. }  
  70. break;  
  71.    
  72.     //写处理函数  
  73.      case MB_REG_WRITE:  
  74. while( usNRegs > 0 )  
  75. {  
  76. usRegHoldingBuf[iRegIndex] = *pucRegBuffer++ << 8;  
  77. usRegHoldingBuf[iRegIndex] |= *pucRegBuffer++;  
  78. iRegIndex++;  
  79. usNRegs--;  
  80. }  
  81.  break;  
  82.   }  
  83. }  
  84. else  
  85. {  
  86. //返回错误状态  
  87. eStatus = MB_ENOREG;  
  88. }  
  89.    
  90. return eStatus;  
  91. }  
  92.    
  93. //读输入寄存器函数 支持的命令为读 0x04   
  94. eMBErrorCode  
  95. eMBRegInputCB( UCHAR * pucRegBuffer, USHORT usAddress, USHORT usNRegs )  
  96. {  
  97.     eMBErrorCode    eStatus = MB_ENOERR;  
  98.     int             iRegIndex;  
  99.    
  100.     if( ( usAddress >= REG_INPUT_START )  
  101.         && ( usAddress + usNRegs <= REG_INPUT_START + REG_INPUT_NREGS ) )  
  102.     {  
  103.         iRegIndex = ( int )( usAddress - REG_INPUT_START );  
  104.         while( usNRegs > 0 )  
  105.         {  
  106.             *pucRegBuffer++ =  
  107.                 ( unsigned char )( usRegInputBuf[iRegIndex] >> 8 );  
  108.             *pucRegBuffer++ =  
  109.                 ( unsigned char )( usRegInputBuf[iRegIndex] & 0xFF );  
  110.             iRegIndex++;  
  111.             usNRegs--;  
  112.         }  
  113.     }  
  114.     else  
  115.     {  
  116.         eStatus = MB_ENOREG;  
  117.     }  
  118.    
  119.     return eStatus;  
  120. }  
  121.    
  122. //线圈处理函数 可读可写 支持的命令为读 0x01 0x05  
  123. eMBErrorCode  
  124. eMBRegCoilsCB( UCHAR * pucRegBuffer, USHORT usAddress, USHORT usNCoils,  
  125.                eMBRegisterMode eMode )  
  126. {  
  127.     //错误状态  
  128.     eMBErrorCode eStatus = MB_ENOERR;  
  129.     //寄存器个数  
  130.     int16_t iNCoils = ( int16_t )usNCoils;  
  131.     //寄存器偏移量  
  132.     int16_t usBitOffset;  
  133.    
  134.    //判断寄存器是不是在范围内  
  135.    if( ( (int16_t)usAddress >= REG_COILS_START ) &&( usAddress + usNCoils <= REG_COILS_START + REG_COILS_SIZE ) )  
  136.    {  
  137.      //计算寄存器偏移量  
  138.      usBitOffset = ( int16_t )( usAddress - REG_COILS_START );  
  139.      switch ( eMode )  
  140.      {  
  141. //读操作  
  142. case MB_REG_READ:  
  143. while( iNCoils > 0 )  
  144. {  
  145. *pucRegBuffer++ = xMBUtilGetBits( ucRegCoilsBuf, usBitOffset,  
  146. ( uint8_t )( iNCoils > 8 ? 8 : iNCoils ) );  
  147. iNCoils -= 8;  
  148. usBitOffset += 8;  
  149. }  
  150.   break;  
  151.    
  152. //写操作  
  153. case MB_REG_WRITE:  
  154. while( iNCoils > 0 )  
  155. {  
  156. xMBUtilSetBits( ucRegCoilsBuf, usBitOffset,  
  157. ( uint8_t )( iNCoils > 8 ? 8 : iNCoils ),  
  158. *pucRegBuffer++ );  
  159. iNCoils -= 8;  
  160. }  
  161. break;  
  162.     }  
  163.   }  
  164. else  
  165. {  
  166.    eStatus = MB_ENOREG;  
  167. }  
  168. return eStatus;   
  169.  return MB_ENOREG;  
  170. }  
  171.    
  172.    
  173. //开关输入寄存器处理函数 可读 支持的命令为读 0x02  
  174. eMBErrorCode  
  175. eMBRegDiscreteCB( UCHAR * pucRegBuffer, USHORT usAddress, USHORT usNDiscrete )  
  176. {  
  177.     return MB_ENOREG;  
  178. }  
  179.    
  180. //针对 assert错误添加的函数  
  181. #ifdef  USE_FULL_ASSERT  
  182. /**  
  183.   * @brief  Reports the name of the source file and the source line number  
  184.   *         where the assert_param error has occurred.  
  185.   * @param  file: pointer to the source file name  
  186.   * @param  line: assert_param error line source number  
  187.   * @retval None  
  188.   */    
  189. void assert_failed(uint8_t* file, uint32_t line)    
  190. {    
  191.   /* User can add his own implementation to report the file name and line number,  
  192.      ex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */    
  193.      
  194.   /* Infinite loop */    
  195.   while (1)    
  196.   {    
  197.   }    
  198. }  
  199. #else  
  200. void __aeabi_assert(const char * x1, const char * x2, int x3)  
  201. {  
  202. }  
  203. #endif  

 

 

 

本系列到此就全部结束了,大家好,我是大Z,感谢大家支持~

 

标签:MODBUS,USART,Modbus,手把手,STM32,TIM,FreeModbus,寄存器,REG
来源: https://www.cnblogs.com/mangorange/p/14228119.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有