硬汉嵌入式论坛

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

STM32V6之串口HAL库串口空闲中断接收不定长字节代码实现

  [复制链接]

5

主题

192

回帖

212

积分

高级会员

积分
212
发表于 2020-11-20 21:32:44 | 显示全部楼层 |阅读模式


05_STM32V6_UART_IT_DMA_IDLE.rar (5.42 MB, 下载次数: 2160)


搜索stm32f4xx_hal_uart.c下的IDLE你会发现搜索不到,因为HAL库没有对IDLE方式的应用处理程序,用户需要自己完成对空闲中断的处理程序下面第一张图是串口空闲中断处理方式,注意如果串口接收如果放在中断里处理,发送HAL库API时IT和DMA需要开关总中断


  1. /* 注意阻塞方式的发送函数不能通过开关中断开操作,避免使用阻塞方式 */
  2. void BSP_UART_Transmit_IT(UART_HandleTypeDef *huart,  uint8_t *pData, uint16_t Size)
  3. {
  4.     while(huart->gState != HAL_UART_STATE_READY)
  5.     {

  6.     }
  7.     __disable_irq();
  8.     HAL_UART_Transmit_IT(huart, pData, Size);
  9.     __enable_irq();
  10. }

  11. void BSP_UART_Transmit_DMA(UART_HandleTypeDef *huart,  uint8_t *pData, uint16_t Size)
  12. {
  13.     while(huart->gState != HAL_UART_STATE_READY)
  14.     {

  15.     }
  16.     __disable_irq();
  17.     HAL_UART_Transmit_DMA(huart, pData, Size);
  18.     __enable_irq();
  19. }
复制代码


处理流程.jpg
1605878783(1).jpg
1605878809.jpg

空闲中断标志通过__HAL_UART_GET_FLAG(huart, UART_FLAG_IDLE)获取,清除空闲中断需要使用:
/* 清除串口空闲中断 */
__HAL_UART_CLEAR_IDLEFLAG(huart);


1605878994(1).jpg
空闲中断触发有2种情况使用IT或者DMA,根据标志位来判断当前使用何种方式

HAL_IS_BIT_SET(huart->Instance->CR3,USART_CR3_DMAR);

触发空闲中断以后DMA传输剩余空闲可以通过查询寄存器
rxCnt =__HAL_DMA_GET_COUNTER(huart->hdmarx);
如果是中断方式的传输在5.3提到了中断发送方式有计时器XferCount
rxCnt = huart->RxXferCount;

我们发起传输的字节数量存放在XferSize里面
所以接收到多少个字节可以根据
Length = XferSize – rxCnt 计算得出。
当触发空闲中断以后这个时候DMAIT传输还没有停止所以需要终止本次传输调用接口HAL_UART_AbortReceive(huart);
关闭串口接收。同时设置接收完成标志位,并重新开启ITDMA接收


1605878923(1).jpg
为何BspUart_t结构体中接收缓冲区使用指针类型,在定义的时候把全局BUF传给pRxBuf,因为每个串口的BUF区大小是不一样的,这样就不用每个串口定义一个结构体类型了,通过rxSize来定义缓冲区的大小,那为何要使用const呢,一个是比较节省RAM,还有就是size定义完不用修改防止意外修改。但是需要注意的是const变量在结构体变量定义的时候必须赋值,除非你的业务程序中用不到。



评分

参与人数 2金币 +40 收起 理由
Qiaohuixi + 20 很给力!
eric2013 + 20 很给力!

查看全部评分

回复

使用道具 举报

1万

主题

6万

回帖

10万

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
106749
QQ
发表于 2020-11-21 09:30:52 | 显示全部楼层
非常感谢楼主分享
回复

使用道具 举报

5

主题

519

回帖

534

积分

金牌会员

积分
534
发表于 2020-11-23 08:46:41 | 显示全部楼层
楼主,图标用啥软件产生的?谢谢分享。
回复

使用道具 举报

5

主题

192

回帖

212

积分

高级会员

积分
212
 楼主| 发表于 2020-11-23 10:36:13 | 显示全部楼层
