在这里插入图片描述

一、写在前面

1.1 文章回顾

该文章对第二篇、使用DMA优化FreeModbus从机数据接收(无OS,裸机移植)的所提出的代码弊端进行优化实现

1.2 FreeModbus从机 数据发送优化版例程下载

移植例程下载戳我

   1. 芯片:STM32F103C8T6
   2. 串口引脚:USART1_TX(PB6)USART1_RX(PB7)
   3. RS485控制引脚:DIR1(PB8)
   4. 采用DMA+接收空闲中断+发送完成中断的串口数据的接收(中断量最少、效率高) 
   5. 使用STM32CubeMX生成的Hal库程序
   6. 未使用RTOS

二、生成CubeMX工程

使用【第二篇、使用DMA优化FreeModbus从机数据接收(无OS,裸机移植)】工程基础上,进行以下改动

1、使能DMA

在这里插入图片描述

三、FreeModbus文件修改

1、portserial.c

1、修改vMBPortSerialEnable函数实现
2、新增函数xMBPortSerialPutFrame,帧发送函数,替代原字符发送函数
3、新增DMA1_Channel4_IRQHandler和HAL_UART_TxCpltCallback函数,处理帧发送结束

/*
 * 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$
 */

#include "port.h"

/* ----------------------- Modbus includes ----------------------------------*/
#include "mb.h"
#include "mbport.h"

/* ----------------------- serial port --------------------------------------*/
extern UART_HandleTypeDef huart1;
extern DMA_HandleTypeDef hdma_usart1_rx; // DMA 句柄
extern DMA_HandleTypeDef hdma_usart1_tx; // 发送 DMA 句柄

#define SLAVE_PORT huart1
#define rs485_dir_recv()  HAL_GPIO_WritePin(DIR1_GPIO_Port,DIR1_Pin,GPIO_PIN_RESET)
#define rs485_dir_send() HAL_GPIO_WritePin(DIR1_GPIO_Port,DIR1_Pin,GPIO_PIN_SET)

volatile uint8_t ucLocalSlaveAddress = 1; //本机modbus从地址
#define MB_SER_PDU_SIZE_MAX     256     /*!< Modbus RTU 帧的最大尺寸 */
extern volatile UCHAR  ucRTUBuf[MB_SER_PDU_SIZE_MAX]; // 用于存储接收的 Modbus RTU 数据的缓冲区
extern volatile USHORT usRcvBufferPos; // 接收缓冲区中已存储数据的位置
extern volatile USHORT usSndBufferCount;
/* ----------------------- static functions ---------------------------------*/
static void prvvUARTTxReadyISR( void );

/* ----------------------- Start implementation -----------------------------*/
void
vMBPortSerialEnable( BOOL xRxEnable, BOOL xTxEnable )
{
    /* If xRXEnable enable serial receive interrupts. If xTxENable enable
     * transmitter empty interrupts.
     */
    if (xRxEnable)
    {
        rs485_dir_recv();
        if (HAL_UART_Receive_DMA(&SLAVE_PORT, (uint8_t *)ucRTUBuf, MB_SER_PDU_SIZE_MAX) != HAL_OK)
        {
            // 处理错误
        }
        __HAL_UART_ENABLE_IT(&SLAVE_PORT, UART_IT_IDLE); // 使能空闲中断
    }
    else
    {
        rs485_dir_send();
        HAL_UART_DMAStop(&SLAVE_PORT); // 停止 DMA 接收
        __HAL_UART_DISABLE_IT(&SLAVE_PORT, UART_IT_IDLE); // 禁止空闲中断
    }
    
    if (xTxEnable)
    {        
        __HAL_UART_ENABLE_IT(&SLAVE_PORT, UART_IT_TC);    // 使能帧发送完成中断 
    }
    else
    {
        __HAL_UART_DISABLE_IT(&SLAVE_PORT, UART_IT_TC);    // 禁止帧发送完成中断 
    }
}

