Freemodbus通信,串口接收模式改为DMA接收模式。

相信老铁们,在实际项目开发中,当使用Freemodbus从机协议栈时,会遇到一个问题,就是网上大多数对于该协议栈的移植,在数据接收这块,使用的大都是串口中断接收模式。这样做会有一个问题,如果一条Modbus总线上有若干个从机,一个个从机接收到主机的数据请求时,返回的数据长度大于1kBytes,会有一个问题,总线上其他的从机都处于串口中断接收模式,这样的话,在这段时间内,其他的从机是处于忙碌状态。可想而知对于MCU的CPU是一种浪费。那么对于现在网上大家常用的stm32,大家在实际项目中,都有可能用到Modbus通信协议,要么是自己写,要么是移植人家的。我在实际项目总,为了省事,直接移植人家的freemodbus,做从机协议栈来使用。但是对它的数据接收模式做了一些修改,具体修改看下文代码。
posterial.c文件:该文件实现对串口的底层配置和发送实现

/* * FreeModbus Libary: STM32 Port * Copyright (C) 2013 Armink * * 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, MA02110-1301USA * * File: $Id: portserial.c,v 1.60 2013/08/13 15:07:05 Armink $ */ /* ----------------------- Platform includes --------------------------------*/ #include "port.h"/* ----------------------- Modbus includes ----------------------------------*/ #include "mb.h" #include "mbport.h"/* ----------------------- static functions ---------------------------------*/ static void prvvUARTTxReadyISR(void); //static void prvvUARTRxISR(void); /* -------------------------- golobal function ------------------------------*/ unsigned char * exportPoint(unsigned short * length); voidMB_DMAchannelInit (void); extern volatile UCHARucRTUBuf[7*1024]; /* ----------------------- Start implementation -----------------------------*/ void vMBPortSerialEnable(BOOL xRxEnable, BOOL xTxEnable) { if (xRxEnable) { //USART_ITConfig(MODBUS_RS485_SERIAL_PORT, USART_IT_RXNE, ENABLE); SLAVE_RS485_RECEIVE_MODE; } else { SLAVE_RS485_SEND_MODE; //USART_ITConfig(MODBUS_RS485_SERIAL_PORT, USART_IT_RXNE, DISABLE); } if (xTxEnable) { USART_ITConfig(MODBUS_RS485_SERIAL_PORT, USART_IT_TXE, ENABLE); } else { USART_ITConfig(MODBUS_RS485_SERIAL_PORT, USART_IT_TXE, DISABLE); } }void vMBPortClose(void) { USART_ITConfig(MODBUS_RS485_SERIAL_PORT, USART_IT_TXE | USART_IT_RXNE, DISABLE); USART_Cmd(MODBUS_RS485_SERIAL_PORT, DISABLE); } /* 默认一个从机 串口3 波特率可设置奇偶检验可设置. */ BOOL xMBPortSerialInit(UCHAR ucPORT, ULONG ulBaudRate, UCHAR ucDataBits, eMBParity eParity) { OS_CPU_SRcpu_sr; GPIO_InitTypeDefGPIO_InitStructure; USART_InitTypeDefUSART_InitStructure; NVIC_InitTypeDefNVIC_InitStructure; /* -------------------时钟初始化------------------------------------. */ //RCC_APB2PeriphClockCmd(MODBUS_RS485_CONTRL_RCC, ENABLE); RCC_APB2PeriphClockCmd(MODBUS_PORT_SERIAL_RCC | MODBUS_RS485_CONTRL_RCC, ENABLE); /* -----------------------IO初始化---------------------------------. */ /* USART1_TX -----.*/ GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; GPIO_InitStructure.GPIO_Pin = MODBUS_PORT_SERIAL_TX_PIN; GPIO_Init(MODBUS_PORT_SERIAL_TX_GPIO,&GPIO_InitStructure); /* USART1_RX------.*/ GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; GPIO_InitStructure.GPIO_Pin = MODBUS_PORT_SERIAL_RX_PIN; GPIO_Init(MODBUS_PORT_SERIAL_RX_GPIO, &GPIO_InitStructure); /* 配置485发送和接收模----------.*/ GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; GPIO_InitStructure.GPIO_Pin = MODBUS_RS485_CONTRL_PIN; GPIO_Init(MODBUS_RS485_CONTRL_GPIO, &GPIO_InitStructure); /* 串口初始化 ---------------. */ USART_InitStructure.USART_BaudRate = ulBaudRate; switch (eParity) { case MB_PAR_NONE: USART_InitStructure.USART_Parity = USART_Parity_No; USART_InitStructure.USART_WordLength = USART_WordLength_8b; break; case MB_PAR_ODD: USART_InitStructure.USART_Parity = USART_Parity_Odd; USART_InitStructure.USART_WordLength = USART_WordLength_9b; break; case MB_PAR_EVEN: USART_InitStructure.USART_Parity = USART_Parity_Even; USART_InitStructure.USART_WordLength = USART_WordLength_9b; break; default: return FALSE; } USART_InitStructure.USART_StopBits = USART_StopBits_1; USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None; USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx; if (ucPORT != 1) return FALSE; OS_ENTER_CRITICAL(); USART_Init(MODBUS_RS485_SERIAL_PORT, &USART_InitStructure); //USART_ITConfig(MODBUS_RS485_SERIAL_PORT, USART_IT_RXNE, ENABLE); USART_Cmd(MODBUS_RS485_SERIAL_PORT, ENABLE); USART_ClearFlag(MODBUS_RS485_SERIAL_PORT,USART_FLAG_TC); USART_DMACmd(MODBUS_RS485_SERIAL_PORT,USART_DMAReq_Rx,ENABLE); /* ------------------中断初始化----------------------------- *设置NVIC优先级分组为Group2:0-3抢占式优先级,0-3的响应式优先级 -----------------------------------------------------------. */ NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); NVIC_InitStructure.NVIC_IRQChannel = MB_IRQn; NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0; NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1; NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; NVIC_Init(&NVIC_InitStructure); OS_EXIT_CRITICAL(); MB_DMAchannelInit(); return TRUE; }BOOL xMBPortSerialPutByte(CHAR ucByte) { USART_SendData(MODBUS_RS485_SERIAL_PORT, ucByte); /* add by frank 2019 -3-11. */ while(USART_GetFlagStatus(MODBUS_RS485_SERIAL_PORT, USART_FLAG_TC) == RESET){}; return TRUE; }BOOL xMBPortSerialGetByte(CHAR * pucByte) { *pucByte = USART_ReceiveData(MODBUS_RS485_SERIAL_PORT); 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 MB_IRQHandler(void) { OS_CPU_SRcpu_sr; OS_ENTER_CRITICAL(); /*发送中断. */ if (USART_GetITStatus(MODBUS_RS485_SERIAL_PORT, USART_IT_TXE) == SET) { prvvUARTTxReadyISR(); } OS_EXIT_CRITICAL(); }//函数名称:MB_DMAchannelInit //功能描述:初始化DMA接收通道 //入口参数:无 //出口参数:无voidMB_DMAchannelInit (void) { DMA_InitTypeDef DMA_InitStructure; static unsigned char*ptraddress; unsigned short length = 0; RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE); DMA_DeInit(DMA1_Channel5); DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)(&USART1->DR); DMA_InitStructure.DMA_MemoryBaseAddr= (uint32_t)(exportPoint(&length)); DMA_InitStructure.DMA_DIR= DMA_DIR_PeripheralSRC; DMA_InitStructure.DMA_BufferSize=length; DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable; DMA_InitStructure.DMA_MemoryInc= DMA_MemoryInc_Enable; DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte; DMA_InitStructure.DMA_MemoryDataSize= DMA_MemoryDataSize_Byte; DMA_InitStructure.DMA_Mode= DMA_Mode_Normal; DMA_InitStructure.DMA_Priority= DMA_Priority_VeryHigh; DMA_InitStructure.DMA_M2M= DMA_M2M_Disable; DMA_Init(DMA1_Channel5, &DMA_InitStructure); DMA_Cmd(DMA1_Channel5, ENABLE); }

