/* 当前使用 USART1:TX=PA9,RX=PA10 */
#define sys_usart              USART1
#define sys_usart_IRQn         USART1_IRQn

/* STM32F10x 固定映射:USART1_TX -> DMA1_Channel4,USART1_RX -> DMA1_Channel5 */
#define sys_usart_dma_tx       DMA1_Channel4
#define sys_usart_dma_rx       DMA1_Channel5

#define sys_TxGpio             GPIOA
#define sys_TxPin              GPIO_Pin_9
#define sys_RxGpio             GPIOA
#define sys_RxPin              GPIO_Pin_10
#define sys_usartClock()       (RCC->APB2ENR |= RCC_APB2ENR_USART1EN)
#define sys_dmaClock()         (RCC->AHBENR |= RCC_AHBENR_DMA1EN)

/* DMA 接收缓冲区大小;一次接收帧不能超过该长度 */
#define SYS_USART_RX_BUF_SIZE  128u

/* DMA 发送等待超时时间基数,按发送长度放大 */
#define SYS_USART_TX_TIMEOUT   0xFFFFu

/* 串口接收状态结构体 */
typedef struct {
    uint8_t rxBuf[SYS_USART_RX_BUF_SIZE];  /* DMA 接收缓冲区 */
    volatile uint8_t rxFrame;              /* 接收到完整一帧标志 */
    volatile uint16_t rxLen;               /* 当前帧有效数据长度 */
} SYS_USART_DATA;

static SYS_USART_DATA sys_usart_data = {0};
static sys_usart_rx_callback_t sys_usart_rx_callback = 0;
/* 重新启动 DMA 接收,准备接收下一帧数据 */
static void sys_usartDmaRxRestart(void)
{
    DMA_Cmd(sys_usart_dma_rx, DISABLE);
    DMA_SetCurrDataCounter(sys_usart_dma_rx, SYS_USART_RX_BUF_SIZE);
    DMA_Cmd(sys_usart_dma_rx, ENABLE);
}
/*
 * 串口初始化
 * bount:波特率,例如 115200
 */
void sys_usartInit(uint32_t bount)
{
    USART_InitTypeDef USART_InitStructure;
    NVIC_InitTypeDef NVIC_InitStructure;
    GPIO_InitTypeDef GPIO_InitStructure;
    DMA_InitTypeDef DMA_InitStructure;

    sys_usartClock();
    sys_dmaClock();

    /* PA9 配置为 USART1_TX,复用推挽输出 */
    GPIO_InitStructure.GPIO_Pin = sys_TxPin;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
    GPIO_Init(sys_TxGpio, &GPIO_InitStructure);

    /* PA10 配置为 USART1_RX,浮空输入 */
    GPIO_InitStructure.GPIO_Pin = sys_RxPin;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
    GPIO_Init(sys_RxGpio, &GPIO_InitStructure);

    /* 串口参数:8 位数据位、1 位停止位、无校验、无硬件流控 */
    USART_InitStructure.USART_BaudRate = bount;
    USART_InitStructure.USART_WordLength = USART_WordLength_8b;
    USART_InitStructure.USART_StopBits = USART_StopBits_1;
    USART_InitStructure.USART_Parity = USART_Parity_No;
    USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
    USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
    USART_Init(sys_usart, &USART_InitStructure);

    /* 配置 RX DMA:外设 USART_DR -> 内存 rxBuf */
    DMA_DeInit(sys_usart_dma_rx);
    DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&sys_usart->DR;
    DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)sys_usart_data.rxBuf;
    DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;
    DMA_InitStructure.DMA_BufferSize = SYS_USART_RX_BUF_SIZE;
    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_High;
    DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;
    DMA_Init(sys_usart_dma_rx, &DMA_InitStructure);

    /* 配置 TX DMA:内存发送缓冲区 -> 外设 USART_DR,每次发送前重新设置地址和长度 */
    DMA_DeInit(sys_usart_dma_tx);
    DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&sys_usart->DR;
    DMA_InitStructure.DMA_MemoryBaseAddr = 0;
    DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST;
    DMA_InitStructure.DMA_BufferSize = 1;
    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_Medium;
    DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;
    DMA_Init(sys_usart_dma_tx, &DMA_InitStructure);

    /* 配置 USART1 空闲中断,用于判断一帧数据接收完成 */
    NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1);
    NVIC_InitStructure.NVIC_IRQChannel = sys_usart_IRQn;
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
    NVIC_Init(&NVIC_InitStructure);

    USART_ITConfig(sys_usart, USART_IT_IDLE, ENABLE);
    USART_DMACmd(sys_usart, USART_DMAReq_Rx, ENABLE);
    USART_DMACmd(sys_usart, USART_DMAReq_Tx, ENABLE);
    USART_Cmd(sys_usart, ENABLE);

    /* 开启第一次 DMA 接收 */
    sys_usartDmaRxRestart();
}
/*
 * 使用 DMA 发送一段数据
 * data:待发送数据指针
 * len :待发送数据长度
 */
