UART_DMA_DBUS-DEMO

本文详细介绍了如何通过DMA加速UART的异步通信过程,包括配置步骤、DMA初始化与使用,以及在串口中断回调中调用DMA接收。特别关注了双缓冲区策略和反相器(DBUS)的应用,以提高数据传输效率。

UART_DMA_DBUS

UART

  • 打开UART或者USART-选择Asynchronous

  • Asynchronous - 异步通信

    Synchronous - 同步通信

  • 打开Parameter Settings选项卡

  • 分别配置波特率、数据位长度、校验位、停止位

  • 数据方向根据需求配置

  • 过采样一般默认16

  • NVIC Setting选项卡中打开串口中断-根据需求自行开启

  • DMA Setting-Add-一般默认即可

  • 优先级根据需求调整

  • GPIO Setting-检查IO口是否选择正确

UART基础配置完成


DMA

  • DMA配置可以使用HAL库函数直接配置
  • 也可以自己仿照HAL库函数进行配置
  • 此处采用自己配置的方式配置DMA

DAM_init

void DMA_init(void)
{
    //enable the DMA transfer for the receiver request
    SET_BIT(huart3.Instance->CR3, USART_CR3_DMAR);
    //enalbe idle interrupt
    __HAL_UART_ENABLE_IT(&huart3, UART_IT_IDLE);
    //disable DMA
    __HAL_DMA_DISABLE(&hdma_usart3_rx);
    while(hdma_usart3_rx.Instance->CR & DMA_SxCR_EN)
    {
        __HAL_DMA_DISABLE(&hdma_usart3_rx);
    }
    hdma_usart3_rx.Instance->PAR = (uint32_t) & (USART3->DR);
    //memory buffer 1
    hdma_usart3_rx.Instance->M0AR = (uint32_t)(sbus_rx_buf[0]);
    //memory buffer 2
    hdma_usart3_rx.Instance->M1AR = (uint32_t)(sbus_rx_buf[1]);
    //data length
    hdma_usart3_rx.Instance->NDTR = SBUS_RX_BUF_NUM;
    //enable double memory buffer
    SET_BIT(hdma_usart3_rx.Instance->CR, DMA_SxCR_DBM);
    //enable DMA
    __HAL_DMA_ENABLE(&hdma_usart3_rx);

}

注:此处需要更改的关键字如下:

huart3 — 对应串口句柄

hdma_usart3_rx — 对应串口DMA接收句柄

USART3 — 对应串口句柄

sbus_rx_buf[0] — 对应DMA第一缓冲区

sbus_rx_buf[1] — 对应DMA第二缓冲区

DMA_use

void DMA_receive(void)
{
	if(huart3.Instance->SR & UART_FLAG_RXNE)
    {
        __HAL_UART_CLEAR_PEFLAG(&huart3);
    }
    else if(USART3->SR & UART_FLAG_IDLE)
    {
        static uint16_t this_time_rx_len = 0;
        __HAL_UART_CLEAR_PEFLAG(&huart3);
        if ((hdma_usart3_rx.Instance->CR & DMA_SxCR_CT) == RESET)
        {
            /* Current memory buffer used is Memory 0 */
            //disable DMA
            __HAL_DMA_DISABLE(&hdma_usart3_rx);
            //get receive data length, length = set_data_length - remain_length
            this_time_rx_len = SBUS_RX_BUF_NUM - hdma_usart3_rx.Instance->NDTR;
            //reset set_data_lenght
            hdma_usart3_rx.Instance->NDTR = SBUS_RX_BUF_NUM;
            //set memory buffer 1
            hdma_usart3_rx.Instance->CR |= DMA_SxCR_CT;
            //enable DMA
            __HAL_DMA_ENABLE(&hdma_usart3_rx);
            if(this_time_rx_len == RC_FRAME_LENGTH)
            {
               sbus_to_rc(sbus_rx_buf[0], &rc);
            }
        }
        else
        {
            /* Current memory buffer used is Memory 1 */
            //disable DMA
            __HAL_DMA_DISABLE(&hdma_usart3_rx);
            //get receive data length, length = set_data_length - remain_length
            this_time_rx_len = SBUS_RX_BUF_NUM - hdma_usart3_rx.Instance->NDTR;
            //reset set_data_lenght
            hdma_usart3_rx.Instance->NDTR = SBUS_RX_BUF_NUM;
            //set memory buffer 0
            DMA1_Stream1->CR &= ~(DMA_SxCR_CT);
            //enable DMA
            __HAL_DMA_ENABLE(&hdma_usart3_rx);
            if(this_time_rx_len == RC_FRAME_LENGTH)
            {
                sbus_to_rc(sbus_rx_buf[1], &rc);
            }
        }
    }
}

注:此处需要更改的关键字如下:

huart3 — 对应串口句柄

hdma_usart3_rx — 对应串口DMA接收句柄

USART3 — 对应串口句柄

sbus_rx_buf[0] — 对应DMA第一缓冲区

