freemodbus 在STM32上的移植

[复制链接]
15777|31
 楼主| 固桐 发表于 2013-5-27 20:59 | 显示全部楼层 |阅读模式
本帖最后由 固桐 于 2013-5-27 22:27 编辑

这几天,研究了freemodbus通信协议,然后在stm32上移植。关于freemodbus通信原理,以及通信过程(状态机转换),这里就不说了。       freemodbus使用的库是最新版本freemodbus-v1.5.0,把相应的库文件加载在STM32工程中。移植时,需要用户改写的文件有两个,一个是porttimer.c文件,一个是portserial.c文件。porttimer.c文件主要用于超时判断,本例使用的是定时器2,portserial.c主要使用串口通信的实现,本例使用的是RS485.porttimer.c修改如下
  1. /* ----------------------- Platform includes --------------------------------*/
  2. #include "port.h"

  3. /* ----------------------- Modbus includes ----------------------------------*/
  4. #include "mb.h"
  5. #include "mbport.h"
  6. #include "..\..\include\nvic.h"
  7. #include "..\..\include\stm32f10x_reg.h"
  8. /* ----------------------- Start implementation -----------------------------*/

  9. BOOL
  10. xMBPortTimersInit( USHORT usTim1Timerout50us ) //配置50us时钟
  11. {

  12.         uint16_t PrescalerValue = 0;
  13.         
  14.         /* TIM2 clock enable */

  15.         
  16.         RCC->CFGR.B.PPRE1 = 4;  //APB1预分频系数
  17.                           //100:HCLK 2分频
  18.                                 //APB1 不超过36MHz

  19.         RCC->APB1ENR.B.TIM2EN = 1;

  20.         /* Compute the prescaler value */
  21.         PrescalerValue = (uint16_t) (36000000 / 20000) - 1; // 1/20000=50us
  22.         /* Time base configuration */
  23.         TIM2->ARR=(uint16_t) usTim1Timerout50us;
  24.         TIM2->PSC= PrescalerValue;
  25.         TIM2->CR1.B.URS = 1;

  26.    MY_NVIC_Init(3,3,28,2);         
  27.                
  28.         /* TIM IT DISABLE */
  29.         TIM2->SR.B.UIF = 0;
  30.         TIM2->DIER.B.UIE = 0;
  31.         TIM2->CR1.B.CEN = 0;
  32.         /* TIM2 DISABLE counter */

  33.         return TRUE;;
  34. }


  35. void vMBPortTimersEnable(  ) //打开时钟
  36. {
  37.          TIM2->SR.B.UIF = 0;
  38.         TIM2->DIER.B.UIE = 1;
  39.         TIM2->CNT= 0;
  40.         TIM2->CR1.B.CEN = 1;
  41.         
  42. }

  43. void vMBPortTimersDisable(  ) //关闭时钟
  44. {
  45.         TIM2->CR1.B.CEN = 0;
  46.         TIM2->CNT= 0;
  47.         TIM2->DIER.B.TIE = 0;
  48.         TIM2->DIER.B.UIE = 0;
  49.         }

  50. /* Create an ISR which is called whenever the timer has expired. This function
  51. * must then call pxMBPortCBTimerExpired( ) to notify the protocol stack that
  52. * the timer has expired.
  53. */
  54. void prvvTIMERExpiredISR( void ) //在时钟中断内调用
  55. {
  56.     ( void )pxMBPortCBTimerExpired(  );
  57. }

  58. void TIM2_IRQHandler(void)
  59. {

  60.     /* Clear TIM4 Capture Compare1 interrupt pending bit*/
  61.    
  62.         TIM2->SR.B.UIF = 0;
  63.         TIM2->SR.B.TIF = 0;
  64.         prvvTIMERExpiredISR( );

  65. }
