打印

freemodbus 在STM32上的移植

[复制链接]
14715|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修改如下
/* ----------------------- Platform includes --------------------------------*/
#include "port.h"

/* ----------------------- Modbus includes ----------------------------------*/
#include "mb.h"
#include "mbport.h"
#include "..\..\include\nvic.h"
#include "..\..\include\stm32f10x_reg.h"
/* ----------------------- Start implementation -----------------------------*/

BOOL
xMBPortTimersInit( USHORT usTim1Timerout50us ) //配置50us时钟
{

        uint16_t PrescalerValue = 0;
        
        /* TIM2 clock enable */

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

        RCC->APB1ENR.B.TIM2EN = 1;

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

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

        return TRUE;;
}


void vMBPortTimersEnable(  ) //打开时钟
{
         TIM2->SR.B.UIF = 0;
        TIM2->DIER.B.UIE = 1;
        TIM2->CNT= 0;
        TIM2->CR1.B.CEN = 1;
        
}

void vMBPortTimersDisable(  ) //关闭时钟
{
        TIM2->CR1.B.CEN = 0;
        TIM2->CNT= 0;
        TIM2->DIER.B.TIE = 0;
        TIM2->DIER.B.UIE = 0;
        }

/* 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(  );
}

void TIM2_IRQHandler(void)
{

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

}
portserial.c文件修改如下

#include "port.h"

/* ----------------------- Modbus includes ----------------------------------*/
#include "mb.h"
#include "mbport.h"
#include "..\..\include\nvic.h"
#include "..\..\include\stm32f10x_reg.h"
#include "mb.h"
#include "mbport.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <math.h>
#include <stdarg.h>



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


/* ----------------------- Start implementation -----------------------------*/
void
vMBPortSerialEnable( BOOL xRxEnable, BOOL xTxEnable ) //控制串口的收发中断
{
        if(TRUE==xRxEnable)
        {
                USART2->CR1.B.RXNEIE = 1;
                                       
        }
        else
        {
                USART2->CR1.B.RXNEIE = 0;
               
        }

        if(TRUE==xTxEnable)
        {
               
                USART2->CR1.B.TXEIE = 1;
               
        }
        else
        {
           
                USART2->CR1.B.TXEIE = 0;
                  
        }
}

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



BOOL
xMBPortSerialInit( UCHAR ucPORT, ULONG ulBaudRate, UCHAR ucDataBits, eMBParity eParity )
{
        float temp;
  float temp2;        

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

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

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

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

        
   MY_NVIC_Init(3,3,38,2);//组2,最低优先级
         GPIOA->CRL.B.MODE4 = 3;
        GPIOA->CRL.B.CNF4 = 0;
   
        RS485_R;
   
        return TRUE;
}

// 串口发
BOOL
xMBPortSerialPutByte( CHAR ucByte )
{
        int i;

        RS485_T;
        
    for(i=0;i<1000;i++);
          USART2->DR.W= ucByte;
          while (!(USART2->SR.B.TC == 1)) ;
        
          RS485_R;
    return TRUE;
}

// 串口收
BOOL
xMBPortSerialGetByte( CHAR * pucByte )
{

    *pucByte = USART2->DR.W;

    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.
*/
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(  );
}
void USART2_IRQHandler(void)
{
        if(USART2->CR1.B.RXNEIE==1)
        {
                prvvUARTRxISR();//接收完成中断
                USART2->SR.B.RXNE=0;
     
        }

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

        
        
}
另外在主函数中,加入测试函数如下

#include "mb.h" 
#include "..\..\include\adc.h"
#include "..\..\include\lm75.h"
#include "..\..\include\timer.h"
#include "..\..\include\simulate.h"
#include "..\..\include\encode.h"
#include "..\..\include\hardware.h"
#include "..\..\include\systick.h"

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

unsigned char REG_INPUT_START=0,REG_HOLDING_START=0;
unsigned char REG_INPUT_NREGS=8,REG_HOLDING_NREGS=8;
unsigned char usRegInputStart=0,usRegHoldingStart=0;