sbus_rx_buf[1] — 对应DMA第二缓冲区

SBUS_RX_BUF_NUM — DMA缓冲区给的空间长度

RC_FRAME_LENGTH — DMA接收到的目标数据长度

sbus_to_rc — 处理DMA接收到的数据的函数

DMA1_Stream1 — 对应的DMA数据流

最后需要在串口回调函数里调用 - DMA_receive()

eg: - 此处以串口3的回调函数为例

void USART3_IRQHandler(void)
{
  /* USER CODE BEGIN USART3_IRQn 0 */
	DMA_receive();
  /* USER CODE END USART3_IRQn 0 */
  HAL_UART_IRQHandler(&huart3);
  /* USER CODE BEGIN USART3_IRQn 1 */
  /* USER CODE END USART3_IRQn 1 */
}

对DMA的理解

DMA_init()

  • 使能DMA串口接收
  • 使能串口空闲中断
  • 先暂时关闭DMA,以便对DMA进行配置
  • 配置DMA外设地址 - 此处用于串口接收,所以配置为串口的DR数据寄存器
  • 配置DMA第一缓冲区地址
  • 配置DMA第二缓冲区地址
  • 配置DMA数据长度 - 此处一般配置为实际接收数据的两倍
  • 使能双缓冲区配置
  • 使能DMA - DMA配置完成后打开DMA

DMA_use

  • 如果接收到数据并且串口有空闲中断触发
  • 先关闭DMA
  • 获取这一次接收到的数据长度
  • 重新设定下一次接收的数据长度
  • 重新设定下一次接收的缓冲器的地址
  • 打开DMA-使能DMA
  • 如果这一次接收到的数据长度等于我DMA接收目标数据长度
  • 则对DMA接收到的数据进行解析处理 - sbus_to_rc ()
  • 下一次就使用另外一个缓冲区进行接收
  • 最后在串口中断回调函数里调用就可以

DBUS

在串口前面加一个反相器

在这里插入图片描述