porttimer.c:该文件实现对定时器的相关配置和增加定时器中断函数//发送还是使用空中断触发方式
/* * FreeModbus Libary: STM32 Port * Copyright (C) 2013 Armink * * 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, MA02110-1301USA * * File: $Id: porttimer.c,v 1.60 2013/08/13 15:07:05 Armink $ *//* ----------------------- Osal includes ------------------------------------*/ #include "includes.h"/* ----------------------- Platform includes --------------------------------*/ #include "port.h"/* ----------------------- Modbus includes ----------------------------------*/ #include "mb.h" #include "mbport.h"/* ----------------------- static functions ---------------------------------*/ static void prvvTIMERExpiredISR(void); /* ----------------------- Start implementation -----------------------------*/ BOOL xMBPortTimersInit(USHORT usTim1Timerout50us) { uint16_t PrescalerValue = https://www.it610.com/article/0; TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure; NVIC_InitTypeDef NVIC_InitStructure; //====================================时钟初始化=========================== //使能定时器3时钟 RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE); //====================================定时器初始化=========================== //定时器时间基配置说明 //HCLK为72MHz,APB1经过2分频为36MHz //TIM3的时钟倍频后为72MHz(硬件自动倍频,达到最大) //TIM3的分频系数为3599,时间基频率为72 / (1 + Prescaler) = 20KHz,基准为50us //TIM最大计数值为usTim1Timerout50u PrescalerValue = (uint16_t) (SystemCoreClock / 20000) - 1; //定时器1初始化 TIM_TimeBaseStructure.TIM_Period = (uint16_t) usTim1Timerout50us; TIM_TimeBaseStructure.TIM_Prescaler = PrescalerValue; TIM_TimeBaseStructure.TIM_ClockDivision = 0; TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure); //预装载使能 TIM_ARRPreloadConfig(TIM3, ENABLE); //====================================中断初始化============================= //设置NVIC优先级分组为Group2:0-3抢占式优先级,0-3的响应式优先级 NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); NVIC_InitStructure.NVIC_IRQChannel = TIM3_IRQn; NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1; NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1; NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; NVIC_Init(&NVIC_InitStructure); //清除溢出中断标志位 TIM_ClearITPendingBit(TIM3, TIM_IT_Update); //定时器3溢出中断关闭 TIM_ITConfig(TIM3, TIM_IT_Update, DISABLE); //定时器3禁能 TIM_Cmd(TIM3, DISABLE); return TRUE; //((1+TIM_Prescaler )/72M)*(1+TIM_Period ) }void vMBPortTimersEnable() { TIM_ClearITPendingBit(TIM3, TIM_IT_Update); TIM_ITConfig(TIM3, TIM_IT_Update, ENABLE); TIM_SetCounter(TIM3, 0); TIM_Cmd(TIM3, ENABLE); }void vMBPortTimersDisable() { TIM_ClearITPendingBit(TIM3, TIM_IT_Update); TIM_ITConfig(TIM3, TIM_IT_Update, DISABLE); TIM_SetCounter(TIM3, 0); TIM_Cmd(TIM3, DISABLE); }void prvvTIMERExpiredISR(void) { (void) pxMBPortCBTimerExpired(); }void TIM3_IRQHandler(void) { OS_CPU_SR cpu_sr; // ENTER_CRITICAL_SECTION(); OS_ENTER_CRITICAL( ); if (TIM_GetITStatus(TIM3, TIM_IT_Update) != RESET) {TIM_ClearFlag(TIM3, TIM_FLAG_Update); //清中断标记 TIM_ClearITPendingBit(TIM3, TIM_IT_Update); //清除定时器T3溢出中断标志位 prvvTIMERExpiredISR(); } // EXIT_CRITICAL_SECTION( ); OS_EXIT_CRITICAL( ); }