//读数字寄存器 功能码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 - usRegInputStart );
        while( usNRegs > 0 )
        {
            *pucRegBuffer++ =
                ( unsigned char )( usRegInputBuf[iRegIndex] >> 8 );
            *pucRegBuffer++ =
                ( unsigned char )( usRegInputBuf[iRegIndex] & 0xFF );
            iRegIndex++;
            usNRegs--;
        }
    }
    else
    {
        eStatus = MB_ENOREG;
    }

    return eStatus;

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

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:
        if(usAddress==1)
                            {
                                    *pucRegBuffer++ = ( unsigned char )( adc.read(1) >> 8 );
            
                                    *pucRegBuffer++ = ( unsigned char )( adc.read(1) & 0xFF );
                                  }
                                if(usAddress==2)
                                  {
                                    *pucRegBuffer++ = ( unsigned char )( adc.read(0) >> 8 );
            
                                    *pucRegBuffer++ = ( unsigned char )( adc.read(0) & 0xFF );
                                  }
                                        if(usAddress==3)
                                  {
                                    *pucRegBuffer++ = ( unsigned char )( adc.read(7) >> 8 );
            
                                    *pucRegBuffer++ = ( unsigned char )( adc.read(7) & 0xFF );
                                  }
                                        if(usAddress==4)
                                  {
                                    *pucRegBuffer++ = ( unsigned char )( lm75.read() >> 8 );
            
                                    *pucRegBuffer++ = ( unsigned char )( lm75.read() & 0xFF );
                                  }
        
         if(usAddress==5)
                                  {
                                                               
                                timer.refresh();
                                *pucRegBuffer++ = ( unsigned char )( tim_parameter.Position_relative >> 8 );
                *pucRegBuffer++ = ( unsigned char )( tim_parameter.Position_relative &0xFF );
                                                
               
        }
                if(usAddress==6)
                                  {
                                                               
                                timer.refresh();
                                *pucRegBuffer++ = ( unsigned char )(  TIM1->CCR2>> 8 );
                *pucRegBuffer++ = ( unsigned char )(  TIM1->CCR2&0xFF );
                                                
               
        }
                                 
            break;
        case MB_REG_WRITE:
            while( usNRegs > 0 )
            {
         

                                usRegHoldingBuf[iRegIndex] = *pucRegBuffer++ << 8;
             usRegHoldingBuf[iRegIndex] |= *pucRegBuffer++;
             iRegIndex++;
                usNRegs--;
            }
        }
    }
    else
    {
        eStatus = MB_ENOREG;
    }
    return eStatus;
}

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

eMBErrorCode
eMBRegCoilsCB( UCHAR * pucRegBuffer, USHORT usAddress, USHORT usNCoils, eMBRegisterMode eMode )
{
    ( void )pucRegBuffer;
    ( void )usAddress;
    ( void )usNCoils;
    ( void )eMode;
    return MB_ENOREG;
}

//读开关寄存器 0x02
eMBErrorCode
eMBRegDiscreteCB( UCHAR * pucRegBuffer, USHORT usAddress, USHORT usNDiscrete )
{
    ( void )pucRegBuffer;
    ( void )usAddress;
    ( void )usNDiscrete;
    return MB_ENOREG;
}

经过,测试,本次移植可以读取节点的温度,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 | 只看该作者
好东西,写成文件,就更好了。

使用特权

评论回复
5
qq20707| | 2013-12-23 15:47 | 只看该作者
看看 怎么样  学习

使用特权

评论回复
6
tianli1980| | 2013-12-23 15:56 | 只看该作者
楼主介绍的步骤很详细,借鉴一下楼主的经验,项。

使用特权

评论回复
7
406978301| | 2013-12-23 19:09 | 只看该作者
不错。。。

使用特权

评论回复
8
Mrzhen009| | 2013-12-27 15:39 | 只看该作者
LZ您好!最近在做STM32的项目,也是刚接触不久,一直使用寄存器版本编写代码,没有使用STM32库函数。
现在由于也要用到485与上位机通讯,希望移植freemodbus,不知道这个协议可不可以用在寄存器开发版本的STM32工程里?谢谢!

使用特权

评论回复
9
zclmn| | 2013-12-27 23:43 | 只看该作者
使用过ModBus,简易版的,自己写的...

使用特权

评论回复
10
zjxcml| | 2013-12-28 10:37 | 只看该作者
先MARK 以后学时看 顶下

使用特权

评论回复
11
dbayj| | 2013-12-28 12:55 | 只看该作者
Mrzhen009 发表于 2013-12-27 15:39
LZ您好!最近在做STM32的项目,也是刚接触不久,一直使用寄存器版本编写代码,没有使用STM32库函数。
现在 ...

随便你怎么用都行。

使用特权

评论回复
12
Mrzhen009| | 2013-12-28 22:19 | 只看该作者
dbayj 发表于 2013-12-28 12:55
随便你怎么用都行。

谢谢!也就是说,这个freemodbus只是提供一些API,只要把源码放入工程,直接可以用它提供的函数接口实现modbus协议?这样理解对吗?

使用特权

评论回复
13
dbayj| | 2013-12-29 11:25 | 只看该作者
首先,你得把它要的驱动接口做好,然后你才可以用它提供给你的接口。至于你是用库的方式写驱动,还是用寄存器方式写驱动,都是一样的。

使用特权

评论回复
14
it_yrj| | 2014-1-7 13:16 | 只看该作者
楼主好,请教一个问题,上位机在读输入寄存器(读数字寄存器 功能码0x04)的时候是怎么跳转到回调函数
eMBErrorCode
eMBRegInputCB( UCHAR * pucRegBuffer, USHORT usAddress, USHORT usNRegs )
的,多谢了!

使用特权

评论回复
15
it_yrj| | 2014-1-7 13:17 | 只看该作者
这种数组寄存器处理方法和从Flash地址取值有何优点?
若非上述操作,如何实现取从机地址的0x4000的数据呢?

使用特权

评论回复
16
dz0658| | 2014-2-26 08:38 | 只看该作者

使用特权

评论回复
17
outstanding| | 2014-2-26 09:48 | 只看该作者

使用特权

评论回复
18
wode112233| | 2014-5-19 11:40 | 只看该作者

使用特权

评论回复
19
hsh| | 2014-5-19 16:47 | 只看该作者
谢谢!学习了。

使用特权

评论回复
20
我心永恒12| | 2014-8-30 09:23 | 只看该作者
亲,这是主机还是从机程序

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

7

主题

15

帖子

1

粉丝