BOOL
xMBPortSerialInit( UCHAR ucPORT, ULONG ulBaudRate, UCHAR ucDataBits, eMBParity eParity )
{
    SLAVE_PORT.Instance = USART1;
    SLAVE_PORT.Init.BaudRate = ulBaudRate;
    SLAVE_PORT.Init.StopBits = UART_STOPBITS_1;
    SLAVE_PORT.Init.Mode = UART_MODE_TX_RX;
    SLAVE_PORT.Init.HwFlowCtl = UART_HWCONTROL_NONE;
    SLAVE_PORT.Init.OverSampling = UART_OVERSAMPLING_16;

    switch(eParity)
    {
    // 奇校验
    case MB_PAR_ODD:
        SLAVE_PORT.Init.Parity = UART_PARITY_ODD;
        SLAVE_PORT.Init.WordLength = UART_WORDLENGTH_9B;            // 带奇偶校验数据位为9bits
        break;

    // 偶校验
    case MB_PAR_EVEN:
        SLAVE_PORT.Init.Parity = UART_PARITY_EVEN;
        SLAVE_PORT.Init.WordLength = UART_WORDLENGTH_9B;            // 带奇偶校验数据位为9bits
        break;

    // 无校验
    default:
        SLAVE_PORT.Init.Parity = UART_PARITY_NONE;
        SLAVE_PORT.Init.WordLength = UART_WORDLENGTH_8B;            // 无奇偶校验数据位为8bits
        break;
    }

    // 初始化 DMA 接收
    SLAVE_PORT.hdmarx = &hdma_usart1_rx;
    hdma_usart1_rx.Instance = DMA1_Channel5; // 根据实际情况修改
    hdma_usart1_rx.Init.Direction = DMA_PERIPH_TO_MEMORY;
    hdma_usart1_rx.Init.PeriphInc = DMA_PINC_DISABLE;
    hdma_usart1_rx.Init.MemInc = DMA_MINC_ENABLE;
    hdma_usart1_rx.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE;
    hdma_usart1_rx.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE;
    hdma_usart1_rx.Init.Mode = DMA_NORMAL;
    hdma_usart1_rx.Init.Priority = DMA_PRIORITY_LOW;
    if (HAL_DMA_Init(&hdma_usart1_rx) != HAL_OK)
    {
        return FALSE;
    }
    __HAL_LINKDMA(&SLAVE_PORT, hdmarx, hdma_usart1_rx);
    
    //初始化 DMA 发送
    SLAVE_PORT.hdmatx = &hdma_usart1_tx;
    hdma_usart1_tx.Instance = DMA1_Channel4; // 根据实际情况修改
    hdma_usart1_tx.Init.Direction = DMA_MEMORY_TO_PERIPH;
    hdma_usart1_tx.Init.PeriphInc = DMA_PINC_DISABLE;
    hdma_usart1_tx.Init.MemInc = DMA_MINC_ENABLE;
    hdma_usart1_tx.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE;
    hdma_usart1_tx.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE;
    hdma_usart1_tx.Init.Mode = DMA_CIRCULAR;
    hdma_usart1_tx.Init.Priority = DMA_PRIORITY_LOW;
    if (HAL_DMA_Init(&hdma_usart1_tx) != HAL_OK)
    {
        return FALSE;
    }
    __HAL_LINKDMA(&SLAVE_PORT, hdmatx, hdma_usart1_tx);
    
    return HAL_UART_Init(&SLAVE_PORT) == HAL_OK ? TRUE : FALSE;
}

BOOL 
xMBPortSerialRestart(void)
{
    HAL_UART_DMAStop(&SLAVE_PORT);
    vMBPortSerialEnable(TRUE, FALSE);
  return  TRUE;
}


