相信老铁们,在实际项目开发中,当使用Freemodbus从机协议栈时,会遇到一个问题,就是网上大多数对于该协议栈的移植,在数据接收这块,使用的大都是串口中断接收模式。这样做会有一个问题,如果一条Modbus总线上有若干个从机,一个个从机接收到主机的数据请求时,返回的数据长度大于1kBytes,会有一个问题,总线上其他的从机都处于串口中断接收模式,这样的话,在这段时间内,其他的从机是处于忙碌状态。可想而知对于MCU的CPU是一种浪费。那么对于现在网上大家常用的stm32,大家在实际项目中,都有可能用到Modbus通信协议,要么是自己写,要么是移植人家的。我在实际项目总,为了省事,直接移植人家的freemodbus,做从机协议栈来使用。但是对它的数据接收模式做了一些修改,具体修改看下文代码。
posterial.c文件:该文件实现对串口的底层配置和发送实现
/*
* FreeModbus Libary: STM32 Port
* Copyright (C) 2013 Armink <armink.ztl@gmail.com>
*
* 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: 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);
void MB_DMAchannelInit (void);
extern volatile UCHAR ucRTUBuf[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_SR cpu_sr;
GPIO_InitTypeDef GPIO_InitStructure;
USART_InitTypeDef USART_InitStructure;
NVIC_InitTypeDef NVIC_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 xMBP