void sys_usartSendData(uint8_t *data, uint16_t len)
{
    uint32_t timeout;

    if ((data == 0) || (len == 0)){
        return;
    }

    /* 每次发送前重新配置 TX DMA 的内存地址和发送长度 */
    DMA_Cmd(sys_usart_dma_tx, DISABLE);
    sys_usart_dma_tx->CMAR = (uint32_t)data;
    DMA_SetCurrDataCounter(sys_usart_dma_tx, len);
    DMA_ClearFlag(DMA1_FLAG_GL4 | DMA1_FLAG_TC4 | DMA1_FLAG_TE4);
    DMA_Cmd(sys_usart_dma_tx, ENABLE);

    /* 等待 DMA 发送完成,加入超时避免异常时死等 */
    timeout = SYS_USART_TX_TIMEOUT * len;
    while ((DMA_GetFlagStatus(DMA1_FLAG_TC4) == RESET) && (timeout != 0)) {
        timeout--;
    }

    DMA_Cmd(sys_usart_dma_tx, DISABLE);
    DMA_ClearFlag(DMA1_FLAG_TC4);
}
/*
 * 获取接收到的一帧数据
 * len:用于返回接收长度,可传入 0
 * 返回值:有新数据时返回接收缓冲区指针,没有新数据时返回 0
 * 注意:该缓冲区会被下一帧 DMA 接收复用,外部需要及时处理或拷贝
 */
uint8_t *sys_usartGetRxData(uint16_t *len)
{
    if (sys_usart_data.rxFrame == 0) {
        return 0;
    }

    sys_usart_data.rxFrame = 0;
    if (len != 0) {
        *len = sys_usart_data.rxLen;
    }

    return sys_usart_data.rxBuf;
}
/* 注册接收回调函数,收到一帧数据后会在 USART1 中断中调用 */
void sys_usartRegisterRxCallback(sys_usart_rx_callback_t callback)
{
    sys_usart_rx_callback = callback;
}

/*
 * 弱定义接收处理函数
 * 外部可以在其他 C 文件中重新定义同名函数,实现自己的接收处理逻辑
 */
__weak void sys_usartRxDataHandle(uint8_t *data, uint16_t len)
{
    (void)data;
    (void)len;
}
/* USART1 中断服务函数:使用空闲中断判断 DMA 接收一帧结束 */
void USART1_IRQHandler(void)
{
    volatile uint32_t tmp;
    uint16_t len;

    if (USART_GetITStatus(sys_usart, USART_IT_IDLE) != RESET) {
        /* 清除 IDLE 标志:必须先读 SR,再读 DR */
        tmp = sys_usart->SR;
        tmp = sys_usart->DR;
        (void)tmp;

        /* 停止 DMA 后读取剩余计数,计算本帧长度 */
        DMA_Cmd(sys_usart_dma_rx, DISABLE);
        len = SYS_USART_RX_BUF_SIZE - DMA_GetCurrDataCounter(sys_usart_dma_rx);

        if (len > 0) {
            sys_usart_data.rxLen = len;
            sys_usart_data.rxFrame = 1;

            /* 预留外部处理入口:弱函数和注册回调二选一或同时使用 */
            sys_usartRxDataHandle(sys_usart_data.rxBuf, len);
            if (sys_usart_rx_callback != 0) {
                sys_usart_rx_callback(sys_usart_data.rxBuf, len);
            }
        }

        /* 重新开启 DMA,接收下一帧 */
        sys_usartDmaRxRestart();
    }
}
uint8_t *sys_rx = NULL;//用于串口接收
uint16_t sys_rtlen = 0;//接收数据长度
//main.c循环中调用
sys_rx = sys_usartGetRxData(&sys_rtlen);//接收到的数据
if((sys_rx != NULL) && (sys_rtlen > 0))
{
	sys_usartSendData(sys_rx, sys_rtlen);//发送接收到的数据
}

Logo

免费领 150 小时云算力,进群参与显卡、AI PC 幸运抽奖

更多推荐