hqgboy 发表于 2020-11-23 08:46
楼主,图标用啥软件产生的?谢谢分享。

你问的是流程图?收费的亿图图示,免费的功能少,不过画流程图工具很多
回复

使用道具 举报

5

主题

519

回帖

534

积分

金牌会员

积分
534
发表于 2020-11-23 15:56:34 | 显示全部楼层
旮旯旭 发表于 2020-11-23 10:36
你问的是流程图?收费的亿图图示,免费的功能少,不过画流程图工具很多

谢谢。。。。
回复

使用道具 举报

0

主题

13

回帖

13

积分

新手上路

积分
13
发表于 2020-12-8 10:06:15 | 显示全部楼层

非常感谢楼主分享
回复

使用道具 举报

3

主题

1223

回帖

1232

积分

至尊会员

积分
1232
发表于 2020-12-10 13:14:32 | 显示全部楼层
感谢楼主分享,很给力!!
回复

使用道具 举报

1

主题

14

回帖

17

积分

新手上路

积分
17
发表于 2021-7-9 03:37:25 | 显示全部楼层
您好 最近发现个问题 使用了您的空闲中断 在串口调试助手给单片机串口发消息 要发两条 单片机才能收到一条 这是什么原因呢?

还有 您在代码里面说 发送函数需要开关中断 这句指的是什么意思呢

目前用的是HAL_UART_Transmit

/* 重新开启串口接收 如果在中断里面开启接收,发送函数需要开关中断
                                具体原因请看文档UART里面关于 __HAL_LOCK 说明 */
                        if(dmaBit)
                        {
                                HAL_UART_Receive_DMA(&huart2, g_tBspUart2.pRxBuf, g_tBspUart2.rxSize);
                        }
                        else
                        {
                                HAL_UART_Receive_IT(&huart2, g_tBspUart2.pRxBuf, g_tBspUart2.rxSize);
                        }
回复

使用道具 举报

5

主题

192

回帖

212

积分

高级会员

积分
212
 楼主| 发表于 2021-7-9 13:09:37 | 显示全部楼层
lyy26655 发表于 2021-7-9 03:37
您好 最近发现个问题 使用了您的空闲中断 在串口调试助手给单片机串口发消息 要发两条 单片机才能收到一条  ...

1.需要发送2条收到一条?你用的F429开发板还是移植了代码?我没遇到过这个情况,是不是哪里没注意?
2.如果重新开启接收的函数HAL_UART_Receive_IT,HAL_UART_Receive_DMA 如果在callback里面开启,
由于发送函数在主流程里面发起串口发送会__HAL_LOCK(UART),这个时候如果刚好触发中断,会导致串口接收开启失败,简单的做法是在发送的时候开关中断,防止进入中断。不过阻塞方式不能用,会导致中断得不到及时的响应。这个是HAL库的问题
回复

使用道具 举报

1

主题

14

回帖

17

积分

新手上路

积分
17
发表于 2021-7-10 13:19:33 | 显示全部楼层
本帖最后由 lyy26655 于 2021-7-12 13:18 编辑
旮旯旭 发表于 2021-7-9 13:09
1.需要发送2条收到一条?你用的F429开发板还是移植了代码?我没遇到过这个情况,是不是哪里没注意?
2. ...

麻烦您了,应该是第二条情况,您说的阻塞方式就是用HAL_UART_Transmit发送对吧……现在整个程序都是用这个函数进行发送的,如果要改工作量可能比较大
在网上搜了下 说暴力解锁也许能解决这个问题?即 注释掉__HAL_LOCK、__HAL_UNLOCK即可
回复

使用道具 举报

5

主题

192

回帖

212

积分

高级会员

积分
212
 楼主| 发表于 2021-7-13 13:45:47 | 显示全部楼层
lyy26655 发表于 2021-7-10 13:19
麻烦您了,应该是第二条情况,您说的阻塞方式就是用HAL_UART_Transmit发送对吧……现在整个程序都是用这 ...

