freemodbus 在STM32上的移植

来源:互联网 发布:阿里云 深信服 编辑:程序博客网 时间:2024/05/14 12:56

        这几天,研究了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 -----------------------------*/BOOLxMBPortTimersInit( USHORT usTim1Timerout50us ) //配置50us时钟{uint16_t PrescalerValue = 0;/* TIM2 clock enable */RCC->CFGR.B.PPRE1 = 4;  //APB1预分频系数                          //100:HCLK 2分频                        //APB1 不超过36MHzRCC->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文件修改如下

/* ----------------------- 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>#defineRS485_T    GPIOA->BSRR.B.SETIO4=1#define RS485_R   GPIOA->BSRR.B.CLRIO4=1/* ----------------------- Start implementation -----------------------------*/voidvMBPortSerialEnable( 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;    }}/****************************************** 配置串口 目前除了波特率其他参数无效 * Usart1 9600-8-N-1*****************************************/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 9600USART2->BRR.B.DIV_Mantissa=temp2;//DIV_Mantissa[11:0]:USARTDIV的整数部分//这12位定义了USART分频器除法因子(USARTDIV)的整数部分。//234 9600USART2->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;}// 串口发BOOLxMBPortSerialPutByte( 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;}// 串口收BOOLxMBPortSerialGetByte( 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" unsigned short int  usRegInputBuf[10]={0x0000,0xfe02,0x1203,0x1304,0x1405,0x1506,0x1607,0x1708,0x1809};unsigned short int *usRegHoldingBuf=usRegInputBuf;//一个测试用的 寄存器数组 地址0-7unsigned 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;//读数字寄存器 功能码0x04eMBErrorCodeeMBRegInputCB( 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 和写0x06eMBErrorCodeeMBRegHoldingCB( 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  x05eMBErrorCodeeMBRegCoilsCB( UCHAR * pucRegBuffer, USHORT usAddress, USHORT usNCoils, eMBRegisterMode eMode ){    ( void )pucRegBuffer;    ( void )usAddress;    ( void )usNCoils;    ( void )eMode;    return MB_ENOREG;}//读开关寄存器 0x02eMBErrorCodeeMBRegDiscreteCB( UCHAR * pucRegBuffer, USHORT usAddress, USHORT usNDiscrete ){    ( void )pucRegBuffer;    ( void )usAddress;    ( void )usNDiscrete;    return MB_ENOREG;}



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

 csdn好像不支持工程的上传,有需要的话联系我 qq 1109503213,一起交流学习