portserial.c文件修改如下

  1. #include "port.h"

  2. /* ----------------------- Modbus includes ----------------------------------*/
  3. #include "mb.h"
  4. #include "mbport.h"
  5. #include "..\..\include\nvic.h"
  6. #include "..\..\include\stm32f10x_reg.h"
  7. #include "mb.h"
  8. #include "mbport.h"
  9. #include <stdio.h>
  10. #include <stdlib.h>
  11. #include <string.h>
  12. #include <ctype.h>
  13. #include <math.h>
  14. #include <stdarg.h>



  15. #define        RS485_T    GPIOA->BSRR.B.SETIO4=1
  16. #define RS485_R   GPIOA->BSRR.B.CLRIO4=1


  17. /* ----------------------- Start implementation -----------------------------*/
  18. void
  19. vMBPortSerialEnable( BOOL xRxEnable, BOOL xTxEnable ) //控制串口的收发中断
  20. {
  21.         if(TRUE==xRxEnable)
  22.         {
  23.                 USART2->CR1.B.RXNEIE = 1;
  24.                                        
  25.         }
  26.         else
  27.         {
  28.                 USART2->CR1.B.RXNEIE = 0;
  29.                
  30.         }

  31.         if(TRUE==xTxEnable)
  32.         {
  33.                
  34.                 USART2->CR1.B.TXEIE = 1;
  35.                
  36.         }
  37.         else
  38.         {
  39.            
  40.                 USART2->CR1.B.TXEIE = 0;
  41.                   
  42.         }
  43. }

  44. * 配置串口 目前除了波特率其他参数无效



  45. BOOL
  46. xMBPortSerialInit( UCHAR ucPORT, ULONG ulBaudRate, UCHAR ucDataBits, eMBParity eParity )
  47. {
  48.         float temp;
  49.   float temp2;        

  50.   AFIO->MAPR.B.USART2_REMAP=0; //无重映像

  51.   RCC->APB1ENR.B.USART2EN = 1; //USART2时钟开启

  52.         GPIOA->CRL.B.MODE2 = 3;        // Configure USART2 Tx (PA.2) as alternate function push-pull
  53.         GPIOA->CRL.B.CNF2 = 2;

  54.         GPIOA->CRL.B.MODE3 = 0;        // Configure USART2 Rx (PA.3) as input floating
  55.         GPIOA->CRL.B.CNF3 = 1;
  56.                            
  57.         
  58.         temp= (72000000.0/2)/(16*ulBaudRate);
  59.         temp2=floor(temp);
  60.         temp=temp-temp2;
  61.         temp*=16;
  62.                                                                
  63.         USART2->BRR.B.DIV_Fraction=temp;//DIV_Fraction[3:0]:USARTDIV的小数部分
  64.                                                                         //这4位定义了USART分频器除法因子(USARTDIV)的小数部分。
  65.                                                                         // 6 9600
  66.         USART2->BRR.B.DIV_Mantissa=temp2;
  67.         //DIV_Mantissa[11:0]:USARTDIV的整数部分
  68.         //这12位定义了USART分频器除法因子(USARTDIV)的整数部分。
  69.         //234 9600
  70.         
  71.         
  72.         USART2->CR1.B.PCE = 0;    //0:校验控制被禁止;
  73.         USART2->CR1.B.M = 0;      //0:一个起始位,8个数据位,n个停止位;
  74.         USART2->CR1.B.RE = 1;     //1:接受被使能。
  75.         USART2->CR1.B.TE = 1;     //1:发送被使能。
  76.         USART2->CR1.B.RXNEIE = 1; //1:当USART_SR中的ORE或者RXNE为1时,产生USART中断。
  77.         USART2->CR1.B.PEIE = 1;   //1:当USART_SR中的PE为1时,产生USART中断。
  78.         USART2->CR1.B.UE = 1;     //1:USART模块使能。
  79.         
  80.         USART2->CR2.B.LBCL = 0;   //0:最后一位数据的时钟脉冲不从SCLK输出;
  81.         USART2->CR2.B.CPOL = 0;   //0:总线空闲时SCLK引脚上保持低电平;
  82.         USART2->CR2.B.CLKEN = 0;  //0:SCLK引脚被禁止。
  83.         USART2->CR2.B.STOP = 0;   //00:1个停止位;
  84.         USART2->CR2.B.CPHA = 1;   //1:时钟二个第边沿进行数据捕获。
  85.         USART2->CR3.B.RTSE = 0;   //0:RTS硬件流控制被禁止;
  86.         USART2->CR3.B.CTSE = 0;   //0:CTS硬件流控制被禁止;

  87.         
  88.    MY_NVIC_Init(3,3,38,2);//组2,最低优先级
  89.          GPIOA->CRL.B.MODE4 = 3;
  90.         GPIOA->CRL.B.CNF4 = 0;
  91.    
  92.         RS485_R;
  93.    
  94.         return TRUE;
  95. }

  96. // 串口发
  97. BOOL
  98. xMBPortSerialPutByte( CHAR ucByte )
  99. {
  100.         int i;

  101.         RS485_T;
  102.         
  103.     for(i=0;i<1000;i++);
  104.           USART2->DR.W= ucByte;
  105.           while (!(USART2->SR.B.TC == 1)) ;
  106.         
  107.           RS485_R;
  108.     return TRUE;
  109. }

  110. // 串口收
  111. BOOL
  112. xMBPortSerialGetByte( CHAR * pucByte )
  113. {

  114.     *pucByte = USART2->DR.W;

  115.     return TRUE;
  116. }

  117. /* Create an interrupt handler for the transmit buffer empty interrupt
  118. * (or an equivalent) for your target processor. This function should then
  119. * call pxMBFrameCBTransmitterEmpty( ) which tells the protocol stack that
  120. * a new character can be sent. The protocol stack will then call
  121. * xMBPortSerialPutByte( ) to send the character.
  122. */
  123. void prvvUARTTxReadyISR( void )
  124. {
  125.     pxMBFrameCBTransmitterEmpty(  );
  126. }

  127. /* Create an interrupt handler for the receive interrupt for your target
  128. * processor. This function should then call pxMBFrameCBByteReceived( ). The
  129. * protocol stack will then call xMBPortSerialGetByte( ) to retrieve the
  130. * character.
  131. */
  132. void prvvUARTRxISR( void )
  133. {
  134.      pxMBFrameCBByteReceived(  );
  135. }
  136. void USART2_IRQHandler(void)
  137. {
  138.         if(USART2->CR1.B.RXNEIE==1)
  139.         {
  140.                 prvvUARTRxISR();//接收完成中断
  141.                 USART2->SR.B.RXNE=0;
  142.      
  143.         }

  144.         if(USART2->CR1.B.TXEIE==1)
  145.         {
  146.                 prvvUARTTxReadyISR();//发送完成中断
  147.                 USART2->SR.B.TC=0;
  148.                
  149.         }

  150.         
  151.         
  152. }
