【stm32HAL库】uart dma收发驱动(含实例)

概要

本文以STM32F1xx_HAL_Driver驱动库做讲解,实验以stm32f103c8芯片做示例,工程采用makefile进行编译。

常用结构体

UART_HandleTypeDef

typedef struct __UART_HandleTypeDef
{
    USART_TypeDef *Instance; // 指向串口寄存器基地址
    UART_InitTypeDef Init; // 串口通信相关的基本配置
    uint8_t *pTxBuffPtr; // 指向串口发送缓冲区
    uint16_t TxXferSize; // 串口发送缓冲区大小
    __IO uint16_t TxXferCount; // 串口剩余需要发送的数目
    uint8_t *pRxBuffPtr; // 指向串口接收缓冲区
    uint16_t RxXferSize; // 串口接收缓冲区大小
    __IO uint16_t RxXferCount; // 串口剩余需要接收的数目
    __IO HAL_UART_RxTypeTypeDef ReceptionType; // 接收类型
    DMA_HandleTypeDef *hdmatx; // 指向串口发送DMA句柄
    DMA_HandleTypeDef *hdmarx; // 指向串口接收DMA句柄
    HAL_LockTypeDef Lock; // 锁对象
    __IO HAL_UART_StateTypeDef gState; // 串口全局句柄管理、串口发送的状态
    __IO HAL_UART_StateTypeDef RxState; // 串口接收的状态
    __IO uint32_t ErrorCode; // 错误码

#if (USE_HAL_UART_REGISTER_CALLBACKS == 1)
    void (*TxHalfCpltCallback)(struct __UART_HandleTypeDef *huart); // 串口发送过半回调函数
    void (*TxCpltCallback)(struct __UART_HandleTypeDef *huart); // 串口发送完成回调函数
    void (*RxHalfCpltCallback)(struct __UART_HandleTypeDef *huart); // 串口接收过半回调函数
    void (*RxCpltCallback)(struct __UART_HandleTypeDef *huart); // 串口接收完成回调函数
    void (*ErrorCallback)(struct __UART_HandleTypeDef *huart); // 串口错误回调函数
    void (*AbortCpltCallback)(struct __UART_HandleTypeDef *huart); // 串口中止完成回调函数
    void (*AbortTransmitCpltCallback)(struct __UART_HandleTypeDef *huart); // 串口中止发送完成回调函数
    void (*AbortReceiveCpltCallback)(struct __UART_HandleTypeDef *huart); // 串口中止接收完成回调函数
    void (*WakeupCallback)(struct __UART_HandleTypeDef *huart); // 串口唤醒回调函数
    void (*RxEventCallback)(struct __UART_HandleTypeDef *huart, uint16_t Pos); // 串口接收时间回调函数
    void (*MspInitCallback)(struct __UART_HandleTypeDef *huart); // 串口初始化回调函数
    void (*MspDeInitCallback)(struct __UART_HandleTypeDef *huart); // 串口释放回调函数
#endif
} UART_HandleTypeDef;

UART_InitTypeDef

typedef struct
{
    uint32_t BaudRate; // 波特率
    uint32_t WordLength; // 数据位比特数
    uint32_t StopBits; // 停止位
    uint32_t Parity; // 奇偶校验模式
    uint32_t Mode; // 串口模式
    uint32_t HwFlowCtl; // 硬件流控
    uint32_t OverSampling; // 过采样
} UART_InitTypeDef;

STM32CubeMX工程模板

下面使用STM32CubeMX进行工程模板搭建,关键的配置信息如下图。
1、打开串口DMA发送接收中断。
【stm32HAL库】uart dma收发驱动(含实例)_第1张图片
2、打开串口全局中断。
【stm32HAL库】uart dma收发驱动(含实例)_第2张图片

关键代码讲解

在main函数中,我们使能jlink swd模式。先进入UART_TestCase串口用例,然后在while(1) --> UART_TestCaseTask串口用例Task进行应用的处理。

main函数

__IO ITStatus g_uartTxReady = RESET;
__IO ITStatus g_uartRxReady = RESET;
#define UART_TEST_BUFF_SIZE 256
uint8_t aTxBuffer[UART_TEST_BUFF_SIZE] = "---------- This is usart dma test demo ----------\n";
uint8_t aRxBuffer[UART_TEST_BUFF_SIZE] = {0};
uint8_t tempBuffer[UART_TEST_BUFF_SIZE] = {0};

int main(void)
{
    HAL_Init();
    SystemClock_Config();
    MX_GPIO_Init();
    MX_DMA_Init();
    MX_USART1_UART_Init();

    __HAL_RCC_GPIOA_CLK_ENABLE();
    __HAL_AFIO_REMAP_SWJ_ENABLE();

    UART_TestCase();

    while (1)
    {
        UART_TestCaseTask();
        HAL_Delay(200);
    }
}

printf实现

在printf中,我们并没有采用DMA进行传输。DMA一般用在数据量大的场景,所以单独在UART_TestCase测试用例进行实现。

__attribute__((used)) int _write(int fd, char *ptr, int len)
{
    (void)HAL_UART_Transmit(&huart1, (uint8_t *)ptr, len, 0xFFFF);

    return len;
}

串口相关中断函数

HAL_UART_TxCpltCallback是串口发送完成中断回调函数,HAL_UARTEx_RxEventCallback是串口空闲中断函数。