是的,一般不用阻塞,IT和DMA方式运行时间比较短,可以在调用前关闭中断,操作完再开启。ST的HAL库CAN通信特别容易触发,后来CAN部分的HAL库ST重写了,但是很遗憾UART I2C SPI ADC这些ST居然不优化掉,真是无语
回复

使用道具 举报

1

主题

14

回帖

17

积分

新手上路

积分
17
发表于 2021-8-2 15:32:28 | 显示全部楼层
旮旯旭 发表于 2021-7-13 13:45
是的,一般不用阻塞,IT和DMA方式运行时间比较短,可以在调用前关闭中断,操作完再开启。ST的HAL库CAN通 ...

那完蛋了 整个程序都是用HAL_UART_Transmit写的 这是不是没有补救措施了 T T

还有您说如果用HAL_UART_Transmit_IT,就是在发送前先__HAL_UART_DISBLE_IT(&huart1, UART_IT_IDLE);
发完再__HAL_UART_ENABLE_IT(&huart1, UART_IT_IDLE); 是这个意思吗

谢谢!
回复

使用道具 举报

1

主题

14

回帖

17

积分

新手上路

积分
17
发表于 2021-8-2 15:43:54 | 显示全部楼层
看了下原工程 BSP_UART_Transmit_IT这个函数已经封装过关中断——》发送——》开中断的操作了 我遇到的“在串口调试助手给单片机串口发消息 要发两条 单片机才能收到一条”这个问题很可能和“发送函数在主流程里面发起串口发送会__HAL_LOCK(UART)”这个有关系T T

现在全部代码写完了 感觉阻塞这个方式应该是没救了T T只能全部将HAL_UART_Transmit替换为BSP_UART_Transmit_IT这种方法了吗
回复

使用道具 举报

1

主题

14

回帖

17

积分

新手上路

积分
17
发表于 2021-8-2 16:11:44 | 显示全部楼层
在使用HAL库的时候发现同一串口的接收和发送如果同时进行的话会发生问题,因为
如果用调用下面的函数
HAL_StatusTypeDef HAL_UART_Receive_IT(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size)

进行接收串口数据的时候,有可能
HAL_StatusTypeDef HAL_UART_Transmit(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size, uint32_t Timeout)

也正在进行中,而这两个函数都会对串口资源上锁
__HAL_LOCK(huart);
导致中断接收函数 HAL_StatusTypeDef HAL_UART_Receive_IT  返回hal_busy; 没有正常的执行,所以串口接收中断也没有打开,再也接收不到下一个字节了。
@旮旯旭 也就是说 这二者是会互相影响 如果正好串口有数据Receive,此时__HAL_LOCK(huart)了,Transmit也会无法发送 对吗

回复

使用道具 举报

1

主题

14

回帖

17

积分

新手上路

积分
17
发表于 2021-8-2 16:23:48 | 显示全部楼层
还有个问题想请问您 如果我向串口1发送数据交互(4G模块) 然后向串口2输出调试信息 都是用HAL_UART_Transmit发送的

while(1)
{
    if(1min_flag == 1)
  {
      HAL_UART_Transmit(&huart1,temp,strlen(temp),0xffff);
      delay_ms(2000);
      HAL_UART_Transmit(&huart2,temp,strlen(temp),0xffff);
      1min_flag=0;
  }
}

中间用延时隔开
此过程中一直保持串口空闲中断 及 BSP_UART_Transmit_IT开启 串口1为定时器超时计数-单数据接收模式  串口2为空闲中断接收不定长字节
给串口1用HAL_UART_Transmit发数据 发送十几包 接收端只能收到一包 不确定是不是和__HAL_LOCK(UART)有关系
回复

使用道具 举报

5

主题

192

回帖

212

积分

高级会员

积分
212
 楼主| 发表于 2021-8-2 19:42:45 | 显示全部楼层
lyy26655 发表于 2021-8-2 16:23
还有个问题想请问您 如果我向串口1发送数据交互(4G模块) 然后向串口2输出调试信息 都是用HAL_UART_Transm ...

