硬汉嵌入式论坛

 找回密码
 立即注册
查看: 348|回复: 3
收起左侧

[技术讨论] STM32F207,串口+DMA+空闲中断,双缓冲

[复制链接]

38

主题

195

回帖

319

积分

高级会员

积分
319
发表于 2024-2-26 18:50:29 | 显示全部楼层 |阅读模式
本帖最后由 会飞的猪_2020 于 2024-2-26 18:52 编辑

Factor.7z (5.42 MB, 下载次数: 4)

简单做了一个回显,没有实际测过,应该有bug。

今天看到HAL库有HAL_UARTEx_ReceiveToIdle_DMA这个函数,然后上班比较闲,瞎写的。用CubeMX生成的,应该STM32系列的api都差不多。



Hal库自己有回调函数HAL_UARTEx_RxEventCallback。它是弱定义的,这个是空闲中断的回调函数。

然后看了一下hal库的实现,它是会把DMA半满和DMA满中断,也进到这里来。

[C] 纯文本查看 复制代码
/**
  * @brief DMA UART receive process half complete callback
  * @param  hdma  Pointer to a DMA_HandleTypeDef structure that contains
  *               the configuration information for the specified DMA module.
  * @retval None
  */
static void UART_DMARxHalfCplt(DMA_HandleTypeDef *hdma)
{
  UART_HandleTypeDef *huart = (UART_HandleTypeDef *)((DMA_HandleTypeDef *)hdma)->Parent;

  /* Initialize type of RxEvent that correspond to RxEvent callback execution;
     In this case, Rx Event type is Half Transfer */
  huart->RxEventType = HAL_UART_RXEVENT_HT;

  /* Check current reception Mode :
     If Reception till IDLE event has been selected : use Rx Event callback */
  if (huart->ReceptionType == HAL_UART_RECEPTION_TOIDLE)
  {
#if (USE_HAL_UART_REGISTER_CALLBACKS == 1)
    /*Call registered Rx Event callback*/
    huart->RxEventCallback(huart, huart->RxXferSize / 2U);
#else
    /*Call legacy weak Rx Event callback*/
    HAL_UARTEx_RxEventCallback(huart, huart->RxXferSize / 2U);
#endif /* USE_HAL_UART_REGISTER_CALLBACKS */
  }
  else
  {
    /* In other cases : use Rx Half Complete callback */
#if (USE_HAL_UART_REGISTER_CALLBACKS == 1)
    /*Call registered Rx Half complete callback*/
    huart->RxHalfCpltCallback(huart);
#else
    /*Call legacy weak Rx Half complete callback*/
    HAL_UART_RxHalfCpltCallback(huart);
#endif /* USE_HAL_UART_REGISTER_CALLBACKS */
  }
}


进来的时候可以通过判断RxEventType 和ReceptionType  知道到底是哪个。

然后我DMA设置的normal模式,每次在回调函数里面调用HAL_UARTEx_ReceiveToIdle_DMA去切换两个缓冲。

比较奇怪的是,我自己测试,如果串口接收的szie正好等于DMA单个buff的szie的时候,会进来两次回调。
所以会回调判断的时候做了Size == USART1_RX_BUF_SIZE这个判断。
[C] 纯文本查看 复制代码
/**
 * @brief 空闲中断回调函数
 * @param {UART_HandleTypeDef} *huart
 * @param {uint16_t} Size
 * @return {*}
 */
void HAL_UARTEx_RxEventCallback(UART_HandleTypeDef *huart, uint16_t Size)
{
    void *pingpong[1] = { 0 };
    if (huart->Instance == USART1) {
        // 当Size和USART1_RX_BUF_SIZE一样的时候,我测试下来不会触发TC,反而是触发HT。
        if (huart->RxEventType == HAL_UART_RXEVENT_IDLE || huart->ReceptionType == HAL_UART_RXEVENT_TC || Size == USART1_RX_BUF_SIZE) {
            pingpong_buffer_set_write_done(&uart1_pingpong_handler, Size);
            pingpong_buffer_get_write_buf(&uart1_pingpong_handler, pingpong);
            HAL_UARTEx_ReceiveToIdle_DMA(&huart1, pingpong[0], USART1_RX_BUF_SIZE);
        }
    }
}


我测试了,打开延时3秒,然后发送两次,它确实可以成功回复两包。说明这个缓冲切换应该是起作用的。
然后如果发送的两次以上的话,我做的逻辑是新的内容覆盖掉老的。
[C] 纯文本查看 复制代码
/**
 * @brief 在主循环中调用
 * @return {*}
 */
void bsp_uart_loop(void)
{
    void    *tempbuf[1];
    uint32_t size;
    if (pingpong_buffer_get_read_buf(&uart1_pingpong_handler, tempbuf, &size)) {

        //        HAL_Delay(3000); // 延时,用于模拟耗时的操作
        // 这里要用阻塞发送,否则的话需要在发送完成中断里面调用pingpong_buffer_set_read_done,不然的话可能这个待发送的buf会被DMA修改。
        HAL_UART_Transmit(&huart1, tempbuf[0], size, 0XFFFF);
        pingpong_buffer_set_read_done(&uart1_pingpong_handler);
    }
}




PS:比较出乎我意料的一点是我的USART1_RX_BUF_SIZE设置的只有10个字节,但是却可以回显大于10个字节的内容。。
我以为不行的,但是实际却可以,没想通是为什么...


回复

使用道具 举报

5

主题

132

回帖

147

积分

初级会员

积分
147
发表于 2024-2-26 19:32:00 | 显示全部楼层
空闲和DMA缓冲区满应该都会进入中断的
回复

使用道具 举报

2

主题

269

回帖

275

积分

高级会员

积分
275
发表于 2024-2-26 20:14:43 | 显示全部楼层
接收的话为什么不用,DMA的循环模式呢,再配个kfifo在串空闲回调时更新一下in位置就行。Size刚好指向下个位置
回复

使用道具 举报

38

主题

195

回帖

319

积分

高级会员

积分
319
 楼主| 发表于 2024-2-27 08:45:13 | 显示全部楼层
cctv180 发表于 2024-2-26 20:14
接收的话为什么不用,DMA的循环模式呢,再配个kfifo在串空闲回调时更新一下in位置就行。Size刚好指向下个位 ...

第一个原因是因为省事...这样子写不用去再加个fifo,写的时候没仔细想,下意识的就这样子做了。这个是真实原因。

我现在想一下,好像加不加没啥影响。当然可能是我哪里没考虑到,我现在脑子过了一下,加了FIFO好像对性能也好,对别的也好没啥提升...
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

QQ|小黑屋|Archiver|手机版|硬汉嵌入式论坛

GMT+8, 2024-4-29 16:36 , Processed in 0.258955 second(s), 27 queries .

Powered by Discuz! X3.4 Licensed

Copyright © 2001-2023, Tencent Cloud.

快速回复 返回顶部 返回列表