mbrtu.c:实现数据的接收解析和数据发送
/* * FreeModbus Libary: A portable Modbus implementation for Modbus ASCII/RTU. * Copyright (c) 2006-2018 Christian Walter * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright *notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright *notice, this list of conditions and the following disclaimer in the *documentation and/or other materials provided with the distribution. * 3. The name of the author may not be used to endorse or promote products *derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * *//* ----------------------- Osal includes ------------------------------------*/ #include "includes.h" /* ----------------------- System includes ----------------------------------*/ #include "stdlib.h" #include "string.h"/* ----------------------- Platform includes --------------------------------*/ #include "port.h"/* ----------------------- Modbus includes ----------------------------------*/ #include "mb.h" #include "mbrtu.h" #include "mbframe.h"#include "mbcrc.h" #include "mbport.h"/* ----------------------- task includes -------------------------------------*/ #include "app_task.h"/* ----------------------- Defines ------------------------------------------*/ #define MB_SER_PDU_SIZE_MIN4/*!< Minimum size of a Modbus RTU frame. */ #define MB_SER_PDU_SIZE_MAX(7 *1024)/*!< Maximum size of a Modbus RTU frame. */ #define MB_SER_PDU_SIZE_CRC2/*!< Size of CRC field in PDU. */ #define MB_SER_PDU_ADDR_OFF0/*!< Offset of slave address in Ser-PDU. */ #define MB_SER_PDU_PDU_OFF1/*!< Offset of Modbus-PDU in Ser-PDU. *//* ----------------------- Type definitions ---------------------------------*/ typedef enum { STATE_RX_INIT,/*!< Receiver is in initial state. */ STATE_RX_IDLE,/*!< Receiver is in idle state. */ STATE_RX_RCV,/*!< Frame is beeing received. */ STATE_RX_ERROR/*!< If the frame is invalid. */ } eMBRcvState; typedef enum { STATE_TX_IDLE,/*!< Transmitter is in idle state. */ STATE_TX_XMIT/*!< Transmitter is in transfer state. */ } eMBSndState; /* ----------------------- Static variables ---------------------------------*/ static volatile eMBSndState eSndState; static volatile eMBRcvState eRcvState; /* ------------------------USART SEND BUFFER ---------------------------------*/ volatile UCHARucRTUBuf[MB_SER_PDU_SIZE_MAX]; static volatile UCHAR *pucSndBufferCur; static volatile USHORT usSndBufferCount; static volatile USHORT usRcvBufferPos; /*--------------------------- Golobal functions ------------------------------*/ voidMB_DMAchannelInit (void); /* ----------------------- Start implementation -----------------------------*/ eMBErrorCode eMBRTUInit( UCHAR ucSlaveAddress, UCHAR ucPort, ULONG ulBaudRate, eMBParity eParity ) { eMBErrorCodeeStatus = MB_ENOERR; ULONGusTimerT35_50us; OS_CPU_SRcpu_sr; ( void )ucSlaveAddress; // ENTER_CRITICAL_SECTION(); OS_ENTER_CRITICAL( ); /* Modbus RTU uses 8 Databits. */ if( xMBPortSerialInit( ucPort, ulBaudRate, 8, eParity ) != TRUE ) { eStatus = MB_EPORTERR; } else { /* - 修改为固定100则定时5ms一次中断. */ usTimerT35_50us = 100; if( xMBPortTimersInit( ( USHORT ) usTimerT35_50us ) != TRUE ) { eStatus = MB_EPORTERR; } } //EXIT_CRITICAL_SECTION(); OS_EXIT_CRITICAL( ); return eStatus; }void eMBRTUStart( void ) { OS_CPU_SRcpu_sr; // ENTER_CRITICAL_SECTION(); OS_ENTER_CRITICAL( ); /* Initially the receiver is in the state STATE_RX_INIT. we start * the timer and if no character is received within t3.5 we change * to STATE_RX_IDLE. This makes sure that we delay startup of the * modbus protocol stack until the bus is free. */ eRcvState = STATE_RX_INIT; vMBPortSerialEnable( TRUE, FALSE ); vMBPortTimersEnable(); OS_EXIT_CRITICAL( ); // EXIT_CRITICAL_SECTION(); }void eMBRTUStop( void ) { OS_CPU_SRcpu_sr; //ENTER_CRITICAL_SECTION(); OS_ENTER_CRITICAL( ); vMBPortSerialEnable( FALSE, FALSE ); vMBPortTimersDisable(); OS_EXIT_CRITICAL( ); //EXIT_CRITICAL_SECTION(); }eMBErrorCode eMBRTUReceive( UCHAR * pucRcvAddress, UCHAR ** pucFrame, USHORT * pusLength ) { OS_CPU_SRcpu_sr; BOOLxFrameReceived = FALSE; eMBErrorCodeeStatus = MB_ENOERR; OS_ENTER_CRITICAL( ); //ENTER_CRITICAL_SECTION(); assert( usRcvBufferPos < MB_SER_PDU_SIZE_MAX ); /* Length and CRC check */ if( ( usRcvBufferPos >= MB_SER_PDU_SIZE_MIN ) && ( usMBCRC16( ( UCHAR * ) ucRTUBuf, usRcvBufferPos ) == 0 ) ) { /* Save the address field. All frames are passed to the upper layed * and the decision if a frame is used is done there. */ *pucRcvAddress = ucRTUBuf[MB_SER_PDU_ADDR_OFF]; /* Total length of Modbus-PDU is Modbus-Serial-Line-PDU minus * size of address field and CRC checksum. */ *pusLength = ( USHORT )( usRcvBufferPos - MB_SER_PDU_PDU_OFF - MB_SER_PDU_SIZE_CRC ); /* Return the start of the Modbus PDU to the caller. */ *pucFrame = ( UCHAR * ) & ucRTUBuf[MB_SER_PDU_PDU_OFF]; xFrameReceived = TRUE; } else { eStatus = MB_EIO; } OS_EXIT_CRITICAL( ); //EXIT_CRITICAL_SECTION(); return eStatus; }eMBErrorCode eMBRTUSend( UCHAR ucSlaveAddress, const UCHAR * pucFrame, USHORT usLength ) { eMBErrorCodeeStatus = MB_ENOERR; USHORTusCRC16; OS_CPU_SRcpu_sr; // ENTER_CRITICAL_SECTION(); OS_ENTER_CRITICAL( ); /* Check if the receiver is still in idle state. If not we where to * slow with processing the received frame and the master sent another * frame on the network. We have to abort sending the frame. */ if( eRcvState == STATE_RX_IDLE ) { /* First byte before the Modbus-PDU is the slave address. */ pucSndBufferCur = ( UCHAR * ) pucFrame - 1; usSndBufferCount = 1; /* Now copy the Modbus-PDU into the Modbus-Serial-Line-PDU. */ pucSndBufferCur[MB_SER_PDU_ADDR_OFF] = ucSlaveAddress; usSndBufferCount += usLength; /* Calculate CRC16 checksum for Modbus-Serial-Line-PDU. */ usCRC16 = usMBCRC16( ( UCHAR * ) pucSndBufferCur, usSndBufferCount ); ucRTUBuf[usSndBufferCount++] = ( UCHAR )( usCRC16 & 0xFF ); ucRTUBuf[usSndBufferCount++] = ( UCHAR )( usCRC16 >> 8 ); /* Activate the transmitter. */ eSndState = STATE_TX_XMIT; vMBPortSerialEnable( FALSE, TRUE ); } else { eStatus = MB_EIO; } // EXIT_CRITICAL_SECTION(); OS_EXIT_CRITICAL( ); return eStatus; }BOOL xMBRTUReceiveFSM( void ) { BOOLxTaskNeedSwitch = FALSE; UCHARucByte; assert( eSndState == STATE_TX_IDLE ); /* Always read the character. */ ( void )xMBPortSerialGetByte( ( CHAR * ) & ucByte ); switch ( eRcvState ) { /* If we have received a character in the init state we have to * wait until the frame is finished. */ case STATE_RX_INIT: vMBPortTimersEnable(); break; /* In the error state we wait until all characters in the * damaged frame are transmitted. */ case STATE_RX_ERROR: vMBPortTimersEnable(); break; /* In the idle state we wait for a new character. If a character * is received the t1.5 and t3.5 timers are started and the * receiver is in the state STATE_RX_RECEIVCE. */ case STATE_RX_IDLE: usRcvBufferPos = 0; ucRTUBuf[usRcvBufferPos++] = ucByte; eRcvState = STATE_RX_RCV; /* Enable t3.5 timers. */ vMBPortTimersEnable(); break; /* We are currently receiving a frame. Reset the timer after * every character received. If more than the maximum possible * number of bytes in a modbus frame is received the frame is * ignored. */ case STATE_RX_RCV: if( usRcvBufferPos < MB_SER_PDU_SIZE_MAX ) { ucRTUBuf[usRcvBufferPos++] = ucByte; } else { eRcvState = STATE_RX_ERROR; } vMBPortTimersEnable(); break; } return xTaskNeedSwitch; }BOOL xMBRTUTransmitFSM( void ) { BOOLxNeedPoll = FALSE; assert( eRcvState == STATE_RX_IDLE ); switch ( eSndState ) { /* We should not get a transmitter event if the transmitter is in * idle state.*/ case STATE_TX_IDLE: /* enable receiver/disable transmitter. */ vMBPortSerialEnable( TRUE, FALSE ); break; case STATE_TX_XMIT: /* check if we are finished. */ if( usSndBufferCount != 0 ) { xMBPortSerialPutByte( ( CHAR )*pucSndBufferCur ); pucSndBufferCur++; /* next byte in sendbuffer. */ usSndBufferCount--; } else { xNeedPoll = xMBPortEventPost( EV_FRAME_SENT ); /* Disable transmitter. This prevents another transmit buffer * empty interrupt. */vMBPortSerialEnable( TRUE, FALSE ); eSndState = STATE_TX_IDLE; } break; }return xNeedPoll; }BOOL xMBRTUTimerT35Expired( void ) { BOOLxNeedPoll = FALSE; static unsigned charxtimeoutCount = 0; static unsigned shortxPreviousLength = 0; unsigned shortxNowLength = 0; /* 超时计数. */ xtimeoutCount ++; /* 获取dma通道剩余长度. */ xNowLength = DMA_GetCurrDataCounter(DMA1_Channel5); /*- 如果DMA通道仍然在接收数据,则清空超时计数. */ if (xNowLength != xPreviousLength) { xtimeoutCount= 0; xPreviousLength = xNowLength; } else { /*- 10ms内没有接收到数据,表示通道处于空闲状态. */ if (xtimeoutCount > 2) { if (xNowLength < MB_SER_PDU_SIZE_MAX ) { usRcvBufferPos = MB_SER_PDU_SIZE_MAX - xNowLength; DMA_ClearFlag(DMA1_FLAG_TC5); DMA_Cmd(DMA1_Channel5,DISABLE); MB_DMAchannelInit(); /*- 修改状态机的状态,表示接收完毕. */ xNeedPoll = xMBPortEventPost( EV_FRAME_RECEIVED ); eRcvState = STATE_RX_IDLE; } xtimeoutCount = 0; } } return xNeedPoll; }//导出指针 unsigned char * exportPoint(unsigned short * length) { *length = MB_SER_PDU_SIZE_MAX; return (unsigned char *)&ucRTUBuf[0]; }