1.你如果都是用的阻塞,你可以把接收开启HAL_UART_Receive_IT或者DMA放在while里面,不要放在中断callback回调里。能保证数据及时处理,且不要忘记在处理完调用接收函数就可以了
2.如果在while里面发送 HAL_LOCK了串口,由于中断优先级高,如果刚好触发中断先处理中断,
在中断里面打开接收的话,由于串口被HAL_LCOK了会导致开启失败。
3.你阻塞方式的干嘛要延迟 2s? 你如果使用IT方式发送,那么你需要注意数据发送完了才能发送下一包,
且发送的缓冲区要全局或静态变量。不然发送出去是乱码,你发送了十几包收到一包是不是没注意在

void HAL_UART_TxCpltCallback(UART_HandleTypeDef *huart)
{
    BspUart_t *phuartx = UART_GetHandlePtr(huart);
        
    phuartx->txStat = UART_TX_IDLE;
}
我目前写的发送函数
void BSP_UART_Transmit_IT(BspUart_t *phuartx,  uint8_t *pData, uint16_t Size)
{
    while(phuartx->txStat == UART_TX_BUSY)
    {
        
    }
    phuartx->txStat = UART_TX_BUSY;
        memcpy(phuartx->pTxBuf, pData, Size);
    __disable_irq();
    HAL_UART_Transmit_IT(phuartx->huart, phuartx->pTxBuf, Size);
    __enable_irq();
}

4.还有你说的串口1是4G模块是吧? 4g模块如果指令阶段的话都是ASCII码。教你一个好方法
#define printf_4G(...)                        HAL_UART_Transmit(&huart1, (uint8_t *)txBuf, \
                                                  (uint16_t)sprintf((char *)txBuf, __VA_ARGS__), 0xFFFF)
printf_4G("AT\r\n");

5.你V6群加了么? 我叫旮旯旭
回复

使用道具 举报

1

主题

14

回帖

17

积分

新手上路

积分
17
发表于 2021-8-4 09:29:45 | 显示全部楼层
旮旯旭 发表于 2021-8-2 19:42
1.你如果都是用的阻塞,你可以把接收开启HAL_UART_Receive_IT或者DMA放在while里面,不要放在中断callbac ...

非常感谢您认真细致的回复 慢慢消化下
回复

使用道具 举报

5

主题

132

回帖

147

积分

初级会员

积分
147
发表于 2021-8-4 14:35:42 | 显示全部楼层
STM的HAL船新版本多了个  HAL_UARTEx_ReceiveToIdle_DMA() 函数,可以直接调用了
回复

使用道具 举报

5

主题

192

回帖

212

积分

高级会员

积分
212
 楼主| 发表于 2021-8-4 16:02:52 | 显示全部楼层
无关风月 发表于 2021-8-4 14:35
STM的HAL船新版本多了个  HAL_UARTEx_ReceiveToIdle_DMA() 函数,可以直接调用了

F7 H7有了  F1 F2 F4也有了?
回复

使用道具 举报

30

主题

139

回帖

234

积分

高级会员

积分
234
发表于 2022-7-12 15:03:36 | 显示全部楼层
请问,接受的数据是不定长的,那对单次接收的最大数据长度有限制吗?
比如我的 uart_rx_dma_buffer[64];
那单次可以接收超过 64字节的数据吗?
回复

使用道具 举报

3

主题

1223

回帖

1232

积分

至尊会员