另外在主函数中,加入测试函数如下

  1. #include "mb.h"
  2. #include "..\..\include\adc.h"
  3. #include "..\..\include\lm75.h"
  4. #include "..\..\include\timer.h"
  5. #include "..\..\include\simulate.h"
  6. #include "..\..\include\encode.h"
  7. #include "..\..\include\hardware.h"
  8. #include "..\..\include\systick.h"

  9. unsigned short int  usRegInputBuf[10]={0x0000,0xfe02,0x1203,0x1304,0x1405,0x1506,0x1607,0x1708,0x1809};
  10. unsigned short int *usRegHoldingBuf=usRegInputBuf;        //一个测试用的 寄存器数组 地址0-7

  11. unsigned char REG_INPUT_START=0,REG_HOLDING_START=0;
  12. unsigned char REG_INPUT_NREGS=8,REG_HOLDING_NREGS=8;
  13. unsigned char usRegInputStart=0,usRegHoldingStart=0;

  14. //读数字寄存器 功能码0x04

  15. eMBErrorCode
  16. eMBRegInputCB( UCHAR * pucRegBuffer, USHORT usAddress, USHORT usNRegs )
  17. {
  18.         eMBErrorCode    eStatus = MB_ENOERR;
  19.         int             iRegIndex;

  20.     if( ( usAddress >= REG_INPUT_START )&& ( usAddress + usNRegs <= REG_INPUT_START + REG_INPUT_NREGS ) )
  21.     {
  22.         iRegIndex = ( int )( usAddress - usRegInputStart );
  23.         while( usNRegs > 0 )
  24.         {
  25.             *pucRegBuffer++ =
  26.                 ( unsigned char )( usRegInputBuf[iRegIndex] >> 8 );
  27.             *pucRegBuffer++ =
  28.                 ( unsigned char )( usRegInputBuf[iRegIndex] & 0xFF );
  29.             iRegIndex++;
  30.             usNRegs--;
  31.         }
  32.     }
  33.     else
  34.     {
  35.         eStatus = MB_ENOREG;
  36.     }

  37.     return eStatus;

  38. }
  39. // 寄存器的读写函数 支持的命令为读 0x03 和写0x06

  40. eMBErrorCode
  41. eMBRegHoldingCB( UCHAR * pucRegBuffer, USHORT usAddress, USHORT usNRegs, eMBRegisterMode eMode )
  42. {
  43.     eMBErrorCode    eStatus = MB_ENOERR;
  44.     int             iRegIndex;


  45.     if( ( usAddress >= REG_HOLDING_START ) && ( usAddress + usNRegs <= REG_HOLDING_START + REG_HOLDING_NREGS ) )
  46.     {
  47.         iRegIndex = ( int )( usAddress - usRegHoldingStart );
  48.         switch ( eMode )
  49.         {
  50.                                        
  51.         case MB_REG_READ:
  52.         if(usAddress==1)
  53.                             {
  54.                                     *pucRegBuffer++ = ( unsigned char )( adc.read(1) >> 8 );
  55.             
  56.                                     *pucRegBuffer++ = ( unsigned char )( adc.read(1) & 0xFF );
  57.                                   }
  58.                                 if(usAddress==2)
  59.                                   {
  60.                                     *pucRegBuffer++ = ( unsigned char )( adc.read(0) >> 8 );
  61.             
  62.                                     *pucRegBuffer++ = ( unsigned char )( adc.read(0) & 0xFF );
  63.                                   }
  64.                                         if(usAddress==3)
  65.                                   {
  66.                                     *pucRegBuffer++ = ( unsigned char )( adc.read(7) >> 8 );
  67.             
  68.                                     *pucRegBuffer++ = ( unsigned char )( adc.read(7) & 0xFF );
  69.                                   }
  70.                                         if(usAddress==4)
  71.                                   {
  72.                                     *pucRegBuffer++ = ( unsigned char )( lm75.read() >> 8 );
  73.             
  74.                                     *pucRegBuffer++ = ( unsigned char )( lm75.read() & 0xFF );
  75.                                   }
  76.         
  77.          if(usAddress==5)
  78.                                   {
  79.                                                                
  80.                                 timer.refresh();
  81.                                 *pucRegBuffer++ = ( unsigned char )( tim_parameter.Position_relative >> 8 );
  82.                 *pucRegBuffer++ = ( unsigned char )( tim_parameter.Position_relative &0xFF );
  83.                                                 
  84.                
  85.         }
  86.                 if(usAddress==6)
  87.                                   {
  88.                                                                
  89.                                 timer.refresh();
  90.                                 *pucRegBuffer++ = ( unsigned char )(  TIM1->CCR2>> 8 );
  91.                 *pucRegBuffer++ = ( unsigned char )(  TIM1->CCR2&0xFF );
  92.                                                 
  93.                
  94.         }
  95.                                  
  96.             break;
  97.         case MB_REG_WRITE:
  98.             while( usNRegs > 0 )
  99.             {
  100.          

  101.                                 usRegHoldingBuf[iRegIndex] = *pucRegBuffer++ << 8;
  102.              usRegHoldingBuf[iRegIndex] |= *pucRegBuffer++;
  103.              iRegIndex++;
  104.                 usNRegs--;
  105.             }
  106.         }
  107.     }
  108.     else
  109.     {
  110.         eStatus = MB_ENOREG;
  111.     }
  112.     return eStatus;
  113. }

  114. //读/写开关寄存器  0x01  x05

  115. eMBErrorCode
  116. eMBRegCoilsCB( UCHAR * pucRegBuffer, USHORT usAddress, USHORT usNCoils, eMBRegisterMode eMode )
  117. {
  118.     ( void )pucRegBuffer;
  119.     ( void )usAddress;
  120.     ( void )usNCoils;
  121.     ( void )eMode;
  122.     return MB_ENOREG;
  123. }

  124. //读开关寄存器 0x02
  125. eMBErrorCode
  126. eMBRegDiscreteCB( UCHAR * pucRegBuffer, USHORT usAddress, USHORT usNDiscrete )
  127. {
  128.     ( void )pucRegBuffer;
  129.     ( void )usAddress;
  130.     ( void )usNDiscrete;
  131.     return MB_ENOREG;
  132. }