BOOL xMBPortSerialPutFrame(UCHAR *pucFrame, USHORT usLength)
{
    // 1. 参数合法性检查(避免缓冲区溢出或无效长度)
    if (pucFrame == NULL || usLength == 0 || usLength > MB_SER_PDU_SIZE_MAX)
    {
        return FALSE;
    }

    // 2. 检查串口状态:确保当前未处于接收/发送中(避免冲突)
    if (HAL_UART_GetState(&SLAVE_PORT) != HAL_UART_STATE_READY)
    {
        return FALSE;
    }
    
    // 3. 重置DMA通道(关键修复点)
    if (HAL_UART_DMAStop(&SLAVE_PORT) != HAL_OK)
        return FALSE;
    
    // 4. 启动 DMA 批量发送
    if (HAL_UART_Transmit_DMA(&SLAVE_PORT, (uint8_t *)pucFrame, usLength) != HAL_OK)
    {
        // DMA 发送启动失败,重置状态
        return FALSE;
    }

    return TRUE;
}

BOOL
xMBPortSerialPutByte( CHAR ucByte )
{
    /* Put a byte in the UARTs transmit buffer. This function is called
     * by the protocol stack if pxMBFrameCBTransmitterEmpty( ) has been
     * called. */
    USART1->DR = ucByte;
    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.
 */
static void prvvUARTTxReadyISR( void )
{
    pxMBFrameCBTransmitterEmpty(  );
}

/*
 * Add your serial port interrupt handler
 *
 */
// 定义结构体来记录不同类型中断的次数
typedef struct {
    uint32_t tc_irq_count;    //发送完成中断
    uint32_t idle_irq_count;  // 空闲中断次数
    uint32_t txe_irq_count;   // 发送空中断次数
    uint32_t rxne_irq_count;  // 接收缓冲区非空中断次数
    uint32_t error_irq_count; // 错误中断次数
    uint32_t dma_irq_count;
} UART1_IRQ_Counters;

// 全局变量,用于存储中断计数
UART1_IRQ_Counters uart1_irq_counters = {0, 0, 0, 0};

// 原有的中断处理函数,添加中断计数逻辑
void USART1_IRQHandler(void)
{
    if (__HAL_UART_GET_FLAG(&SLAVE_PORT, UART_FLAG_IDLE)) // 空闲中断标记被置位
    {
        uart1_irq_counters.idle_irq_count++;                                           // 空闲中断次数加 1
        __HAL_UART_CLEAR_IDLEFLAG(&SLAVE_PORT);                                        // 清除空闲中断标记
        usRcvBufferPos = MB_SER_PDU_SIZE_MAX - __HAL_DMA_GET_COUNTER(&hdma_usart1_rx); // 计算接收到的数据长度

        // 将接收到的从机地址存储到指定位置
        if (ucRTUBuf[0] == ucLocalSlaveAddress || ucRTUBuf[0] == MB_ADDRESS_BROADCAST)
        {
            xMBPortEventPost(EV_FRAME_RECEIVED); // 通知协议栈有新帧接收
        }
        else
        {
            // 停止 DMA 接收
            HAL_UART_DMAStop(&SLAVE_PORT);
            vMBPortSerialEnable(TRUE, FALSE);
        }
    }
}

void HAL_UART_TxCpltCallback(UART_HandleTypeDef *huart)
{
    if (huart == &SLAVE_PORT)
    {
        HAL_UART_DMAStop(&SLAVE_PORT);
      
        while (__HAL_UART_GET_FLAG(&SLAVE_PORT, UART_FLAG_TC) == RESET);
        
        __HAL_UART_CLEAR_FLAG(&SLAVE_PORT, UART_FLAG_TC);
        
        rs485_dir_recv();
        
        __HAL_UART_DISABLE_IT(&SLAVE_PORT, UART_IT_TC);
        
        usSndBufferCount = 0;
        pxMBFrameCBTransmitterEmpty();
    }
}

void DMA1_Channel4_IRQHandler()
{
  HAL_DMA_IRQHandler(&hdma_usart1_tx);
  uart1_irq_counters.dma_irq_count ++;
}

2、mbrtu.c

1、修改xMBRTUTransmitFSM函数,增加状态STATE_TX_WAIT,等待DMA发送结束,该函数被中断回调函数调用,以及触发发送器时调用。
2、修改eMBRTUSend函数,直接调用状态机进行发送数据
3、pucSndBufferCur不再使用volatile修饰,因为DMA不接受这种类型的内存

/* 
 * 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.
 *
 */

/* ----------------------- 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"

/* ----------------------- Defines ------------------------------------------*/
#define MB_SER_PDU_SIZE_MIN     4       /*!< Modbus RTU 帧的最小尺寸 */
#define MB_SER_PDU_SIZE_MAX     256     /*!< Modbus RTU 帧的最大尺寸 */
#define MB_SER_PDU_SIZE_CRC     2       /*!< PDU 中 CRC 字段的大小 */
#define MB_SER_PDU_ADDR_OFF     0       /*!< 从机地址在串行 PDU 中的偏移量 */
#define MB_SER_PDU_PDU_OFF      1       /*!< Modbus-PDU 在串行 PDU 中的偏移量 */

/* ----------------------- Type definitions ---------------------------------*/
typedef enum
{
    STATE_RX_INIT,              /*!< 接收器处于初始状态 */
    STATE_RX_IDLE,              /*!< 接收器处于空闲状态 */
    STATE_RX_RCV,               /*!< 正在接收帧 */
    STATE_RX_ERROR              /*!< 如果帧无效 */
} eMBRcvState;

typedef enum
{
    STATE_TX_IDLE,              /*!< 发送器处于空闲状态 */
    STATE_TX_XMIT,              /*!< 发送器处于传输状态 */
    STATE_TX_WAIT               /*!< 发送器等待DMA发送结束*/
} eMBSndState;

/* ----------------------- Static variables ---------------------------------*/
volatile eMBSndState eSndState; // 发送器状态,volatile 确保在多线程或中断环境下能正确访问
static volatile eMBRcvState eRcvState; // 接收器状态,volatile 确保在多线程或中断环境下能正确访问

volatile UCHAR  ucRTUBuf[MB_SER_PDU_SIZE_MAX]; // 用于存储接收的 Modbus RTU 数据的缓冲区

static UCHAR *pucSndBufferCur; // 指向发送缓冲区当前位置的指针
volatile USHORT usSndBufferCount; // 发送缓冲区中剩余要发送的字节数

volatile USHORT usRcvBufferPos; // 接收缓冲区中已存储数据的位置

/* ----------------------- Start implementation -----------------------------*/
eMBErrorCode
eMBRTUInit( UCHAR ucSlaveAddress, UCHAR ucPort, ULONG ulBaudRate, eMBParity eParity )
{
    eMBErrorCode    eStatus = MB_ENOERR;

    ( void )ucSlaveAddress;
    ENTER_CRITICAL_SECTION(  );

    /* Modbus RTU uses 8 Databits. */
    if( xMBPortSerialInit( ucPort, ulBaudRate, 8, eParity ) != TRUE )
    {
        eStatus = MB_EPORTERR;
    }
    else
    {

    }
    EXIT_CRITICAL_SECTION(  );

    return eStatus;
}

void
eMBRTUStart( void )
{
    ENTER_CRITICAL_SECTION(  ); // 进入临界区
    eRcvState = STATE_RX_IDLE; // 设置接收器状态为初始状态
    vMBPortSerialEnable( TRUE, FALSE ); // 使能串口接收,禁用串口发送
    EXIT_CRITICAL_SECTION(  ); // 退出临界区
}

void
eMBRTUStop( void )
{
    ENTER_CRITICAL_SECTION(  ); // 进入临界区
    vMBPortSerialEnable( FALSE, FALSE ); // 禁用串口接收和发送
    EXIT_CRITICAL_SECTION(  ); // 退出临界区
}

eMBErrorCode
eMBRTUReceive( UCHAR * pucRcvAddress, UCHAR ** pucFrame, USHORT * pusLength )
{
    BOOL            xFrameReceived = FALSE; // 标记是否接收到有效帧
    eMBErrorCode    eStatus = MB_ENOERR; // 错误状态码,初始设为无错误

    ENTER_CRITICAL_SECTION(  ); // 进入临界区
    assert( usRcvBufferPos < MB_SER_PDU_SIZE_MAX ); // 断言确保接收缓冲区位置在有效范围内

    /* 长度和 CRC 检查 */
    if( ( usRcvBufferPos >= MB_SER_PDU_SIZE_MIN )
        && ( usMBCRC16( ( UCHAR * ) ucRTUBuf, usRcvBufferPos ) == 0 ) )
    {
        /* 保存地址字段。所有帧都传递到上层,
         * 是否使用该帧的决策在那里进行。
         */
        *pucRcvAddress = ucRTUBuf[MB_SER_PDU_ADDR_OFF]; // 将接收到的从机地址存储到指定位置

        /* Modbus-PDU 的总长度是 Modbus 串行线路 PDU 减去
         * 地址字段大小和 CRC 校验和。
         */
        *pusLength = ( USHORT )( usRcvBufferPos - MB_SER_PDU_PDU_OFF - MB_SER_PDU_SIZE_CRC ); // 计算有效数据长度

        /* 将 Modbus PDU 的起始位置返回给调用者。 */
        *pucFrame = ( UCHAR * ) & ucRTUBuf[MB_SER_PDU_PDU_OFF]; // 设置指向有效数据的指针
        xFrameReceived = TRUE; // 标记接收到有效帧
    }
    else
    {
        eStatus = MB_EIO; // 如果帧无效,设置错误状态为输入输出错误
    }

    EXIT_CRITICAL_SECTION(  ); // 退出临界区
    return eStatus; // 返回错误状态码
}

eMBErrorCode
eMBRTUSend( UCHAR ucSlaveAddress, const UCHAR * pucFrame, USHORT usLength )
{
    eMBErrorCode    eStatus = MB_ENOERR; // 错误状态码,初始设为无错误
    USHORT          usCRC16; // 用于存储 CRC16 校验和

    ENTER_CRITICAL_SECTION(  ); // 进入临界区

    /* 检查接收器是否仍处于空闲状态。如果不是,说明我们处理接收到的帧太慢,
     * 并且主机在网络上发送了另一个帧。我们必须中止发送该帧。
     */
    if( eRcvState == STATE_RX_IDLE )
    {
        /* Modbus-PDU 之前的第一个字节是从机地址。 */
        pucSndBufferCur = ( UCHAR * ) pucFrame - 1; // 设置发送缓冲区指针指向从机地址位置
        usSndBufferCount = 1; // 初始化发送缓冲区计数为 1(从机地址占 1 字节)

        /* 现在将 Modbus-PDU 复制到 Modbus 串行线路 PDU 中。 */
        pucSndBufferCur[MB_SER_PDU_ADDR_OFF] = ucSlaveAddress; // 将从机地址存储到发送缓冲区
        usSndBufferCount += usLength; // 更新发送缓冲区计数为从机地址和有效数据长度之和

        /* 计算 Modbus 串行线路 PDU 的 CRC16 校验和。 */
        usCRC16 = usMBCRC16( ( UCHAR * ) pucSndBufferCur, usSndBufferCount ); // 计算 CRC16 校验和
        ucRTUBuf[usSndBufferCount++] = ( UCHAR )( usCRC16 & 0xFF ); // 将 CRC16 低字节存储到缓冲区
        ucRTUBuf[usSndBufferCount++] = ( UCHAR )( usCRC16 >> 8 ); // 将 CRC16 高字节存储到缓冲区

        /* 激活发送器。 */
        eSndState = STATE_TX_XMIT; // 设置发送器状态为传输状态

        xMBRTUTransmitFSM();
    }
    else
    {
        eStatus = MB_EIO; // 如果接收器不处于空闲状态,设置错误状态为输入输出错误
    }
    EXIT_CRITICAL_SECTION(  ); // 退出临界区
    return eStatus; // 返回错误状态码
}

BOOL
xMBRTUReceiveFSM( void )
{
    BOOL            xTaskNeedSwitch = FALSE; // 标记是否需要任务切换
    assert( eSndState == STATE_TX_IDLE ); // 断言确保发送器处于空闲状态

    switch ( eRcvState )
    {
    case STATE_RX_INIT:
         eRcvState = STATE_RX_IDLE; // 直接切换到空闲状态
        break;

        /* 在错误状态下,我们等待直到损坏的帧中的所有字符都被传输。 */
    case STATE_RX_ERROR:
        eRcvState = STATE_RX_IDLE; // 直接切换到空闲状态
        break;

    case STATE_RX_IDLE:
       // 无需处理,等待空闲中断触发接收完成事件
        break;

    case STATE_RX_RCV:
        // 无需处理,等待空闲中断触发接收完成事件
        break;
    }
    return xTaskNeedSwitch; // 返回是否需要任务切换的标记
}

BOOL
xMBRTUTransmitFSM( void )
{
    BOOL            xNeedPoll = FALSE; // 标记是否需要轮询
    static uint32_t ulTxTimeout = 0;
    assert( eRcvState == STATE_RX_IDLE ); // 断言确保接收器处于空闲状态

    switch ( eSndState )
    {
        /* 如果发送器处于空闲状态,我们不应该收到发送器事件。  */
    case STATE_TX_IDLE:
        /* 启用接收器/禁用发送器。 */
        vMBPortSerialEnable( TRUE, FALSE ); // 使能串口接收,禁用串口发送
        break;

    case STATE_TX_XMIT:
        // 启动 DMA 批量发送
        vMBPortSerialEnable( FALSE, TRUE ); // 禁用串口接收,使能串口发送
        if (xMBPortSerialPutFrame(pucSndBufferCur, usSndBufferCount) == TRUE)
        {
            eSndState = STATE_TX_WAIT; // 切换到等待发送完成状态
        }
        else
        {
            // 发送启动失败,恢复空闲状态
            vMBPortSerialEnable(TRUE, FALSE);
            eSndState = STATE_TX_IDLE;
        }
        break;
    case STATE_TX_WAIT:
          if(usSndBufferCount == 0)
          {
            xNeedPoll = xMBPortEventPost( EV_FRAME_SENT ); // 发送帧发送完成事件
            vMBPortSerialEnable( TRUE, FALSE ); // 使能串口接收,禁用串口发送
            eSndState = STATE_TX_IDLE; // 设置发送器状态为空闲状态
            ulTxTimeout = 0;
          }else if(ulTxTimeout++ > 100000) // 约100ms超时(根据系统时钟调整)
          {
              // 超时重置
              xMBPortSerialRestart();
              eSndState = STATE_TX_IDLE;
              ulTxTimeout = 0;
          }
          break;
    }

    return xNeedPoll; // 返回是否需要轮询的标记
}

四、测试验证

uart1_irq_counters结构体记录了系统进入中断的次数,可以看到每发送一帧数据,系统进入两次串口空闲中断,大大减少了中断次数。
请添加图片描述

Logo

智能硬件社区聚焦AI智能硬件技术生态,汇聚嵌入式AI、物联网硬件开发者,打造交流分享平台,同步全国赛事资讯、开展 OPC 核心人才招募,助力技术落地与开发者成长。

更多推荐