【Freemodbus通信,串口接收模式改为DMA接收模式。】port.h:串口的相关配置宏
  • /* * FreeModbus Libary: BARE Port * Copyright (C) 2006 Christian Walter * * 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, MA02110-1301USA * * File: $Id$ */#ifndef _PORT_H #define _PORT_H#include #include #include "includes.h" #include #include /* ---------------------------------STM32 includes ----------------------------*/ #include "stm32f10x.h" #define INLINEinline #define PR_BEGIN_EXTERN_Cextern "C" { #define PR_END_EXTERN_C}/* --------------------------Modbus macros -----------------------------------*/ #define MODBUS_PORT_SERIAL_RCCRCC_APB2Periph_USART1 #define MODBUS_PORT_SERIAL_TX_GPIOGPIOA #define MODBUS_PORT_SERIAL_RX_GPIOGPIOA #define MODBUS_PORT_SERIAL_TX_PINGPIO_Pin_9 #define MODBUS_PORT_SERIAL_RX_PINGPIO_Pin_10#define MODBUS_RS485_CONTRL_GPIOGPIOA #define MODBUS_RS485_CONTRL_PINGPIO_Pin_11 #define MODBUS_RS485_CONTRL_RCCRCC_APB2Periph_GPIOA #define MODBUS_RS485_SERIAL_PORTUSART1#define MB_IRQnUSART1_IRQn #define MB_IRQHandlerUSART1_IRQHandler //TODO暂时先写B2引脚,等组网测试时再确认 #define SLAVE_RS485_SEND_MODEGPIO_SetBits(MODBUS_RS485_CONTRL_GPIO,MODBUS_RS485_CONTRL_PIN) #define SLAVE_RS485_RECEIVE_MODEGPIO_ResetBits(MODBUS_RS485_CONTRL_GPIO,MODBUS_RS485_CONTRL_PIN)#define ENTER_CRITICAL_SECTION( ) #define EXIT_CRITICAL_SECTION( )/*位带操作,实现51类似的GPIO控制功能 *具体实现思想,参考<>第五章(87页~92页).IO口操作宏定义*/ #define BITBAND(addr, bitnum) ((addr & 0xF0000000)+ 0x2000000 \ +((addr &0xFFFFF)<<5)+(bitnum<<2)) #define MEM_ADDR(addr)*((volatile unsigned long*)(addr)) #define BIT_ADDR(addr, bitnum)MEM_ADDR(BITBAND(addr, bitnum)) /* ------------------------IO口地址映射 ----------------------------------------*/ #define GPIOA_ODR_Addr(GPIOA_BASE+12) /*!> 0x4001080C */ #define GPIOB_ODR_Addr(GPIOB_BASE+12) /*!> 0x40010C0C */ #define GPIOC_ODR_Addr(GPIOC_BASE+12) /*!> 0x4001100C */ #define GPIOD_ODR_Addr(GPIOD_BASE+12) /*!> 0x4001140C */ #define GPIOE_ODR_Addr(GPIOE_BASE+12) /*!> 0x4001180C */ #define GPIOF_ODR_Addr(GPIOF_BASE+12) /*!> 0x40011A0C */ #define GPIOG_ODR_Addr(GPIOG_BASE+12) /*!> 0x40011E0C */ #define GPIOA_IDR_Addr(GPIOA_BASE+8)/*!> 0x40010808 */ #define GPIOB_IDR_Addr(GPIOB_BASE+8)/*!> 0x40010C08 */ #define GPIOC_IDR_Addr(GPIOC_BASE+8)/*!> 0x40011008 */ #define GPIOD_IDR_Addr(GPIOD_BASE+8)/*!> 0x40011408 */ #define GPIOE_IDR_Addr(GPIOE_BASE+8)/*!> 0x40011808 */ #define GPIOF_IDR_Addr(GPIOF_BASE+8)/*!> 0x40011A08 */ #define GPIOG_IDR_Addr(GPIOG_BASE+8)/*!> 0x40011E08 */ /* ----------------------- IO口操作,只对单一的IO口 ----------------------------*/#define PAout(n)BIT_ADDR(GPIOA_ODR_Addr,n) #define PAin(n)BIT_ADDR(GPIOA_IDR_Addr,n)#define PBout(n)BIT_ADDR(GPIOB_ODR_Addr,n) #define PBin(n)BIT_ADDR(GPIOB_IDR_Addr,n)#define PCout(n)BIT_ADDR(GPIOC_ODR_Addr,n) #define PCin(n)BIT_ADDR(GPIOC_IDR_Addr,n)#define PDout(n)BIT_ADDR(GPIOD_ODR_Addr,n) #define PDin(n)BIT_ADDR(GPIOD_IDR_Addr,n)#define PEout(n)BIT_ADDR(GPIOE_ODR_Addr,n) #define PEin(n)BIT_ADDR(GPIOE_IDR_Addr,n)#define PFout(n)BIT_ADDR(GPIOF_ODR_Addr,n) #define PFin(n)BIT_ADDR(GPIOF_IDR_Addr,n) #define PGout(n)BIT_ADDR(GPIOG_ODR_Addr,n) #define PGin(n)BIT_ADDR(GPIOG_IDR_Addr,n)#define LED1PCout(6)#define GREEN_LED_BLINK() do \ {\ LED1 = ~LED1 ; \ }while(0) typedef uint8_t BOOL; typedef unsigned char UCHAR; typedef char CHAR; typedef uint16_t USHORT; typedef int16_t SHORT; typedef uint32_t ULONG; typedef int32_t LONG; #ifndef TRUE #define TRUE1 #endif#ifndef FALSE #define FALSE0 #endif #endif


  • 在这里简单介绍一下,实现方式。上面三个文件起始已经把实现代码展示出来了,主要想说的是,第一步在串口配置中,开启串口DMA接收功能。
  • 将原有初始化串口的函数中,将定时器时间修改为5ms中断一次
  • 在定时器中断函数中,做超时接收判断,接收完成,则修改状态机的状态。
  • 注意啊,老铁们,原作者发送和接收缓冲Buff使用的都是volatile UCHARucRTUBuf[MB_SER_PDU_SIZE_MAX];
  • 人家实现状态机的切换,其实就是在定时器中完成的,且对发送和接收冲突通过标志位,做了一下处理。
  • 代码给力的话,给我加分哦。老铁门。
  • 如有转载请备注出处

    推荐阅读