经过,测试,本次移植可以读取节点的温度,AD转换值,等参数,实现了预期的效果。


dong_abc 发表于 2013-5-27 21:03 | 显示全部楼层
我记得freemodbus包里自带了stm32 demo吧。
 楼主| 固桐 发表于 2013-5-27 22:25 | 显示全部楼层
dong_abc 发表于 2013-5-27 21:03
我记得freemodbus包里自带了stm32 demo吧。

在demo目录下  bare文件中,修改 porttimer.c和portserial.c即可
yqzhou1980 发表于 2013-11-12 11:06 | 显示全部楼层
好东西,写成文件,就更好了。
qq20707 发表于 2013-12-23 15:47 | 显示全部楼层
看看 怎么样  学习
tianli1980 发表于 2013-12-23 15:56 | 显示全部楼层
楼主介绍的步骤很详细,借鉴一下楼主的经验,项。
406978301 发表于 2013-12-23 19:09 | 显示全部楼层
不错。。。
Mrzhen009 发表于 2013-12-27 15:39 | 显示全部楼层
LZ您好!最近在做STM32的项目,也是刚接触不久,一直使用寄存器版本编写代码,没有使用STM32库函数。
现在由于也要用到485与上位机通讯,希望移植freemodbus,不知道这个协议可不可以用在寄存器开发版本的STM32工程里?谢谢!
zclmn 发表于 2013-12-27 23:43 | 显示全部楼层
使用过ModBus,简易版的,自己写的...
zjxcml 发表于 2013-12-28 10:37 | 显示全部楼层
先MARK 以后学时看 顶下
dbayj 发表于 2013-12-28 12:55 | 显示全部楼层
Mrzhen009 发表于 2013-12-27 15:39
LZ您好!最近在做STM32的项目,也是刚接触不久,一直使用寄存器版本编写代码,没有使用STM32库函数。
现在 ...