void HAL_UART_TxCpltCallback(UART_HandleTypeDef *UartHandle)
{
    if(UartHandle->Instance == USART1) {
        g_uartTxReady = SET;
    }
}

void HAL_UARTEx_RxEventCallback(UART_HandleTypeDef *huart, uint16_t Size)
{
    if(huart->Instance == USART1) {
        g_uartRxReady = SET;
	}
}

UART_TestCase函数

在UART_TestCase测试用例中,我们将看到uart dma发送和接收的实例,同时计算了uart dma传输的时间。

void UART_TestCase(void)
{
    uint32_t tickStart = 0;

    printf("dma transmit test case\n");
    printf("gState:%#x, RxState:%#x\n", huart1.gState, huart1.RxState);
    if(HAL_UART_Transmit_DMA(&huart1, (uint8_t*)aTxBuffer, strlen((const char *)aTxBuffer)) != HAL_OK) {
        printf("HAL_UART_Transmit_DMA err!!!\n");
        return;
    }
    sprintf((char *)tempBuffer, "wait before gState:%#x, RxState:%#x\n", huart1.gState, huart1.RxState);
    tickStart = HAL_GetTick();
    while(!g_uartTxReady);
    g_uartTxReady = RESET;
    tickStart = HAL_GetTick() - tickStart;
    printf("%s", tempBuffer);
    printf("wait after  gState:%#x, RxState:%#x\n", huart1.gState, huart1.RxState);
    printf("dma transmit cost tick:%ld\n", tickStart);

    printf("dma receive test case\n");
    if(HAL_UARTEx_ReceiveToIdle_DMA(&huart1, (uint8_t *)aRxBuffer, sizeof(aRxBuffer)) != HAL_OK) {
        printf("HAL_UARTEx_ReceiveToIdle_DMA err!!!\n");
        return;
    }
    while(!g_uartRxReady)
    {
        printf("wait while gState:%#x, RxState:%#x\n", huart1.gState, huart1.RxState);
        HAL_Delay(500);
    }
    printf("wait after gState:%#x, RxState:%#x\n", huart1.gState, huart1.RxState);
    printf("dma receive data success\n");
    printf("aRxBuffer:%s\n", aRxBuffer);
    g_uartRxReady = RESET;
    memset(aRxBuffer, 0, sizeof(aRxBuffer));
    HAL_UARTEx_ReceiveToIdle_DMA(&huart1, (uint8_t *)aRxBuffer, sizeof(aRxBuffer));
}

UART_TestCaseTask函数

UART_TestCaseTask函数主要实现的功能是将串口DMA接收到的数据进行打印。

void UART_TestCaseTask(void)
{
    if(g_uartRxReady) {
        printf("aRxBuffer:%s\n", aRxBuffer);
        g_uartRxReady = RESET;
        memset(aRxBuffer, 0, sizeof(aRxBuffer));
        HAL_UARTEx_ReceiveToIdle_DMA(&huart1, (uint8_t *)aRxBuffer, sizeof(aRxBuffer));
    }
}

编译与下载

使用Visual Studio Code进行代码的编辑、编译、下载、调试,本部分不做详细介绍,有兴趣的可到附件下载相关资源。

实验结果

在控制台(git bush)执行make install命令,将代码下载至开发板。串口工具输出如下。

dma transmit test case
gState:0x20, RxState:0x20
---------- This is usart dma test demo ----------
wait before gState:0x21, RxState:0x20
wait after  gState:0x20, RxState:0x20
dma transmit cost tick:4
dma receive test case
wait while gState:0x20, RxState:0x22
wait while gState:0x20, RxState:0x22
wait while gState:0x20, RxState:0x22
wait while gState:0x20, RxState:0x22
wait after gState:0x20, RxState:0x20
dma receive data success
aRxBuffer: ****UART_TwoBoards communication based on DMA****
aRxBuffer: ****UART_TwoBoards communication based on DMA****
aRxBuffer: ****UART_TwoBoards communication based on DMA****
aRxBuffer: ****UART_TwoBoards communication based on DMA****
aRxBuffer: ****UART_TwoBoards communication based on DMA****

可以看到:在执行完HAL_UART_Transmit_DMA函数后,gState由ready态HAL_UART_STATE_READY(0x20)变为tx busy态HAL_UART_STATE_BUSY_TX(0x21),UART DMA发送完成后,再变为ready态HAL_UART_STATE_READY(0x20)。

在调用HAL_UARTEx_ReceiveToIdle_DMA函数后,RxState由ready态HAL_UART_STATE_READY(0x20)变为rx busy态HAL_UART_STATE_BUSY_RX(0x22),如果没有收到串口工具发来的数据,会一直打印。当收到串口工具发来的字符串“ UART_TwoBoards communication based on DMA”时,printf回显到串口工具。

进入到UART_TestCaseTask函数后,如果有接收到字符串,将printf回显到串口工具。可以看到,串口工具后面又发来了4次字符串“ UART_TwoBoards communication based on DMA”。

参考文献

[1] 为什么UART串口通信要16倍过采样数据
https://blog.csdn.net/wordwarwordwar/article/details/80178708
[2] STM32-CubeMx-HAL库-串口空闲中断+DMA——利用HAL_UARTEx_ReceiveToIdle_DMA实现不定长数据接收
https://blog.csdn.net/weixin_43864631/article/details/125855290

附件

stm32f103c8_uart_dma_20230604代码实例。
https://download.csdn.net/download/qq_24629659/87862907

你可能感兴趣的:(STM,stm32)