积分
1232
发表于 2022-7-12 16:22:11 | 显示全部楼层
[C] 纯文本查看 复制代码
HAL_StatusTypeDef HAL_UARTEx_ReceiveToIdle_DMA(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size)
{
  HAL_StatusTypeDef status;

  /* Check that a Rx process is not already ongoing */
  if (huart->RxState == HAL_UART_STATE_READY)
  {
    if ((pData == NULL) || (Size == 0U))
    {
      return HAL_ERROR;
    }

    __HAL_LOCK(huart);

    /* Set Reception type to reception till IDLE Event*/
    huart->ReceptionType = HAL_UART_RECEPTION_TOIDLE;

    status =  UART_Start_Receive_DMA(huart, pData, Size);

    /* Check Rx process has been successfully started */
    if (status == HAL_OK)
    {
      if (huart->ReceptionType == HAL_UART_RECEPTION_TOIDLE)
      {
        __HAL_UART_CLEAR_IDLEFLAG(huart);
        ATOMIC_SET_BIT(huart->Instance->CR1, USART_CR1_IDLEIE);
      }
      else
      {
        /* In case of errors already pending when reception is started,
           Interrupts may have already been raised and lead to reception abortion.
           (Overrun error for instance).
           In such case Reception Type has been reset to HAL_UART_RECEPTION_STANDARD. */
        status = HAL_ERROR;
      }
    }

    return status;
  }
  else
  {
    return HAL_BUSY;
  }
}


这函数怎么个用法?
回复

使用道具 举报

5

主题

192

回帖

212

积分

高级会员

积分
212
 楼主| 发表于 2022-7-13 11:45:37 | 显示全部楼层
morning_enr6U 发表于 2022-7-12 16:22
[mw_shl_code=c,true]HAL_StatusTypeDef HAL_UARTEx_ReceiveToIdle_DMA(UART_HandleTypeDef *huart, uint8_ ...

https://www.armbbs.cn/forum.php? ... digest%26digest%3D1

看这个帖子在V7板子上实现的,V6的板子调用的函数回调都一样的
回复

使用道具 举报

0

主题

11

回帖

11

积分

新手上路

积分
11
发表于 2022-7-24 09:08:29 | 显示全部楼层
STM32串口DMA 定时计数接收不定长数据和发送定长数据.pdf (508.66 KB, 下载次数: 113)
多谢分享,官方也有个类似的实现方式
回复

使用道具 举报

2

主题

8

回帖

19

积分

新手上路

积分
19
发表于 2022-11-10 22:02:06 | 显示全部楼层
无关风月 发表于 2021-8-4 14:35
STM的HAL船新版本多了个  HAL_UARTEx_ReceiveToIdle_DMA() 函数,可以直接调用了

F4的勉强可以用。F1的不好用。
回复

使用道具 举报

75

主题

684

回帖

909

积分

金牌会员

积分
909
发表于 2022-11-12 12:03:42 | 显示全部楼层
HAL库有空闲中断的处理的,只不过有点BUG,需要自己修改一下的。
回复

使用道具 举报

0

主题

26

回帖

26

积分

新手上路

积分
26
发表于 2022-11-24 16:04:41 | 显示全部楼层
本帖最后由 miaoguoqiang 于 2022-11-25 09:04 编辑
庄永 发表于 2022-11-12 12:03
HAL库有空闲中断的处理的,只不过有点BUG,需要自己修改一下的。

什么bug.新版本HAL还没遇到问题
回复

使用道具 举报

0

主题

13

回帖

13

积分

新手上路

积分
13
发表于 2022-12-3 13:48:59 | 显示全部楼层
回复

使用道具 举报

4

主题

286

回帖

298

积分

高级会员

积分
298
发表于 2024-4-22 16:07:28 | 显示全部楼层
楼主有个小小的疑问,为什么“printf()“函数用了标准的 while()方式,没有用MDA方式?
回复

使用道具 举报

1万

主题

6万

回帖

10万

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
106749
QQ
发表于 2024-4-22 17:22:15 | 显示全部楼层
soyshell 发表于 2024-4-22 16:07
楼主有个小小的疑问,为什么“printf()“函数用了标准的 while()方式,没有用MDA方式?

fputc那里每次打印1个字符,DMA效率比较低
回复

使用道具 举报

4

主题

286

回帖

298

积分

高级会员

积分
298
发表于 2024-4-22 18:00:48 | 显示全部楼层
谢谢硬汉的回复。已经依照楼主的方法移植到F407上,非常好用!
回复

使用道具 举报

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

本版积分规则

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

GMT+8, 2024-5-4 04:24 , Processed in 0.299637 second(s), 29 queries .

Powered by Discuz! X3.4 Licensed

Copyright © 2001-2023, Tencent Cloud.

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