<think> 我们正在讨论的是STM32F0系列,根据前面的讨论,F0系列没有现成的宏,需要自己定义。 问题:如何正确启用UARTDMA接收?是否可以用`SET_BIT((__HANDLE__)->Instance->CR3, USART_CR3_DMAR)`或者`huart->Instance->CR3 |= USART_CR3_DMAR`来代替`__HAL_UART_ENABLE_DMA_RX(huart)`? 回答:是的,对于F0系列,我们可以使用`SET_BIT`宏或者直接操作寄存器来使能DMA接收。但是要注意,除了设置UART的CR3寄存器,还要配置并启动DMA通道。 ### 解决方案: 在STM32F0系列中,使能UARTDMA接收需要两个步骤: 1. 设置UART的CR3寄存器的DMAR位(DMA接收使能)。 2. 启动DMA通道(使用HAL库函数或直接配置DMA通道)。 #### 代码示例: ```c // 自定义宏:使能UART DMA接收 #define __HAL_UART_ENABLE_DMARX(__HANDLE__) \ do { \ SET_BIT((__HANDLE__)->Instance->CR3, USART_CR3_DMAR); \ __HAL_DMA_ENABLE((__HANDLE__)->hdmarx); \ } while(0) // 使用示例: void UART_Start_DMA_Receive(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size) { // 配置DMA接收缓冲区 huart->hdmarx->Instance->CMAR = (uint32_t)pData; huart->hdmarx->Instance->CNDTR = Size; // 使能UART DMA接收 __HAL_UART_ENABLE_DMARX(huart); // 如果还需要开启DMA传输完成中断等,需单独设置 } ``` ### 详细解释: 1. **设置UART的CR3寄存器**: - `USART_CR3_DMAR`是UART控制寄存器3中使能DMA接收的位。设置该位后,UART会在接收到数据时产生DMA请求。 - 使用`SET_BIT`宏或直接赋值(`huart->Instance->CR3 |= USART_CR3_DMAR;`)均可。 2. **启动DMA通道**: -设置UART之前或之后,需要确保DMA通道已配置并启动。 - 可以使用`__HAL_DMA_ENABLE`宏来使能DMA通道,该宏会设置DMA_CCR寄存器的EN位。 3. **完整步骤**: 通常,我们使用`HAL_UART_Receive_DMA`函数来启动DMA接收。但在F0系列中,该函数内部已经处理了这些步骤。如果我们需要自定义操作,可以按照上述方式。 ### 替代方案:直接使用寄存器操作 ```c // 使能UART DMA接收 huart->Instance->CR3 |= USART_CR3_DMAR; // 使能DMA通道 huart->hdmarx->Instance->CCR |= DMA_CCR_EN; ``` ### 注意事项: - 在启动DMA接收之前,应该先配置DMA通道(内存地址、外设地址、传输长度等)。 - 如果需要中断,还需要配置DMA中断并使能NVIC。 - 在F0系列中,DMA的配置和使能与其它系列类似,但寄存器名称可能略有不同。 ### 与标准HAL库的兼容性: 标准HAL库中的`HAL_UART_Receive_DMA`函数已经包含了这些步骤。因此,如果没有特殊需求,建议直接使用该函数。但是,在回调函数中需要单独控制DMA的使能/禁用时,可以按照上述方式自定义宏。 §§相关问题§§: 1. 在STM32F0中如何配置UARTDMA发送? 2. 如何安全地停止STM32F0的UART DMA接收并重新启动? 3. 在DMA传输过程中,如何获取当前剩余传输数据量? 4. STM32F0系列中DMA中断处理函数应该注意哪些事项? </think> ### 正确启用 UART DMA 接收的实现 在 STM32F0 系列中,您可以使用以下方法正确启用 UART DMA 接收: #### 推荐实现代码: ```c // 使能 UART DMA 接收的推荐方法 void HAL_UART_Enable_DMA_RX(UART_HandleTypeDef *huart) { // 1. 设置 UART CR3 寄存器的 DMAR 位 SET_BIT(huart->Instance->CR3, USART_CR3_DMAR); // 2. 使能 DMA 通道 __HAL_DMA_ENABLE(huart->hdmarx); // 3. 可选:使能 DMA 中断 __HAL_DMA_ENABLE_IT(huart->hdmarx, DMA_IT_TC); // 传输完成中断 } ``` ### 三种实现方式的比较: 1. **使用 `SET_BIT` 宏 (推荐)**: ```c SET_BIT(huart->Instance->CR3, USART_CR3_DMAR); ``` - 这是最安全的方式,直接使用 HAL 库提供的宏 - 可读性好,易于维护 - 自动处理寄存器访问的原子性 2. **直接寄存器操作**: ```c huart->Instance->CR3 |= USART_CR3_DMAR; // 正确写法 ``` - 等价于 `SET_BIT` 但更底层 - **注意:** 您提供的 `|= ~USART_CR3_DMAR` 是错误的写法,应该是 `|= USART_CR3_DMAR` 3. **自定义宏 (清晰封装)**: ```c #define __HAL_UART_ENABLE_DMA_RX(__HANDLE__) \ do { \ SET_BIT((__HANDLE__)->Instance->CR3, USART_CR3_DMAR); \ __HAL_DMA_ENABLE((__HANDLE__)->hdmarx); \ } while(0) ``` ### 完整 UART DMA 接收配置示例: ```c // 在 main.c 或其他适当位置 // 定义接收缓冲区 uint8_t rx_buffer[128]; void Configure_UART_DMA(UART_HandleTypeDef *huart) { // 1. 配置 DMA huart->hdmarx->Instance->CMAR = (uint32_t)rx_buffer; // 内存地址 huart->hdmarx->Instance->CPAR = (uint32_t)&huart->Instance->RDR; // 外设地址 huart->hdmarx->Instance->CNDTR = sizeof(rx_buffer); // 数据传输量 // 2. 配置 DMA 通道优先级和工作模式 MODIFY_REG(huart->hdmarx->Instance->CCR, DMA_CCR_PL | DMA_CCR_MSIZE | DMA_CCR_PSIZE | DMA_CCR_MINC | DMA_CCR_CIRC, DMA_PRIORITY_MEDIUM | // 中等优先级 DMA_MDATAALIGN_BYTE | // 内存字节对齐 DMA_PDATAALIGN_BYTE | // 外设字节对齐 DMA_MINC_ENABLE | // 内存地址递增 DMA_CIRCULAR); // 循环模式 // 3. 使能 DMA 接收 SET_BIT(huart->Instance->CR3, USART_CR3_DMAR); // 使能 UART DMA 请求 __HAL_DMA_ENABLE(huart->hdmarx); // 激活 DMA 通道 // 4. 使能 DMA 传输完成中断 __HAL_DMA_ENABLE_IT(huart->hdmarx, DMA_IT_TC); // 5. 使能 UART 接收中断(如果需要错误处理) __HAL_UART_ENABLE_IT(huart, UART_IT_ERR); } ``` ### 关键注意事项: 1. **操作顺序**: - 先配置 DMA 参数(地址、长度、模式) - 再使能 UARTDMA 请求 (`USART_CR3_DMAR`) - 最后使能 DMA 通道 (`DMA_CCR_EN`) 2. **寄存器区别**: - `USART_CR3_DMAR`: UART 控制寄存器3的 DMA 接收使能位 - `DMA_CCR_EN`: DMA 通道配置寄存器的通道使能位 - 两者都需要设置才能使 DMA 接收正常工作 3. **中断处理**: DMA 传输完成后需要在中断中处理数据: ```c void DMA1_Channel1_IRQHandler(void) { if(__HAL_DMA_GET_FLAG(hdma, DMA_FLAG_TC1)) { // 清除传输完成标志 __HAL_DMA_CLEAR_FLAG(hdma, DMA_FLAG_TC1); // 处理接收到的数据 Process_Rx_Data(rx_buffer, sizeof(rx_buffer)); } } ```
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值