当预先不知道要接收的数据时,如何使用 DMA 在 UART 上读取数据的信息。
在STM32中,通常有以下几种模式:
-
轮询模式
程序必须轮询状态位以检查是否已收到新字符并以足够快的速度读取它以获得所有字节
优点
很容易实现,但在真正项目中的应用很少
缺点
在突发数据中很容易错过接收到的字符
仅适用于低波特率
应用程序必须非常快速地检查是否收到新的数据 -
中断模式(无DMA):
UART触发中断,CPU跳转到服务程序处理数据接收
优点
目前程序中最常用的方法
在低速率下工作良好,115200 波特
缺点
为每个接收到的字符执行中断服务程序
可能会在具有许多中断的高性能 MCU 中停止其他任务
一次接收突发数据时可能会停止操作系统 -
DMA 模式:
DMA 用于在硬件级别将数据从 USART RX 数据寄存器传输到用户存储器。 除了在必要时由应用程序处理接收到的数据,此时不需要应用程序交互
优点
i.从 USART 外设到内存的传输是在硬件完成的,无需 CPU干涉
ii.可以很容易地与操作系统一起工作
iii.针对最高波特率 > 1Mbps 和低功耗应用进行了优化
vi.在大量数据突发的情况下,增加数据缓冲区大小可以改进功能
缺点
i.DMA 硬件必须事先知道要传输的字节数
ii.如果通信失败,DMA 可能不会通知应用程序所有传输的字节本文仅关注接收未知数据长度的 DMA 模式。
STM32中的DMA
在STM32中DMA有norma和circular两种模式,每种模式在数据进行传输时需要告知传输数据的大小
- Normal mode:
DMA 开始传输数据,当它传输完成后就会停止。下一次开始需要手动处理。 - Circular mode:
DMA从传输开始,当它传输完成后会自动按照相同配置重新传输,周而复始直至被控制停止或传输发生错误。
在这两种mode中有两种中断模式:
- 达到半传输(Half-Transfer complete (HT) interrupt):DMA 数据传输达到一半时 HTIF 标志位被置 1,如果使能 HTIE 中断控制位将产生达到半传输中断;
- 传输完成(Transfer-Complete (TC) interrupt):DMA 数据传输完成时 TCIF 标志位被置 1,如果使能 TCIE 中断控制位将产生传输完成中断;
我们通过 DMA 收到有关 HT 或 TC 事件的通知。 想象一下假设要接收 20 个字节,但它仅接收 14 个会发生什么呢:
- 程序会等待数据到达20个时才会处理
- 如果设置了HT中断,则会知道目前至少已经接受到了10个数据,不会告知剩下的4个数据。
我们必须解决这种情况!
U(S)ART
大多数 STM32 系列U(S)ART都有带空闲(IDLE )检测。 如果没有IDLE检测,其中一些具有可编程延迟的接收器超时功能。 如果这甚至不可用,则应用程序可以仅使用带有 DMA 的轮询模式,示例如下。
IDLE模式或接收超时中断,可用于判断数据有么有接收完整。
假设我们以 115200 波特接收了 10 个字节。 115200 波特的每个字节在 UART 线上大约需要 10us,总共 100us。
IDLE中断将在检测到 RX 线上的 1 个字符不活动时通知应用程序,这意味着在最后一个字符后 10us 之后。
应用程序可以对此事件做出反应并相应地处理数据。
DMA+U(S)ART
还是上述例子,程序等待接收20个字节,但实际只接收到了14个数据,如何解决这一问题。
解决方案
-
将 DMA 置于循环模式以避免 DMA 传输完成后还得手动配置。
-
申请一块大的内存,足够装下最大长度数据包
- 如果波特是11500,突发最大数据长度100,要保证可以装下这100个数据。
- 最好将接收缓冲区设置为至少 100 字节,除非可以确保处理方法比数据突发更快
- 在 115200 波特时,100 字节也就用1ms 时间就传输完成了
DMA HT/TC + U(S)ART IDEl
本节描述了可能的 4 种可能情况和另外一种情况,解释了应用程序需要 HT/TC 事件的原因
图片上使用的缩写
R: 下次读取数据的位置
W: DMA 将在内存中保存下一个字节位置
HT: 由 DMA 触发的半传输事件
TC: 由 DMA 触发的传输完成事件
IDLE: UART是否空间
DMA
- DAM采用循环模式(Circular mode)
- 20字节的数据缓存,HT事件表示已接收10个数据
会出现的情况
-
A:程序由 HT 中断通知,可以读取/处理数据,由 UART 接收
-
B:DMA 传输接下来的 10 个字节。 在这种情况下,读取/处理到最后一个数据后,因为是Circular mode所以会自动回到开始的位置。
-
C:DMA接收了10个字节,但不是对齐的,无法触发HT/TC事件。 .
- 当剩余6个数据接收完成后,才会触发TC事件
- 使用UART的IDLE检测,来接收剩余的4个字节,当IDLE中断后说明已经没有数据传输了,来完成数据接收,或者使用数据超时也可以
-
D:DMA接收了10个字节数据,不是对齐的缓存读位置在下,写位置在上。
- 通过TC事件可以接收4个字节
- 使用UART的IDLE检测,来接收剩余的6个字节,当IDLE中断后说明已经没有数据传输了,来完成数据接收,或者使用数据超时也可以
-
E:如果我们只依赖 IDLE检测。 如果在突发中接收到的字节数超过了 DMA 所能容纳的字节数,会发生什么? 在这种情况下,可以容纳 20 个字节,但是我们在突发中收到了 30 个字节如何处理呢
- IDLE事件说明已经接收完最后一帧数据。
- 数据的红色部分表示溢出前一个数据的最后一个数据 = 我们丢失了 10 个字节
- 避免采用轮询 DMA,而是 更改比接收突发数据可能发生的更快,或者通过使用 HT/TC 事件
UART IDLE 线路检测 + DMA HT&TC 中断
应用程序通过 IDLE 线路检测或 DMA TC/HT 事件获得通知
应用程序只有在收到 3 个中断中的任何一个时才必须处理数据
P:应用程序不需要轮询新的变化
P:应用程序接收事件中断
P:应用程序可能会进入低功耗模式以延长电池寿命(如果使用电池)
C:在中断中读取(处理)数据。 我们努力尽快执行中断程序
C:长时间中断执行可能会破坏应用程序中的其他兼容性
传入数据的处理来自 2 个中断向量,因此重要的是它们不会相互抢占。 将两者设置为相同的抢占优先级!
例程代码
https://github.com/MaJerle/stm32-usart-uart-dma-rx-tx