FreeModbus开源协议栈的移植和详解(四)- FreeModbus在STM32上的移植

    xiaoxiao2023-10-14  162

    FreeModbus开源协议栈的移植和详解(四)

    概述一、移植前的准备二、将FreeModbus文件源码添加到STM32工程中三、PORT文件夹修改1、port.h文件2、portserial.c2.1 `vMBPortSerialEnable()`函数2.2`xMBPortSerialInit()`函数2.3`xMBPortSerialPutByte()`函数2.4`xMBPortSerialGetByte()`函数2.5`USART1_IRQHandler()`函数 3、porttimer.c3.1 `xMBPortTimersInit()`函数3.2`vMBPortTimersEnable()`函数3.3 `vMBPortTimersDisable()`函数3.4 `TIM2_IRQHandler()`函数 4、portevent.c4.1`xMBPortEventInit()`函数4.2`xMBPortEventPost()`函数4.1`xMBPortEventGet()`函数 5、main.c6、其他注意地方

    概述

    在前面几篇文章中,对FreeModbus文件的源码进行了分析,还剩下与平台相关的接口部分,在这里通过对FreeModbus在STM32上的移植过程为例来介绍FreeModbus的接口部分。

    一、移植前的准备

    移植FreeModbus之前需要准备好FreeModbus源码,关于源码的获取方式,参考我之前的文章: https://blog.csdn.net/u014100102/article/details/90453930 STM32的工程,这个在此不做说明,只要之前做过STM32的对STM32工程的建立都比较熟悉了。 总结: 移植前需要准备: 1、FreeModbus源码 2、STM32基础工程

    二、将FreeModbus文件源码添加到STM32工程中

    1、在工程中新建FreeModbus文件夹,将Modbus文件夹中的所有文件及文件夹复制进来。 2、将FreeModbus-v1.6->demo->AVR->port文件夹复制到STM32工程中的FreeModbus文件夹中。并将文件添加到工程中。 3、文件添加好之后,来开始看port文件夹下的文件。里面的文件需要修改,移植到STM32平台上,就可以使用了。

    三、PORT文件夹修改

    1、port.h文件

    port.h文件主要定义了FreeModbus使用到的数据类型定义,这里和平台相关的有一个,就是进出临界区的定义,不同的MCU的定义不同,STM32修改如下:

    #define ENTER_CRITICAL_SECTION() __set_PRIMASK(1) //关总中断 #define EXIT_CRITICAL_SECTION() __set_PRIMASK(0) //开总中断

    此外,头文件也需要修改,包含stm32的头文件

    #include "stm32f10x.h"

    2、portserial.c

    port serial.c文件夹中是实现底层串口的相关函数。这里先贴出代码。

    #include "port.h" /* ----------------------- Modbus includes ----------------------------------*/ #include "mb.h" #include "mbport.h" /**控制串口发送和接收中断**/ void vMBPortSerialEnable( BOOL xRxEnable, BOOL xTxEnable ) { if(xRxEnable == TRUE) { USART_ITConfig(USART1, USART_IT_RXNE, ENABLE); //接收非空中断 } else { USART_ITConfig(USART1, USART_IT_RXNE, DISABLE); } if(xTxEnable == TRUE) { USART_ITConfig(USART1, USART_IT_TXE, ENABLE); //发送中断空 } else { USART_ITConfig(USART1, USART_IT_TXE, DISABLE); } } BOOL xMBPortSerialInit( UCHAR ucPORT, ULONG ulBaudRate, UCHAR ucDataBits, eMBParity eParity ) { GPIO_InitTypeDef GPIO_InitStructure; USART_InitTypeDef USART_InitStructure; NVIC_InitTypeDef NVIC_InitStructure; /* prevent compiler warning. */ (void)ucPORT; //USART1端口配置 RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1|RCC_APB2Periph_GPIOA, ENABLE); //使能USART1,GPIOA时钟 //USART1_TX PA.9 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9; //PA.9 GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //复用推挽输出 GPIO_Init(GPIOA, &GPIO_InitStructure); //初始化PA9 //USART1_RX PA.10 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;//浮空输入 GPIO_Init(GPIOA, &GPIO_InitStructure); //初始化PA10 USART_InitStructure.USART_BaudRate = ulBaudRate; if(ucDataBits == 9) USART_InitStructure.USART_WordLength = USART_WordLength_9b; // 9位数据 ; else USART_InitStructure.USART_WordLength = USART_WordLength_8b; // 8位数据 ; if(eParity == MB_PAR_ODD) USART_InitStructure.USART_Parity = USART_Parity_Odd; // 奇校验; else if(eParity == MB_PAR_EVEN) USART_InitStructure.USART_Parity = USART_Parity_Even; // 偶校验; else USART_InitStructure.USART_Parity = USART_Parity_No; // 无校验位; USART_InitStructure.USART_StopBits = USART_StopBits_1; // 在帧结尾传输1个停止位 ; USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None; // 硬件流控制失能 ; USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx; // 接收发送使能 ; USART_Init(USART1, &USART_InitStructure); // USART_ITConfig(USART1, USART_IT_RXNE, ENABLE); USART_Cmd(USART1, ENABLE); // NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn; NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0; NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3; NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; NVIC_Init(&NVIC_InitStructure); return TRUE; } BOOL xMBPortSerialPutByte( CHAR ucByte ) { USART_SendData(USART1, ucByte); while (USART_GetFlagStatus(USART1, USART_FLAG_TC) == RESET);/*等待发送完成*/ return TRUE; } BOOL xMBPortSerialGetByte( CHAR * pucByte ) { *pucByte = USART_ReceiveData(USART1); return TRUE; } void USART1_IRQHandler(void) //串口1中断服务程序 { u8 Res; OSIntEnter(); if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET) { pxMBFrameCBByteReceived(); USART_ClearITPendingBit(USART1, USART_IT_RXNE); } if(USART_GetITStatus(USART1, USART_IT_TXE) != RESET) { pxMBFrameCBTransmitterEmpty(); USART_ClearITPendingBit(USART1, USART_IT_TXE); } OSIntExit(); //退出中断 }

    2.1 vMBPortSerialEnable()函数

    该函数实现STM32串口发送中断和接收中断的使能。

    2.2xMBPortSerialInit()函数

    该函数对UART串口进行初始化,由eMBRTUInit函数进行调用。

    2.3xMBPortSerialPutByte()函数

    串口发送函数,将STM32串口发送函数进行封装,供协议栈使用

    2.4xMBPortSerialGetByte()函数

    串口接收函数,将STM32串口接收函数进行封装,供协议栈使用

    2.5USART1_IRQHandler()函数

    串口中断处理函数,包含发送中断和接收中断,都是调用之前mbrtu.c中的中断函数进行处理,关于mbrtu.c,请参考之前的文章: https://blog.csdn.net/u014100102/article/details/90543437

    3、porttimer.c

    该文件主要包含定时器处理相关函数,话不多说,直接贴代码

    /* * FreeModbus Libary: BARE Port * Copyright (C) 2006 Christian Walter <wolti@sil.at> * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA * * File: $Id$ */ /* ----------------------- Platform includes --------------------------------*/ #include "port.h" /* ----------------------- Modbus includes ----------------------------------*/ #include "mb.h" #include "mbport.h" /* ----------------------- static functions ---------------------------------*/ /* ----------------------- Start implementation -----------------------------*/ BOOL xMBPortTimersInit( USHORT usTim1Timerout50us ) { TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure; NVIC_InitTypeDef NVIC_InitStructure; RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE); //时钟使能 TIM_TimeBaseInitStructure.TIM_Period = usTim1Timerout50us; TIM_TimeBaseInitStructure.TIM_Prescaler=16800-1; //定时器分频 50us TIM_TimeBaseInitStructure.TIM_CounterMode=TIM_CounterMode_Up; //向上计数模式 TIM_TimeBaseInitStructure.TIM_ClockDivision=TIM_CKD_DIV1; TIM_TimeBaseInit(TIM2,&TIM_TimeBaseInitStructure);//初始化TIM NVIC_InitStructure.NVIC_IRQChannel=TIM2_IRQn; //定时器3中断 NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=1; //抢占优先级1 NVIC_InitStructure.NVIC_IRQChannelSubPriority=9; //子优先级8 NVIC_InitStructure.NVIC_IRQChannelCmd=ENABLE; NVIC_Init(&NVIC_InitStructure); //TIM_ITConfig(TIM2,TIM_IT_Update,ENABLE); //允许定时器更新中断 TIM_Cmd(TIM2,ENABLE); //使能定时器 return TRUE; } inline void vMBPortTimersEnable( ) { /* Enable the timer with the timeout passed to xMBPortTimersInit( ) */ TIM_ClearITPendingBit(TIM2, TIM_IT_Update); TIM_SetCounter(TIM2, 0); //TIM_Cmd(TIM2, ENABLE); TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE); } inline void vMBPortTimersDisable( ) { /* Disable any pending timers. */ TIM_SetCounter(TIM2, 0); //TIM_Cmd(TIM2, DISABLE); TIM_ITConfig(TIM2, TIM_IT_Update, DISABLE); } void TIM2_IRQHandler(void) { OSIntEnter(); //进入中断 if(TIM_GetITStatus(TIM2,TIM_IT_Update)==SET) //溢出中断 { pxMBPortCBTimerExpired(); } TIM_ClearITPendingBit(TIM2, TIM_IT_Update); OSIntExit(); }

    3.1 xMBPortTimersInit()函数

    定时器初始化函数,初始化定时器50us一次中断。

    3.2vMBPortTimersEnable()函数

    使能定时器中断

    3.3 vMBPortTimersDisable()函数

    禁止定时器中断

    3.4 TIM2_IRQHandler()函数

    定时器中断处理函数,调用mbrtu.c中的定时器中断函数,关于mbrtu.c,请参考之前的文章: https://blog.csdn.net/u014100102/article/details/90543437

    4、portevent.c

    该文件主要包含事件处理相关函数,话不多说,直接贴代码

    /* ----------------------- Modbus includes ----------------------------------*/ #include "mb.h" #include "mbport.h" /* ----------------------- Variables ----------------------------------------*/ static eMBEventType eQueuedEvent; static BOOL xEventInQueue; /* ----------------------- Start implementation -----------------------------*/ BOOL xMBPortEventInit( void ) { xEventInQueue = FALSE; return TRUE; } BOOL xMBPortEventPost( eMBEventType eEvent ) { xEventInQueue = TRUE; eQueuedEvent = eEvent; return TRUE; } BOOL xMBPortEventGet( eMBEventType * eEvent ) { BOOL xEventHappened = FALSE; if( xEventInQueue ) { *eEvent = eQueuedEvent; xEventInQueue = FALSE; xEventHappened = TRUE; } return xEventHappened; }

    4.1xMBPortEventInit()函数

    初始化事件队列

    4.2xMBPortEventPost()函数

    发送一个事件

    4.1xMBPortEventGet()函数

    读取一个事件

    5、main.c

    上面把接口文件都移植好了,现在看一下main函数怎么使用

    #define REG_HOLDING_START 0x2000 #define REG_HOLDING_NREGS 4 /* ----------------------- Static variables ---------------------------------*/ static USHORT usRegHoldingStart = REG_HOLDING_START; static USHORT usRegHoldingBuf[REG_HOLDING_NREGS] = {23, 45, 88, 32}; int main(void) { delay_init(); //延时初始化 NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //中断分组配置 eMBInit(MB_RTU, 0x01, 0, 115200, MB_PAR_NONE); //初始化freemodbus 设置RTU模式和ID等 eMBEnable(); while(1) { eMBPoll(); delay_ms(30); } } 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 ) { /* Pass current register values to the protocol stack. */ case MB_REG_READ: while( usNRegs > 0 ) { *pucRegBuffer++ = ( unsigned char )( usRegHoldingBuf[iRegIndex] >> 8 ); *pucRegBuffer++ = ( unsigned char )( usRegHoldingBuf[iRegIndex] & 0xFF ); iRegIndex++; usNRegs--; } break; /* Update current register values with new values from the * protocol stack. */ case MB_REG_WRITE: while( usNRegs > 0 ) { usRegHoldingBuf[iRegIndex] = *pucRegBuffer++ << 8; usRegHoldingBuf[iRegIndex] |= *pucRegBuffer++; iRegIndex++; usNRegs--; } } } else { eStatus = MB_ENOREG; } return eStatus; } /** * @功能 * @参数 * @返回值 */ 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; }

    这里主函数主要是进行协议栈的初始化、使能和轮循,之前的博客已经介绍过,再次不做介绍。可以看之前的文章。下面两个函数实现具体的逻辑,就是读保持寄存器的值和寄存器输入。这两个函数都由freemodbus中具体的功能码处理函数调用。

    6、其他注意地方

    FreeModbus代码中有一处有问题,代码如下:

    eMBException eMBFuncReadHoldingRegister( UCHAR * pucFrame, USHORT * usLen ) { USHORT usRegAddress; USHORT usRegCount; UCHAR *pucFrameCur; eMBException eStatus = MB_EX_NONE; eMBErrorCode eRegStatus; if( *usLen == ( MB_PDU_FUNC_READ_SIZE + MB_PDU_SIZE_MIN ) ) { usRegAddress = ( USHORT )( pucFrame[MB_PDU_FUNC_READ_ADDR_OFF] << 8 ); usRegAddress |= ( USHORT )( pucFrame[MB_PDU_FUNC_READ_ADDR_OFF + 1] ); //usRegAddress++; usRegCount = ( USHORT )( pucFrame[MB_PDU_FUNC_READ_REGCNT_OFF] << 8 ); usRegCount = ( USHORT )( pucFrame[MB_PDU_FUNC_READ_REGCNT_OFF + 1] ); /* Check if the number of registers to read is valid. If not * return Modbus illegal data value exception. */ if( ( usRegCount >= 1 ) && ( usRegCount <= MB_PDU_FUNC_READ_REGCNT_MAX ) ) { /* Set the current PDU data pointer to the beginning. */ pucFrameCur = &pucFrame[MB_PDU_FUNC_OFF]; *usLen = MB_PDU_FUNC_OFF; /* First byte contains the function code. */ *pucFrameCur++ = MB_FUNC_READ_HOLDING_REGISTER; *usLen += 1; /* Second byte in the response contain the number of bytes. */ *pucFrameCur++ = ( UCHAR ) ( usRegCount * 2 ); *usLen += 1; /* Make callback to fill the buffer. */ eRegStatus = eMBRegHoldingCB( pucFrameCur, usRegAddress, usRegCount, MB_REG_READ ); /* If an error occured convert it into a Modbus exception. */ if( eRegStatus != MB_ENOERR ) { eStatus = prveMBError2Exception( eRegStatus ); } else { *usLen += usRegCount * 2; } } else { eStatus = MB_EX_ILLEGAL_DATA_VALUE; } } else { /* Can't be a valid request because the length is incorrect. */ eStatus = MB_EX_ILLEGAL_DATA_VALUE; } return eStatus; }

    将以下这句话注释掉

    //usRegAddress++;

    本人移植的代码如下

    说明:本人移植的代码包含UCOSIII操作系统且在GNU编译器下移植。 链接如下: https://download.csdn.net/download/u014100102/11203636 其他FreeModbus代码请参考我其他文章。

    最新回复(0)