[应用相关]

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

[复制链接]
1913|71
手机看帖
扫描二维码
随时随地手机跟帖
東南博士|  楼主 | 2020-2-11 10:45 | 显示全部楼层 |阅读模式
Freemodbus通信,串口接收模式改为DMA接收模式

使用特权

评论回复
東南博士|  楼主 | 2020-2-11 10:48 | 显示全部楼层
在实际项目开发中,当使用Freemodbus从机协议栈时,会遇到一个问题,就是网上大多数对于该协议栈的移植,在数据接收这块,使用的大都是串口中断接收模式。这样做会有一个问题,如果一条Modbus总线上有若干个从机,一个个从机接收到主机的数据请求时,返回的数据长度大于1kBytes,会有一个问题,总线上其他的从机都处于串口中断接收模式,这样的话,在这段时间内,其他的从机是处于忙碌状态。可想而知对于MCU的CPU是一种浪费。那么对于现在网上大家常用的stm32,大家在实际项目中,都有可能用到Modbus通信协议,要么是自己写,要么是移植人家的。我在实际项目总,为了省事,直接移植人家的freemodbus,做从机协议栈来使用。但是对它的数据接收模式做了一些修改,具体修改看下文代码。

使用特权

评论回复
東南博士|  楼主 | 2020-2-11 10:49 | 显示全部楼层
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];

使用特权

评论回复
東南博士|  楼主 | 2020-2-11 10:50 | 显示全部楼层
/* ----------------------- 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);
        }
}

使用特权

评论回复
東南博士|  楼主 | 2020-2-11 10:50 | 显示全部楼层
void vMBPortClose(void)
{
        USART_ITConfig(MODBUS_RS485_SERIAL_PORT, USART_IT_TXE | USART_IT_RXNE, DISABLE);
        USART_Cmd(MODBUS_RS485_SERIAL_PORT, DISABLE);
}

使用特权

评论回复
東南博士|  楼主 | 2020-2-11 10:51 | 显示全部楼层
/* 默认一个从机 串口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;
}

使用特权

评论回复
東南博士|  楼主 | 2020-2-11 10:51 | 显示全部楼层
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;
}

使用特权

评论回复
東南博士|  楼主 | 2020-2-11 10:51 | 显示全部楼层
BOOL xMBPortSerialGetByte(CHAR * pucByte)
{
        *pucByte = USART_ReceiveData(MODBUS_RS485_SERIAL_PORT);
        return TRUE;
}

使用特权

评论回复
東南博士|  楼主 | 2020-2-11 10:52 | 显示全部楼层
/*
* 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();
}

使用特权

评论回复
東南博士|  楼主 | 2020-2-11 10:55 | 显示全部楼层
/*
* 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_SR  cpu_sr;

        OS_ENTER_CRITICAL();

        /*  发送中断. */
        if (USART_GetITStatus(MODBUS_RS485_SERIAL_PORT, USART_IT_TXE) == SET)
        {
                prvvUARTTxReadyISR();
        }
       

        OS_EXIT_CRITICAL();
}

使用特权

评论回复
東南博士|  楼主 | 2020-2-11 10:55 | 显示全部楼层
////////////////////////////////////////////////////////////////////////////////
//函数名称:MB_DMAchannelInit
//功能描述:初始化DMA接收通道
//入口参数:无
//出口参数:无
////////////////////////////////////////////////////////////////////////////////
void  MB_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);  

}

使用特权

评论回复
東南博士|  楼主 | 2020-2-11 10:56 | 显示全部楼层
porttimer.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: 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);

使用特权

评论回复
東南博士|  楼主 | 2020-2-11 10:56 | 显示全部楼层
/* ----------------------- Start implementation -----------------------------*/
BOOL xMBPortTimersInit(USHORT usTim1Timerout50us)
{

        uint16_t PrescalerValue = 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 )
       
}

使用特权

评论回复
東南博士|  楼主 | 2020-2-11 10:57 | 显示全部楼层

void vMBPortTimersEnable()
{
        TIM_ClearITPendingBit(TIM3, TIM_IT_Update);
        TIM_ITConfig(TIM3, TIM_IT_Update, ENABLE);
        TIM_SetCounter(TIM3, 0);
        TIM_Cmd(TIM3, ENABLE);
}

使用特权

评论回复
東南博士|  楼主 | 2020-2-11 10:57 | 显示全部楼层
void vMBPortTimersDisable()
{
        TIM_ClearITPendingBit(TIM3, TIM_IT_Update);
        TIM_ITConfig(TIM3, TIM_IT_Update, DISABLE);
        TIM_SetCounter(TIM3, 0);
        TIM_Cmd(TIM3, DISABLE);
}

使用特权

评论回复
東南博士|  楼主 | 2020-2-11 10:57 | 显示全部楼层
void prvvTIMERExpiredISR(void)
{
        (void) pxMBPortCBTimerExpired();
}

使用特权

评论回复
東南博士|  楼主 | 2020-2-11 10:57 | 显示全部楼层
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(        );
}

使用特权

评论回复
東南博士|  楼主 | 2020-2-11 10:58 | 显示全部楼层
mbrtu.c:实现数据的接收解析和数据发送

使用特权

评论回复
東南博士|  楼主 | 2020-2-11 10:58 | 显示全部楼层
/*
* FreeModbus Libary: A portable Modbus implementation for Modbus ASCII/RTU.
* Copyright (c) 2006-2018 Christian Walter <cwalter@embedded-solutions.at>
* 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_MIN     4       /*!< 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_CRC     2       /*!< Size of CRC field in PDU. */
#define MB_SER_PDU_ADDR_OFF     0       /*!< Offset of slave address in Ser-PDU. */
#define MB_SER_PDU_PDU_OFF      1       /*!< 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 UCHAR  ucRTUBuf[MB_SER_PDU_SIZE_MAX];

static volatile UCHAR *pucSndBufferCur;
static volatile USHORT usSndBufferCount;

static volatile USHORT usRcvBufferPos;



/*--------------------------- Golobal functions ------------------------------*/
void  MB_DMAchannelInit (void);

使用特权

评论回复
東南博士|  楼主 | 2020-2-11 10:58 | 显示全部楼层
/* ----------------------- Start implementation -----------------------------*/
eMBErrorCode
eMBRTUInit( UCHAR ucSlaveAddress, UCHAR ucPort, ULONG ulBaudRate, eMBParity eParity )
{
    eMBErrorCode    eStatus = MB_ENOERR;
    ULONG           usTimerT35_50us;
        OS_CPU_SR                  cpu_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;
}

使用特权

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

本版积分规则

367

主题

6048

帖子

34

粉丝