打印
[其他ST产品]

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

[复制链接]
206|19
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
概要

本文以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;


使用特权

评论回复
沙发
结合国际经验|  楼主 | 2023-9-30 23:32 | 只看该作者
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;

使用特权

评论回复
板凳
结合国际经验|  楼主 | 2023-9-30 23:32 | 只看该作者
STM32CubeMX工程模板
下面使用STM32CubeMX进行工程模板搭建,关键的配置信息如下图。
1、打开串口DMA发送接收中断。

使用特权

评论回复
地板
结合国际经验|  楼主 | 2023-9-30 23:33 | 只看该作者
打开串口全局中断。

使用特权

评论回复
5
结合国际经验|  楼主 | 2023-9-30 23:33 | 只看该作者
关键代码讲解
在main函数中,我们使能jlink swd模式。先进入UART_TestCase串口用例,然后在while(1) --> UART_TestCaseTask串口用例Task进行应用的处理。

使用特权

评论回复
6
结合国际经验|  楼主 | 2023-9-30 23:33 | 只看该作者
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);
    }
}

使用特权

评论回复
7
结合国际经验|  楼主 | 2023-9-30 23:33 | 只看该作者
printf实现
在printf中,我们并没有采用DMA进行传输。DMA一般用在数据量大的场景,所以单独在UART_TestCase测试用例进行实现。

使用特权

评论回复
8
结合国际经验|  楼主 | 2023-9-30 23:33 | 只看该作者
__attribute__((used)) int _write(int fd, char *ptr, int len)
{
    (void)HAL_UART_Transmit(&huart1, (uint8_t *)ptr, len, 0xFFFF);

    return len;
}

使用特权

评论回复
9
结合国际经验|  楼主 | 2023-9-30 23:33 | 只看该作者
串口相关中断函数
HAL_UART_TxCpltCallback是串口发送完成中断回调函数,HAL_UARTEx_RxEventCallback是串口空闲中断函数。

使用特权

评论回复
10
结合国际经验|  楼主 | 2023-9-30 23:34 | 只看该作者
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;
        }
}

使用特权

评论回复
11
结合国际经验|  楼主 | 2023-9-30 23:34 | 只看该作者
UART_TestCase函数
在UART_TestCase测试用例中,我们将看到uart dma发送和接收的实例,同时计算了uart dma传输的时间。

使用特权

评论回复
12
结合国际经验|  楼主 | 2023-9-30 23:34 | 只看该作者
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));
}

使用特权

评论回复
13
结合国际经验|  楼主 | 2023-9-30 23:34 | 只看该作者
UART_TestCaseTask函数
UART_TestCaseTask函数主要实现的功能是将串口DMA接收到的数据进行打印。

使用特权

评论回复
14
结合国际经验|  楼主 | 2023-9-30 23:34 | 只看该作者
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));
    }
}

使用特权

评论回复
15
结合国际经验|  楼主 | 2023-9-30 23:34 | 只看该作者
编译与下载
使用Visual Studio Code进行代码的编辑、编译、下载、调试,本部分不做详细介绍,有兴趣的可到附件下载相关资源。

使用特权

评论回复
16
结合国际经验|  楼主 | 2023-9-30 23:34 | 只看该作者
实验结果
在控制台(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****

使用特权

评论回复
17
结合国际经验|  楼主 | 2023-9-30 23:35 | 只看该作者
可以看到:在执行完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)。

使用特权

评论回复
18
结合国际经验|  楼主 | 2023-9-30 23:35 | 只看该作者
在调用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回显到串口工具。

使用特权

评论回复
19
结合国际经验|  楼主 | 2023-9-30 23:35 | 只看该作者
进入到UART_TestCaseTask函数后,如果有接收到字符串,将printf回显到串口工具。可以看到,串口工具后面又发来了4次字符串“ UART_TwoBoards communication based on DMA”。

使用特权

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

本版积分规则

47

主题

572

帖子

1

粉丝