随便你怎么用都行。
Mrzhen009 发表于 2013-12-28 22:19 | 显示全部楼层
dbayj 发表于 2013-12-28 12:55
随便你怎么用都行。

谢谢!也就是说,这个freemodbus只是提供一些API,只要把源码放入工程,直接可以用它提供的函数接口实现modbus协议?这样理解对吗?
dbayj 发表于 2013-12-29 11:25 | 显示全部楼层
首先,你得把它要的驱动接口做好,然后你才可以用它提供给你的接口。至于你是用库的方式写驱动,还是用寄存器方式写驱动,都是一样的。
it_yrj 发表于 2014-1-7 13:16 | 显示全部楼层
楼主好,请教一个问题,上位机在读输入寄存器(读数字寄存器 功能码0x04)的时候是怎么跳转到回调函数
eMBErrorCode
eMBRegInputCB( UCHAR * pucRegBuffer, USHORT usAddress, USHORT usNRegs )
的,多谢了!
it_yrj 发表于 2014-1-7 13:17 | 显示全部楼层
这种数组寄存器处理方法和从Flash地址取值有何优点?
若非上述操作,如何实现取从机地址的0x4000的数据呢?
dz0658 发表于 2014-2-26 08:38 | 显示全部楼层
outstanding 发表于 2014-2-26 09:48 | 显示全部楼层
wode112233 发表于 2014-5-19 11:40 | 显示全部楼层
hsh 发表于 2014-5-19 16:47 | 显示全部楼层
谢谢!学习了。
我心永恒12 发表于 2014-8-30 09:23 | 显示全部楼层
亲,这是主机还是从机程序
您需要登录后才可以回帖 登录 | 注册

本版积分规则

7

主题

15

帖子

1

粉丝
快速回复 在线客服 返回列表 返回顶部