爱折腾的孩纸

stm32 Checklists - USART篇

基础初始化过程

  1. APB EnableClock, 查找定时器所在APB及分组(一般APB1 GRP1),开启时钟
    LL_APB1_GRP1_EnableClock(LL_APB1_GRP1_PERIPH_USARTx)
    
  2. 如果有相应的GPIO,初始化GPIO时钟、端口等。
  3. 填写 USART_InitStruct
    LL_USART_InitTypeDef USART_InitStruct = {0};
    USART_InitStruct.BaudRate = 9600;
    USART_InitStruct.DataWidth = LL_USART_DATAWIDTH_8B;
    USART_InitStruct.StopBits = LL_USART_STOPBITS_1;
    USART_InitStruct.Parity = LL_USART_PARITY_NONE;
    USART_InitStruct.TransferDirection = LL_USART_DIRECTION_TX_RX;
    USART_InitStruct.HardwareFlowControl = LL_USART_HWCONTROL_NONE;
    USART_InitStruct.OverSampling = LL_USART_OVERSAMOLING_16;
    
  4. 根据需要关闭或打开Overrun检测。该检测启用后,硬件将自动判断是否在接受过程中发生了输入进入,从而造成数据丢失。同时启用后,开启RXNE中断同时会开启ORE中断,给中断处理造成麻烦。
    LL_USART_DisableOverrunDetect(USARTx);
    
  5. 设置异步模式。
    LL_USART_ConfigAsyncMode(USARTx);
    
  6. 初始化USART。
    LL_USART_Init(USARTx, &USART_InitStruct);
    
  7. 启动USART。
    LL_USART_Enable(USARTx);
    

软件发送一个字节

  1. 初始化完毕。
  2. 等待传输空闲。
    while (!LL_USART_IsActiveFlag_TXE(USARTx));
    
  3. 发送数据。
    LL_USART_TransmitData8(USARTx, data);
    

软件接收一个字节

  1. 初始化完毕。
  2. 读取内容。
    while (LL_USART_IsActiveFlag_RXNE(USARTx))
    {
        uint8_t data = LL_USART_ReceiveData8(USARTx);
    }
    

中断接收

  1. 初始化完毕。
  2. 开启 RXNE 中断。
    LL_USART_EnableIT_RXNE(USARTx);
    
  3. 编写中断处理函数。
    void USARTx_IRQHandler(void)
    {
        if (LL_USART_IsActiveFlag_RXNE(USARTx))
        {
            uint8_t data = LL_USART_ReceiveData8(USARTx);
        }
    }
    
  4. 如果启用了Overrun,检查并清除ORE标志。
    if (LL_USART_IsActiveFlag_ORE(USARTx))
    {
        LL_USART_ClearFlag_ORE(USARTx);
    }
    

使用 DMA 接收

使用 DMA 接收可以最大限度避免丢失数据,同时可降低接收时的 CPU 占用。一种常用的通信模式是,发送数据前配置好 DMA,然后发送数据,同时 DMA 处理数据接收,发送完毕后,根据应接收的字节数判断是否已接收完毕。

  1. 初始化完毕。
  2. 查询外设应使用的DMA控制器及通道,配置 DMA。
    LL_DMA_SetDataTransferDirection(DMAx, LL_DMA_CHANNEL_x, LL_DMA_DIRECTION_PERIPH_TO_MEMORY);
    LL_DMA_SetChannelPriorityLevel(DMAx, LL_DMA_CHANNEL_x, LL_DMA_PRIORITY_LOW);
    LL_DMA_SetMode(DMAx, LL_DMA_CHANNEL_x, LL_DMA_MODE_NORMAL);
    LL_DMA_SetPeriphIncMode(DMAx, LL_DMA_CHANNEL_x, LL_DMA_PERIPH_NOINCRMENT);
    LL_DMA_SetMemoryIncMode(DMAx, LL_DMA_CHANNEL_x, LL_DMA_MEMORY_INCREMENT);
    LL_DMA_SetPeriphSize(DMAx, LL_DMA_CHANNEL_x, LL_DMA_PDATAALIGN_BYTE);
    LL_DMA_SetMemorySize(DMAx, LL_DMA_CHANNEL_x, LL_DMA_MDATAALIGN_BYTE);
    
  3. 设置 DMA 地址,传输数据量。
    LL_DMA_SetPeriphAddress(DMAx, LL_DMA_CHANNEL_x, LL_USART_DMA_GetRegAddr(USARTx, LL_USART_DMA_REG_DATA_RECEIVE));
    LL_DMA_SetMemoryAddress(DMAx, LL_DMA_CHANNEL_x, (uint32_t)buffer);
    LL_DMA_SetDataLength(DMAx, LL_DMA_CHANNEL_x, bufferSize);
    
  4. 启动 DMA 通道。
    LL_DMA_EnableChannel(DMAx, LL_DMA_CHANNEL_x);
    
  5. 清空 USART 接收缓冲。否则可能造成写入内存的第一个字节不是需要的内容。
    while (LL_USART_IsActiveFlag_RXNE(USARTx))
    {
        uint8_t drop = LL_USART_ReceiveData8(USARTx);
    }
    
  6. 启动 USART 发送到 DMA。
    LL_USART_EnableDMAReq_RX(USARTx);
    
  7. 查询已接收的字节数。
    static uint16_t __dma_read_size()
    {
        return BUFFER_SIZE - LL_DMA_GetDataLength(DMAx, LL_DMA_CHANNEL_x);
    }
    
  8. 等待接收到需要大小的数据。
    static uint16_t __dma_wait_read(uint16_t sizeUntil, uint32_t timeout)
    {
        uint32_t tick = HAL_GetTick();
        while (__dma_read_size() < sizeUntil)
        {
            if (HAL_GetTick() - tick > timeout)
            {
                return TIMEOUT;
            }
        }
        return SUCCESS;
    }
    

评论