你的浏览器版本过低,可能导致网站不能正常访问!
为了你能正常使用网站功能,请使用这些浏览器。

MiniPro STM32H750 开发指南_V1.1-DMA实验

[复制链接]
STMCU小助手 发布时间:2022-10-7 20:07
DMA实验/ e' }) Y. X! l" G( _* M
本章,我们将介绍STM32H750的DMA。我们将利用DMA来实现串口数据传送,并在LCD模块上显示当前的传送进度。' S8 |3 W( `* A: S2 `
2 Z, N4 {7 j: x1 F
30.1 DMA简介9 d  U$ E  M1 Z
DMA,全称为:Direct Memory Access,即直接存储器访问。DMA传输方式无需CPU直接控制传输,也没有中断处理方式那样保留现场和恢复现场的过程,通过硬件为RAM与I/O设备开辟一条直接传送数据的通路,能使CPU的效率大为提高。6 d3 X6 T& I6 e6 b% r0 s
STM32H750内部有4个DMA控制器,它们的连接关系请参考前面第十一章的图11.3.2 STM32H7的总线架构图。图中可以看到每个域都拥有各自的 DMA控制器,D1域有1个高速主DMA(MDMA);D2 域有2个双口DMA,DMA1和DMA2;D3域有1个基本DMA(BDMA)。
) e2 D9 S9 {7 |9 X1、MDMA用于实现:内存内存、内存外设、外设内存之间的高速数据传输,MDMA支持AXI和AHBS总线之间的数据传输。注意:仅MDMA支持AHBS的访问(可访问DTCM和ITCM),其他DMA都无法访问AHBS总线。
% S# @2 K" T, D. z2、双口DMA同样可以实现:内存内存、内存外设、外设内存之间的高速数据传输,双口DMA支持AHB外设之间的数据传输,具有独立的FIFO,支持不同位宽的数据传输。
, z9 x% W6 v- ^3、BDMA用于实现:内存内存、内存外设、外设内存、外设到外设之间的高速数据传输。
/ T+ u  S* m$ P# C3 n$ V本章我们用到的是双口DMA,因此接下来,我们重点介绍双口DMA。
* m9 J6 D1 X  V, j7 vSTM32H750内部有2个双口DMA控制器(DMA1和DMA2),共16个数据流(每个控制器8个),每一个双口DMA控制器都用于管理一个或多个外设的存储器访问请求。每个数据流可以有多达115个通道(或称请求)。每个数据流通道都有一个仲裁器,用于处理DMA请求间的优先级。5 X2 k  j  c" {& [3 |' n
STM32H750的双口DMA有以下一些特性:
% o* N" ^+ Q6 f. G8 z+ G; g① 双AHB主总线架构,一个用于存储器访问,另一个用于外设访问! r* z8 l4 q9 L9 i. C4 u
② 仅支持32位访问的AHB从编程接口
8 |4 Y0 t" z! @& f7 _③ 每个DMA控制器有8个数据流,每个数据流有多达115个通道(或称请求)3 ]! z& t3 G$ l( N
④ 每个数据流有单独的四级32位先进先出存储器缓冲区(FIFO),可用于FIFO模式或直接模式
$ |8 \" |. X; w/ I9 `) p⑤ 通过硬件可以将每个数据流配置为:: v# M7 \% V8 S+ k
1,支持外设到存储器、存储器到外设和存储器到存储器传输的常规通道! z4 O; y% A8 e4 ~
2,支持在存储器方双缓冲的双缓冲区通道
$ k. E: m6 i: r% [⑥ 8个数据流中的每一个都连接到专用硬件DMA通道(请求)1 l+ E) x" O, K' s
⑦ DMA 数据流请求之间的优先级可用软件编程(4个级别:非常高、高、中、低),在软件优先级相同的情况下可以通过硬件决定优先级(例如,请求0的优先级高于请求1)+ p$ v  ~+ g# W' z  [4 w5 X
⑧ 每个数据流也支持通过软件触发存储器到存储器的传输+ @& W9 M- X7 F* ~& P# H
⑨ 每个数据流的通道请求可以由DMA请求复用器1(DMAMUX1)选择,可供选择的通道请求数多达115个。此选择可由软件配置,允许多个外设启动DMA请求
2 t9 w! ^* A& W: c2 `/ I/ Y⑩ 要传输的数据项的数目可以由DMA控制器或外设管理:
8 [; j5 r- C0 C' G1,DMA 流控制器:要传输的数据项的数目是1到65535,可用软件编程7 a' T/ N  `4 B
2,外设流控制器:要传输的数据项的数目未知并由源或目标外设控制,这些外设通过硬件发出传输结束的信号0 r& I, y2 m$ `8 d
⑪ 独立的源和目标传输宽度(字节、半字、字):源和目标的数据宽度不相等时,DMA自动封装/解封必要的传输数据来优化带宽。这个特性仅在FIFO模式下可用。8 z$ F% E8 Z8 _/ ^) a3 c
⑫ 对源和目标的增量或非增量寻址2 ?# m' k% A) B: T: a3 G
⑬ 支持4个、8个和16个节拍的增量突发传输。突发增量的大小可由软件配置,通常等于外设FIFO大小的一半/ S& ~8 k- P) x: a+ \4 i+ k
⑭ 每个数据流都支持循环缓冲区管理
, A4 F" [% u3 ?0 a⑮ 5个事件标志(DMA 半传输、DMA 传输完成、DMA 传输错误、DMA FIFO 错误、直接模式错误),进行逻辑或运算,从而产生每个数据流的单个中断请求( q, t7 r. N1 K9 H# F, g
30.1.1 DMA框图8 E) P' o: T% O6 t
STM32H750有两个双口DMA控制器,DMA1和DMA2,本章,我们仅针对DMA2进行介绍。下面先来学习双口DMA控制器框图,通过学习DMA控制器框图会有一个很好的整体掌握,同时对之后的编程也会有一个清晰的思路。STM32H750的双口MDA控制器框图如图30.1.1.1所示:
+ |, e2 F) z' Q
3 O" ~% m4 N9 M; r" ` 81fadafb240644eea4797c03ed27d3be.png
. I) s( V: [& O5 ?6 c; T) d6 I! K: d
9 w6 G" i: I3 j" I图30.1.1.1 DMA控制器框图
3 L2 K* O# P1 A* l5 ]图中,我们标记了6处位置,起作用分别是:7 n1 E* s, P5 ~$ n* ]( p
①,DMA控制器的从机编程接口,通过该接口可以对DMA的相关控制寄存器进行设置,从而配置DMA,实现不同的功能。同时,该接口可以输出dma_it[0:7]的中断信号到NVIC,以及dma_tcif[0:7]的信号到MDMA。
9 n: B- p! g% V0 W# G②,DMA控制器的外设接口,用于访问相关外设,特别的,当外设接口设置的访问地址是内存地址的时候,DMA就可以工作在内存到内存模式了。/ [8 i* Z7 D6 {( X
③,DMA控制器的FIFO区,每个数据流(总共8个数据流)都有一个独立的FIFO,可以实现存储器接口到外设接口之间的数据长度非对齐传输。+ D' y; _, h6 }9 T& Y
④,DMA控制器的存储器接口,用于访问外部存储器,特别的当存储器地址是外设地址的时候,可以实现类似外设到外设的传输效果。. o$ j" l: ]& A0 R+ I$ f
⑤,DMA控制器的仲裁器,用于仲裁数据流0~7的请求优先级,保证数据有序传输。
5 W( q2 V6 [" w4 R& q/ F⑥,这是DMA控制器数据流0~7的通道请求信号,由DMAMUX1的选择,每个数据流有多达115个通道请求可以选择。我们必须根据实际需求来选择对应的通道请求。' x4 P& Z, l) D0 Z
这里重点介绍一下DMAMUX1,其全称是DMA请求复用器1,用于管理DMA1和DMA2的通道请求,而DMAMUX2则用来管理BDMA的通道请求,这里重点介绍DMAMUX1。
/ y  @- g# l6 {) Z; KDMAMUX1总共有16个通道,其中通道07对应DMA1的数据流07,通道815对应DMA2的数据流07。DMAMUX框图如图30.1.1.2所示:
5 o9 `" I  i" t+ q. y! y" H5 r0 `6 \, z8 `5 |  D- l8 {
fac0507b58bb459cadedae7e64f94b43.png
' w, ]) k. i  X1 e2 ?" Y+ x  u+ y) v4 M* f4 c2 H( v+ W, {) T4 ~) J+ U/ O
图30.1.1.2 DMAMUX控制器框图
& q! b5 \: V; a2 R整个DMAMUX的功能比较复杂,包括:选择具体的DMA请求通道(源)、同步控制、请求生成、请求计数和中断等,本章我们只用到其最简单的应用:选择具体的DMA请求通道,即通过DMAMUX1选择DMA1/DMA2的数据流通道(请求通道)。为了方便说明,我们在图中标出了几个关键点:( P/ v' e1 r5 d7 A1 ^  x
①,外设请求,即STM32H7芯片内部外设的请求,这部分总共有p+1个外设请求,对于DMAMUX1来说,总共有107个(p=106)。具体的对应关系详见:《STM32H7xx参考手册_V7(英文版).pdf》第694页,Table 120。
* Y" c# k& n7 C+ s8 p②,通道选择,通过DMAMUX_CmCR(对DMAMUX1来说,m=0~15,该寄存器也写作:DMAMUX_CCRm,下同)寄存器的DMAREQ_ID[7:0]位来选择DMA的具体请求通道,总共有115个通道,其中①处有107个,还有8个通道来自DMAMUX内部的请求生成器,我们一般用①处的107个请求通道。/ Z5 D& j2 n; _) F  P$ G
③,同步控制,用于同步DMA请求,最终输出给DMA控制器,我们本章不使用同步控制,因此可以关闭同步(通过DMAMUX_CmCR寄存器的SE位设置)。9 l( |8 J8 y/ R# ^5 O
④,输出到DMA控制器的请求信号,对DMAMUX1来说总共有16个输出请求信号(m=15),分别对应DMA1和DMA2的数据流0~7。$ q+ @: i7 G( S0 p0 C' x
因此,我们一般只需要通过设置DMAMUX1通道m的DMAMUX1_CmCR寄存器来选择输入通道,即可完成DMA1/DMA2某个数据流的DMA输入请求通道选择。  j+ g6 ~& [4 R! M$ Y4 Y/ \
举例来说,假设我们要设置DMA2数据流3的DMA输入请求通道为:串口7的TX(UART7_TX),则:) K: U: W0 _" t% R! L
1,DMA2数据流3对应DMAMUX1的通道11(从0开始算起)。9 Z4 f+ b' q1 @/ e+ F
2,串口7的TX对应的外设DMA请求编号为:80(查《STM32H7xx参考手册_V7(英文版).pdf》第696页Table 121)。
& s) E8 R3 Y& o, r/ C3 u9 x2 R1 f! D因此,我们只需要设置DMAMUX1_CCR11的DMAREQ_ID[7:0]位为80即可完成DMA2数据流3的请求来自UART7_TX的设置。: D" u  b; d5 ^
DMAMUX1的DMA请求资源分配表,如表30.1.1.1所示:5 y% A+ `1 _# P* U9 x& B8 w
$ U' @  }4 L5 G2 O9 |1 M9 j
633d844fad7a4e2d8e91e6ea43a82b42.png ! Z/ Y; o8 {$ U! Q& F7 N6 T8 j, X

; m( C: t% S6 v* E+ g% T, S表30.1.1.1 DMAMUX1 DMA请求资源分配表(部分)' M8 H5 a# v+ A0 I% {
由表可知,DMAMUX1的DMA请求输入总共有115个,表中我们省略了一部分内容,详见:《STM32H7xx参考手册_V7(英文版)》第696页Table 120和Table 121。, a* v' y% a- `' v7 v
由于DMA1/DMA2数据流0~7的DMA请求全部是来自DMAMUX1的设置(16个通道),DMAMUX1的所有通道都可以独立设置,因此,对于STM32H7来说,DMA1/DMA2的任何一个数据流的请求都可以来自115个通道的任意一个,因此相对于STM32其他系列来说,STM32H7的DMA配置更加灵活,无需固定通道对应固定外设请求,可以随意设置。
1 z1 ~7 a6 Z( ]- {  r9 |30.1.2 DMA寄存器
5 C+ ^5 J* [2 ODMAMUX1请求线复用器通道x配置寄存器(DMAMUX1_CxCR),(x = 0到15)
& O' s+ D& w' o2 D3 pDMAMUX1请求线复用器通道x配置寄存器描述如图30.1.2.1所示:; W/ J6 a# a3 u" h

8 C: R5 Q- J& h) A5 d 182592e40b75411d9ca77f20bf3bc193.png ' m! L& y* E- |. z2 S# {1 [
6 m; t) t, E" C; [) o  @7 x& M
图30.1.2.1 DMAMUX1_CxCR寄存器各位描述
$ y0 F4 Q7 ]% T本章,该寄存器我们只需要关心DMAREQ_ID[7:0]这8个位,其他位全部用不到(设置为0即可),DMAREQ_ID[7:0]用于选择输出通道x的DMA输入请求,我们需要通过这8个位选择DMA1/DMA2数据流的请求源。请求列表见表30.1.1(完整的表见《STM32H7xx参考手册_V7(英文版)》第696页Table 120和Table 121)。' @1 I# ~0 G- e, E6 V& x. A, b
DMA中断状态寄存器(DMA_LISR和DMA_HISR)* D9 z) F- A' J0 X
DMA中断状态寄存器,该寄存器总共有2个:DMA_LISR和DMA_HISR,每个寄存器管理4数据流(总共8个),DMA_LISR寄存器用于管理数据流03,而DMA_HISR用于管理数据流47。这两个寄存器各位描述都完全一模一样,只是管理的数据流不一样。: O  P) q8 U: u& d# G
这里我们仅以DMA_LISR寄存器为例进行介绍,DMA_LISR各位描述如图30.1.2.2所示:
5 W' D1 y0 {( o: @8 P  \5 e5 g7 P7 m
500de946a55a4e1982c4239a11ace7d7.png
/ k5 P* c* V; B* s8 ]+ d' w7 B
0 [* Q1 T' J/ I+ m+ ]图30.1.2.2 DMA_LISR寄存器$ q+ Q5 r  ?6 Q; M' [
如果开启了DMA_LISR中这些位对应的中断,则在达到条件后就会跳到中断服务函数里面去,即使没开启,我们也可以通过查询这些位来获得当前DMA传输的状态。这里我们常用的是TCIFx位,即数据流x的DMA传输完成与否标志。注意此寄存器为只读寄存器,所以在这些位被置位之后,只能通过其他的操作来清除。DMA_HISR寄存器各位描述通DMA_LISR寄存器各位描述完全一样,只是对应数据流4~7,这里我们就不列出来了。
* x4 C( ^" K: F) HDMA中断标志清除寄存器(DMA_LIFCR和DMA_HIFCR)! K! i# }) i8 j2 i
DMA中断标志清除寄存器, 该寄存器同样有2个:DMA_LIFCR和DMA_HIFCR,同样是每个寄存器控制4个数据流,DMA_LIFCR寄存器用于管理数据流0~3,而DMA_ HIFCR用于管理数据流4~7。这两个寄存器各位描述都完全一模一样,只是管理的数据流不一样。
- t' B/ s1 a- n# _% O1 D- x% t8 b2 A这里,我们仅以DMA_LIFCR寄存器为例进行介绍,DMA_LIFCR各位描述如图30.1.2.3所示:
0 `/ [( E) ^$ o: N$ j& y% G5 m$ j$ |# k5 i" h5 M/ }6 h
4c3d3efcd5b24658b98f1b1e75d46020.png
& R+ W: p( b$ }, H& v3 B$ A5 l" a+ F' K+ c( U) ^
图30.1.2.3 DMA_LIFCR寄存器/ B/ f" S# Z3 f" ~1 U3 x( o" @
DMA_LIFCR的各位就是用来清除DMA_LISR的对应位的,通过写1清除。在DMA_LISR被置位后,我们必须通过向该位寄存器对应的位写入1来清除。DMA_HIFCR的使用同DMA_LIFCR类似,这里就不做介绍了。
! C2 @9 X0 S) J7 x) H9 ?第四个是DMA数据流x配置寄存器(DMA_SxCR)(x=0~7,下同)。该寄存器的我们在这里就不贴出来了,见《STM32H7xx参考手册_V3(中文版).pdf》第565页15.5.5小节。该寄存器控制着DMA的很多相关信息,包括数据宽度、外设及存储器的宽度、优先级、增量模式、传输方向、中断允许、使能等都是通过该寄存器来设置的。所以DMA_ SxCR是DMA传输的核心控制寄存器。( r0 z$ V+ K, c
第五个是DMA数据流x数据项数寄存器(DMA_SxNDTR)。这个寄存器控制DMA数据流x的每次传输所要传输的数据量。其设置范围为0~65535。并且该寄存器的值会随着传输的进行而减少,当该寄存器的值为0的时候就代表此次数据传输已经全部发送完成了。所以可以通过这个寄存器的值来知道当前DMA传输的进度。特别注意,这里是数据项数目,而不是指的字节数。比如设置数据位宽为16位,那么传输一次(一个项)就是2个字节。
- h9 [4 \% r: L# K- R' h5 A- V第六个是DMA数据流x的外设地址寄存器(DMA_SxPAR)。该寄存器用来存储STM32H750外设的地址,比如我们使用串口1,那么该寄存器必须写入0x40011028(其实就是&USART1_TDR)。如果使用其他外设,就修改成相应外设的地址就行了。
* D) z6 ]+ g( M) T最后一个是DMA数据流x的存储器地址寄存器,由于STM32H750的DMA支持双缓存,所以存储器地址寄存器有两个:DMA_SxM0AR和DMA_SxM1AR,其中DMA_SxM1AR仅在双缓冲模式下,才有效。本章我们没用到双缓冲模式,所以存储器地址寄存器就是:DMA_SxM0AR,该寄存器和DMA_CPARx差不多,但是是用来放存储器的地址的。比如我们使用SendBuf[7800]数组来做存储器,那么我们在DMA_SxM0AR中写入&SendBuff就可以了。& w& d8 s# O3 E- c: f
30.2 硬件设计; @* r9 x" `6 s, L
1.例程功能
" l+ [/ D1 @$ `1 W每按下按键KEY0,串口1就会以DMA方式发送数据,同时在LCD上面显示传送进度。打开串口调试助手,可以收到DMA发送的内容。LED0闪烁用于提示程序正在运行。/ x2 x0 v' [: E3 ~
2.硬件资源
6 C4 O( }# J7 A1)RGB灯
1 I* G0 _3 }" O: \  URED : LED0 - PB4' _, m6 {3 h  G7 q/ |/ N8 y
2)独立按键 KEY0 - PA1! ]5 F+ j* S; I0 ]" g/ s
3)串口1(PA9/PA10连接在板载USB转串口芯片CH340上面)
. G- r& i2 j! w( B2 t  f9 F! d6 q5 N9 U4)正点原子2.8/3.5/4.3/7/10寸TFTLCD模块(仅限MCU屏,16位8080并口驱动)
# K/ |& P) @+ A! _5)DMA
2 f  i! j6 F- q3.原理图/ Z' U# Q2 U; q8 m# G3 x
DMA属于STM32H750内部资源,通过软件设置好就可以了。本实验通过按键KEY0触发使用DMA的方式向串口发送数据,LCD显示传送进度,通过串口上位机可以看到传输的内容。
8 \- m  H* d9 @8 {
% r# r0 A- j0 u30.3 程序设计
2 B+ Q, l, X# \. Z7 F- z6 p30.3.1 DMA的HAL库驱动
6 @( r2 |8 }/ @& F1 H( DDMA在HAL库中的驱动代码在stm32h7xx_hal_dma.c文件(及其头文件)中。0 E0 m5 O5 u7 D0 e3 v+ h
4.HAL_DMA_Init函数& U( }1 z* E# T& o6 M
DMA的初始化函数,其声明如下:: w$ C! F! m* X% z; w3 r/ O2 \" [0 o
HAL_StatusTypeDef HAL_DMA_Init(DMA_HandleTypeDef *hdma);- O! Y( u- R9 M' {6 m' _( |
函数描述:: z$ N. ?* S& F7 S
用于初始化DMA1,DMA2和BDMA。& ]) V$ i& g& [
函数形参:4 ]$ _% `' }  a0 q6 a. b) |
形参1是DMA_HandleTypeDef结构体类型指针变量,其定义如下:$ r  z& n6 S  ~: V: w& u
  1. typedef struct __DMA_HandleTypeDef
    : D* c/ c: e7 _; x5 J
  2. {( r  \$ x5 U4 `+ h
  3.   void                              *Instance;               /* 寄存器基地址 */. n2 g; C# `6 o8 t, D' w8 B
  4.   DMA_InitTypeDef                 Init;             /* DAM通信参数 */
    ( |& h* }  g2 G2 e4 s: n( F
  5.   HAL_LockTypeDef                 Lock;             /* DMA锁对象 */
    & o6 B6 X: n7 a- Y" a& y
  6.   __IO HAL_DMA_StateTypeDef     State;                  /* DMA传输状态 */6 G9 p" Q' B1 N. C7 u0 a! p9 s6 ~
  7.   void                              *Parent;                 /* 父对象状态,HAL库处理的中间变量 */  K  A% ?9 {# H
  8.   void (*XferCpltCallback)( struct __DMA_HandleTypeDef *hdma);/*DMA传输完成回调*/
    ' `/ Q- \9 I, s
  9.   void (* XferHalfCpltCallback)( struct __DMA_HandleTypeDef * hdma);  % C; o# g% d+ |; z; h7 \" x
  10. /* DMA一半传输完成回调 */+ N& ]2 }, i/ W( H) R; R& [
  11.   void (* XferM1CpltCallback)( struct __DMA_HandleTypeDef * hdma);   $ D5 N( E( s% I
  12.         /* DMA传输完整的Memory1回调 */) r- ?, p8 h$ C" _- u
  13.   void (* XferM1HalfCpltCallback)( struct __DMA_HandleTypeDef * hdma);  & D9 K9 [) A+ `' _/ {
  14. /* DMA传输半完全内存回调 */
    + V$ {9 e. f. x4 q8 N/ x; o/ Y
  15.   void (* XferErrorCallback)( struct __DMA_HandleTypeDef * hdma);7 {' W+ e5 \, d4 c) w. S$ \& {( k, n9 I
  16. /*DMA传输错误回调*/
    , a. A" N& N; B# x' t4 j
  17.   void (* XferAbortCallback)( struct __DMA_HandleTypeDef * hdma);+ U1 @- `; `7 g* F$ t9 B7 G. q
  18. /* DMA传输中止回调 */
    / k( a/ q+ i$ o
  19. __IO uint32_t                   ErrorCode;                      /* DMA存取错误代码 */
    : w9 d$ B" y5 {5 h5 ~# R3 R9 l% j
  20. uint32_t                         StreamBaseAddress;              /* DMA数据流基地址 */4 x  m$ O+ e( u: e. Z6 [
  21. uint32_t                         StreamIndex;                     /* DMA数据流索引号 */( S8 }; B/ R' z1 s7 _/ p0 c! d$ g
  22. DMAMUX_Channel_TypeDef        *DMAmuxChannel;                  /* DMAMUX信道基础地址 */
    3 |% i; i+ r2 c3 t
  23. DMAMUX_ChannelStatus_TypeDef *DMAmuxChannelStatus;          /* DMAMUX通道状态基础地址*/
    " V" y' ~! [6 f. |) B; t0 f
  24. uint32_t                         DMAmuxChannelStatusMask;        /* DMAMUX通道状态掩码 */
    ) G  q% q1 q$ H, w9 J' i
  25. DMAMUX_RequestGen_TypeDef     *DMAmuxRequestGen;/* DMAMUX请求生成器基地地址 */
    ( _) U$ e3 H$ h
  26. DMAMUX_RequestGenStatus_TypeDef  *DMAmuxRequestGenStatus;1 [  F2 y1 a% ?6 O9 V4 c
  27. /* DMAMUX请求生成器状态地址 */
    7 F( G& r8 k; Y4 r5 ?$ U% J) w
  28. uint32_t             DMAmuxRequestGenStatusMask;  /* DMAMUX请求生成器状态掩码 */) v( P( c: U2 Z
  29. }DMA_HandleTypeDef;
复制代码
* R5 f( k. w! E# Z# e' _9 {
这个结构体内容比较多,上面已注释中文翻译,下面列出几个成员说明一下。/ _) n) {2 A' Q# i1 X
Instance:是用来设置寄存器基地址,例如要设置为DMA2的数据流7,那么取值为DMA2_Stream7。1 J" {0 {  c8 a# Z# z: w
Parent:是HAL库处理中间变量,用来指向DMA通道外设句柄。
( ^/ _) E" J* d: p) z3 Y9 HStreamBaseAddress和StreamIndex是数据流基地址和索引号,这个是HAL库处理的时候会自动计算,用户无需设置。
0 O) e0 i$ V! w' ?: ~* z' s  V其他成员变量是HAL库处理过程状态标识变量,这里就不做过多讲解。) X/ r; B; Q; J
接下来我们重点介绍Init,它是DMA_InitTypeDef结构体类型变量,该结构体定义如下:4 L0 B0 Q* \, D! n  K) G8 g
2 S- x4 p! ]! j4 W. ^' o. x
  1. typedef struct
    7 K# v& P5 e0 l4 }* C5 F& g- l
  2. {
    % y3 u3 e+ }0 \; |4 H3 y8 _2 b
  3.   uint32_t Request;                      /* 请求设置,设置是哪个外设请求的 */     , e- q+ B- |3 t; E7 S
  4.   uint32_t Direction;                    /* 传输方向,例如存储器到外设DMA_MEMORY_TO_PERIPH */ 6 w/ v! q/ p7 W/ S* W
  5.   uint32_t PeriphInc;                    /* 外设(非)增量模式,非增量模式DMA_PINC_DISABLE */  
    4 s# P6 |" l4 o( ]1 X. |) y
  6.   uint32_t MemInc;                       /* 存储器(非)增量模式,增量模式DMA_MINC_ENABLE */  
    + H7 K& I. Q3 w- u, \
  7.   uint32_t PeriphDataAlignment;        /* 外设数据大小:8/16/32位 */- U' R. Z) a  L/ \6 V8 ?/ K( l- Y
  8.   uint32_t MemDataAlignment;           /* 存储器数据大小:8/16/32位 */: `: d, [# D1 F8 L4 |
  9.   uint32_t Mode;                         /* 模式:外设流控模式/循环模式/普通模式 */    6 o$ y, }7 ^) o  j% h/ I4 R
  10.   uint32_t Priority;                   /* DMA优先级:低/中/高/非常高 */
    - o/ c' V# z6 R2 i. P
  11.   uint32_t FIFOMode;                    /* FIFO模式开启或者禁止 */
    9 W' l/ h7 t! b
  12.   uint32_t FIFOThreshold;             /* FIFO阈值选择 */; T4 H3 m) o- ?" E
  13.   uint32_t MemBurst;                    /* 存储器突发模式:单次/4个节拍/8个节拍/16个节拍 */  
    4 j: c/ p. o1 y+ O4 B
  14.   uint32_t PeriphBurst;                 /* 外设突发模式:单次/4个节拍/8个节拍/16个节拍 */    1 C( B: K9 I0 `$ ^. j8 D: ^* }
  15. }DMA_InitTypeDef;
复制代码
1 g7 Y2 s+ Z4 g  J. N+ l! A" ~
该结构体成员变量非常多,但是每个成员变量配置的基本都是DMA_SxCR寄存器和DMA_SxFCR寄存器的相应位。; c" p1 x9 {+ X5 [
函数返回值:
; e( v1 }# S5 }9 H2 AHAL_StatusTypeDef枚举类型的值。% X, G+ f" u# ]2 W7 v8 i4 N
以DMA的方式传输串口数据配置步骤
3 p& a2 h& o3 v8 c1)使能DMA时钟  O! Y1 {% l( \
DMA的时钟使能是通过AHB1ENR寄存器来控制的,这里我们要先使能时钟,才可以配置DMA相关寄存器。HAL库方法为:
1 I; \# I9 t, F__HAL_RCC_DMA1_CLK_ENABLE(); /* DMA1时钟使能 /
1 A5 y. p( \  E% @8 M, v  c__HAL_RCC_DMA2_CLK_ENABLE(); / DMA2时钟使能 */
' a4 k2 h3 T1 H4 j2)初始化DMA
! D! t& L4 ]4 C" ]" d调用HAL_DMA_Init函数初始化DMA的相关参数,包括配置通道,外设地址,存储器地址,传输数据量等。
  A) c; v1 N8 V& W$ UHAL库为了处理各类外设的DMA请求,在调用相关函数之前,需要调用一个宏定义标识符,来连接DMA和外设句柄。例如要使用串口DMA发送,所以方式为:
) e. J1 P& O4 ]3 T__HAL_LINKDMA(&g_uart1_handle, hdmatx, g_dma_handle);
( v) e+ m* ?8 P! s! o! g+ X其中g_uart1_handle是串口初始化句柄,我们在usart.c中定义过了。g_dma_handle是DMA初始化句柄。hdmatx是外设句柄结构体的成员变量,在这里实际就是g_uart1_handle的成员变量。在HAL库中,任何一个可以使用DMA的外设,它的初始化结构体句柄都会有一个DMA_HandleTypeDef指针类型的成员变量,是HAL库用来做相关指向的。hdmatx就是DMA_HandleTypeDef结构体指针类型。" p5 d  I# h& M* D- ?/ {
这句话的含义就是把g_uart1_handle句柄的成员变量hdmatx和DMA句柄g_dma_handle连接起来,是纯软件处理,没有任何硬件操作。( `$ X3 |% a0 b, @* E
这里我们就点到为止,如果大家要详细了解HAL库指向关系,请查看本实验宏定义标识符__HAL_LINKDMA的定义和调用方法,就会很清楚了。/ p& N! N; N/ u
3)使能串口的DMA发送,启动传输: L+ ^" k' T& m
串口1的DMA发送实际是串口控制寄存器CR3的位7来控制的,在HAL库中操作该寄存器来使能串口DMA发送的函数为HAL_UART_Transmit_DMA。
0 W4 _. k1 o) N& d  P9 `这里大家需要注意,调用该函数后会开启相应的DMA中断,对于本章实验,我们是通过查询的方法获取数据传输状态,所以并没有做中断相关处理,也没有编写中断服务函数。; B; u! X* i; X* A
HAL库还提供了对串口的DMA发送的停止,暂停,继续等操作函数:4 h6 g. @* ?$ t! p. G# t
HAL_StatusTypeDef HAL_UART_DMAStop(UART_HandleTypeDef huart); / 停止 */
9 T' ~. b7 d* B+ L0 qHAL_StatusTypeDef HAL_UART_DMAPause(UART_HandleTypeDef huart); / 暂停 */- P6 T  E0 J% }! s7 \& G! u4 b7 k/ U
HAL_StatusTypeDef HAL_UART_DMAResume(UART_HandleTypeDef huart); / 恢复 */- r2 D( p* P2 A, Y
4)查询DMA传输状态, _2 d. i- x7 v9 y" K8 c  m
在DMA传输过程中,我们要查询DMA传输通道的状态,使用的方法是:
- R" Q0 H; W: c6 p* m. ^__HAL_DMA_GET_FLAG(&g_dma_handle, DMA_FLAG_TCIF3_7);
1 `7 y( q' n6 {* T获取当前传输剩余数据量:% N& P% g9 v! I& V( \
__HAL_DMA_GET_COUNTER(&g_dma_handle);$ S: p: P& L" A; x* Z
同样,我们也可以设置对应的DMA数据流传输的数据量大小,函数为:
. N4 [: P. e3 f  T. D__HAL_DMA_SET_COUNTER (&g_dma_handle, 1000);7 W2 V; ~/ R; l* a# E- B8 n
DMA相关的库函数我们就讲解到这里,大家可以查看固件库中文手册详细了解。" i/ P0 T2 k7 h8 j) _
5)DMA中断使用方法
$ h% o0 X* h7 s" k5 QDMA中断对于每个数据流都有一个中断服务函数,比如DMA2_Stream7的中断服务函数为DMA2_Stream7_IRQHandler。HAL库提供了通用DMA中断处理函数HAL_DMA_IRQHandler,在该函数内部,会对DMA传输状态进行分析,然后调用相应的中断处理回调函数:! ~: Q. N* H1 r% k! g) v/ |
void HAL_UART_TxCpltCallback(UART_HandleTypeDef huart); / 发送完成回调函数 */% S$ H  g4 M1 t; U9 b8 W
void HAL_UART_TxHalfCpltCallback(UART_HandleTypeDef huart);/ 发送一半回调函数 */% S" O  H( l3 d$ v) `4 |( o: w
void HAL_UART_RxCpltCallback(UART_HandleTypeDef huart); / 接收完成回调函数 */
  ^* p, R% D& Z$ O# rvoid HAL_UART_RxHalfCpltCallback(UART_HandleTypeDef huart);/ 接收一半回调函数 */
' |1 s6 l# {: e4 F( @void HAL_UART_ErrorCallback(UART_HandleTypeDef huart); / 传输出错回调函数 */
8 z9 `; C) V. n. n; g* C* \30.3.2 程序流程图& m: R5 z# [4 W  |* l) \
% M, z  @3 d- m( e/ K
1cea58d87a1746b2bf7f2e0ba3e26e77.png ! {: ~2 y- v1 O" ^3 q
- p! |. O  W5 ?9 C3 m& ?* l0 {5 k
图30.3.2.1 DMA实验程序流程图
6 h( {1 _* O, c% p7 Q& _( u* ^
30.3.3 程序解析
8 V; z6 P( c$ @1 C6 C' N1.DMA驱动代码; g# g; F1 ]0 ?2 T" t
这里我们只讲解核心代码,详细的源码请大家参考光盘本实验对应源码。DMA驱动源码包括两个文件:dma.c和dma.h。. N- D* W1 Y: N' G. ]) x
dma.h头文件只有函数的声明,下面我们直接介绍dma.c的程序,下面是DMA的初始化函数,其定义如下:
# j7 }8 U) v1 ?! c- W! w( s  E
  1. /**
    / P$ D7 Q+ B( C3 \7 }1 a) f
  2. * @brief                   串口TX DMA初始化函数' `2 i9 R; {! x, [7 e
  3. *   @note            这里的传输形式是固定的, 这点要根据不同的情况来修改3 A; b2 ^$ r& T6 q2 `; d& Q
  4. *                      从存储器 -> 外设模式/8位数据宽度/存储器增量模式
    & y6 ]  T9 s! n+ ^5 M3 u
  5. *
    ; d) K, p8 M4 P6 H' X2 L! m' ?
  6. * @param               dma_stream_handle : DMA数据流,DMA1_Stream0~7/DMA2_Stream0~7
    : Q0 X+ g. E/ k( k& b
  7. * @retval              无
    * G, ?' B& m5 T
  8. */$ M3 D9 B2 x5 J5 e
  9. void dma_init(DMA_Stream_TypeDef *dma_stream_handle, uint32_t ch)
    ! n5 R9 p1 M4 q% b; c- G5 d' ~
  10. { % e$ `8 h/ \8 b1 F9 N( b
  11. /* 得到当前stream是属于DMA2还是DMA1 */
    1 r+ ^1 p0 w( c/ T) {
  12.     if ((uint32_t)dma_stream_handle > (uint32_t)DMA2)     % G( _1 f, {9 r
  13.     {6 ^6 o' |" U* t7 T7 g
  14.         __HAL_RCC_DMA2_CLK_ENABLE();                      /* DMA2时钟使能 */
    : c+ v; ?# e: T
  15.     }. k; l, [# d9 ^" A& a1 m( k" n
  16.     else
    9 [0 \* v; v, u4 g+ ~) p: o: s. U
  17.     {+ F- y4 Z& J6 {" Z  \
  18.         __HAL_RCC_DMA1_CLK_ENABLE();                      /* DMA1时钟使能 */7 Z! J" _; ?$ S7 _' j
  19.     }" p, l, D6 m1 A# g" `/ L
  20. /* 将DMA与USART1联系起来(发送DMA) */
    " L5 c. L2 L7 h4 o7 c% m
  21.     __HAL_LINKDMA(&g_uart1_handle,hdmatx, g_dma_handle);  7 F" r2 t; S$ R$ n5 H3 V
  22. 0 a% K/ m8 {) R) d
  23.     /* Tx DMA配置 *// x# v  e4 t$ O$ [" {4 F( @% c
  24.     g_dma_handle.Instance = dma_stream_handle;                           /* 数据流选择 */5 X" Y% J& Y- v
  25.     g_dma_handle.Init.Request = ch;                                        /* DMA通道选择 */+ {. r( o8 b( I) E& z) E) ?8 g2 |
  26.     g_dma_handle.Init.Direction = DMA_MEMORY_TO_PERIPH;                /* 存储器到外设 */
    . X% z& ]! l, v; T8 A
  27.     g_dma_handle.Init.PeriphInc = DMA_PINC_DISABLE;                     /* 外设非增量模式 */7 H2 Z4 c) G$ Z7 v
  28.     g_dma_handle.Init.MemInc = DMA_MINC_ENABLE;                          /* 存储器增量模式 */
    1 y+ W, M9 L! y9 S3 V" T
  29.     g_dma_handle.Init.PeriphDataAlignment=DMA_PDATAALIGN_BYTE;/*外设数据长度:8位*/
    4 q1 o0 L9 F: T
  30.     g_dma_handle.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE/*存储器数据长度:8位*/% A9 I/ n& I; u5 Y" ~
  31.     g_dma_handle.Init.Mode = DMA_NORMAL;                                /* 外设流控模式 */4 a' s9 B( @8 Y0 _
  32.     g_dma_handle.Init.Priority = DMA_PRIORITY_MEDIUM;                /* 中等优先级 */4 {! X* {# {5 z. q
  33.     g_dma_handle.Init.FIFOMode = DMA_FIFOMODE_DISABLE;               /* 关闭FIFO模式 */
    4 P1 J5 W& i7 J9 h" C+ _
  34.     g_dma_handle.Init.FIFOThreshold = DMA_FIFO_THRESHOLD_FULL;        /* FIFO阈值配置 */1 n1 n  s! Q& W% I4 z
  35.     g_dma_handle.Init.MemBurst = DMA_MBURST_SINGLE;                 /* 存储器突发单次传输 */% |; m! _2 V  D6 |0 t
  36.     g_dma_handle.Init.PeriphBurst = DMA_PBURST_SINGLE;             /* 外设突发单次传输 */3 F+ _' B5 {% ]: Y) w9 E; g2 i

  37. 7 E' j( V% s, Z5 r$ |- R
  38.     HAL_DMA_Init(&g_dma_handle);0 a) [+ {/ d/ a5 D" G6 p6 c% ?3 P
  39. }
复制代码

. N8 J. O! e8 z# D该函数是一个通用的DMA配置函数,DMA1/DMA2的所有通道,都可以利用该函数配置,不过有些固定参数可能要适当修改(比如位宽,传输方向等)。该函数在外部只能修改DMA数据流编号和通道号,更多的其他设置只能在该函数内部修改。对照前面的配置步骤的详细讲解来分析这部分代码即可。8 _6 P& W5 \4 a# K' z0 \
2. main.c代码; C. S3 V; [! ^
在main.c里面编写如下代码:
2 r/ U5 ]; V6 \8 E$ R3 B- P! y9 D; M" T
  1. /* 要循环发送的字符串 */
    ( H7 y1 s+ I! d# J9 V4 s4 L6 r
  2. const uint8_t TEXT_TO_SEND[] = {"正点原子 MiniPRO STM32H7 DMA 串口实验"};   ' |, E# g, |9 |: F1 h0 {/ H
  3. #define SEND_BUF_SIZE       (sizeof(TEXT_TO_SEND) + 2) * 200   /* 发送数据长度 */
    8 k$ Z! H0 {. b" t
  4. uint8_t g_sendbuf[SEND_BUF_SIZE];                       /* 发送数据缓冲区 */
    - t  m/ C, s' F

  5. ; r+ J- \$ H- K. t# u( P* V1 v
  6. int main(void)
    # |& V& t; B% C6 y: [( V7 X
  7. {# \; x6 z2 J7 `9 D: t! O5 J3 Q
  8.     uint8_t  key = 0;$ O, x4 u0 A& H. H* s# F
  9.     uint16_t i, k;0 Q: N7 B6 V" V; N( O/ A) g
  10.     uint16_t len;/ y6 k4 w/ U: {
  11.     uint8_t  mask = 0;
    . U) W) U' o6 W1 g' A
  12.     float pro = 0;                                  /* 进度 */
    ' }" G' c. M: e9 d/ G
  13. % |) c) S  N  A$ E4 F
  14.     sys_cache_enable();                            /* 打开L1-Cache */
    6 m/ `4 V( Z. U8 T9 ]- [) e7 z5 J0 P0 @
  15.     HAL_Init();                                      /* 初始化HAL库 */
    , ^( h* F7 P: `
  16.     sys_stm32_clock_init(240, 2, 2, 4);         /* 设置时钟, 480Mhz */
    , N# R# V' g/ M8 [' b1 C) q0 P
  17.     delay_init(480);                                /* 延时初始化 */0 ~* d( A, f  t- Y% \5 |# m2 d
  18.     usart_init(115200);                            /* 串口初始化为115200 */
    1 L9 C: Z$ W5 z( U3 z1 K3 ?# U! v' n
  19.     mpu_memory_protection();                      /* 保护相关存储区域 */
    6 ~) w( |& S+ h4 q
  20.     led_init();                                      /* 初始化LED */
    / J# S" p  q6 c! q, U4 n4 K
  21.     lcd_init();                                      /* 初始化LCD */
    : K) p" F# Q% I0 {, }$ `+ w
  22.     key_init();                                      /* 初始化按键 */0 q! c. ?, ^8 T; O
  23. & x7 B# ~( E4 f. J* f! W+ h
  24.     dma_init(DMA2_Stream7, DMA_REQUEST_USART1_TX);  /* 初始化DMA */9 i5 v8 `$ z. {* {+ W& a8 a
  25. 6 I5 |$ n& X# M% ^' M  V
  26.     lcd_show_string(30, 50, 200, 16, 16, "STM32", RED);
    2 v& S1 D. }7 H# [2 z3 b) Y
  27.     lcd_show_string(30, 70, 200, 16, 16, "DMA TEST", RED);
    5 m) Z9 l2 p. S- J) B
  28.     lcd_show_string(30, 90, 200, 16, 16, "ATOM@ALIENTEK", RED);' k" l' W& {" W6 x1 F+ p
  29.     lcd_show_string(30, 110, 200, 16, 16, "KEY0:Start", RED);
    2 E; D; m  ^/ E

  30. 8 Y1 W! `* L7 k
  31.     len = sizeof(TEXT_TO_SEND);
    $ t+ [0 \5 z- r# }3 G3 x6 s9 o
  32.     k = 0;/ ^8 i+ c0 _/ u1 a
  33. 2 B2 O5 g* t( j$ {0 E+ K! u" q" y
  34.     for (i = 0; i < SEND_BUF_SIZE; i++) /* 填充ASCII字符集数据 */3 B! P+ W# V# M6 @; r. {
  35.     {! r8 C/ {# V. N% k
  36.         if (k >= len)   /* 入换行符 */4 g' {& }# |0 S8 }! x- e
  37.         {/ w" @% a: T( j6 V" S8 f  M
  38.             if (mask)( \. u" i! l1 f! {4 |) |
  39.             {
    * b' I$ X. D2 p
  40.                 g_sendbuf<i> = 0x0a;
    - C! \; A- \, S2 b
  41.                 k = 0;. ~0 L6 \" m+ V0 U. @7 t2 s% r, N
  42.             }
    3 o! S0 g- l8 T/ a
  43.             else
    , |5 k2 s. S1 O: ?
  44.             {
    ( \2 @) ]9 O' m5 x# _' r
  45.                 g_sendbuf<i> = 0x0d;) Q  \' U' ^  {; \" C
  46.                 mask++;
    * I' @$ v6 Q: D. H7 {" ~9 Z
  47.             }% u5 @3 S2 ], Y. i1 J  S/ H
  48.         }( H: g$ C+ S2 W6 J
  49.         else     /* 复制TEXT_TO_SEND语句 */
    6 K) ]$ N% K+ g2 o
  50.         {2 P# C/ {$ h  A! J/ w
  51.             mask = 0;# n/ ]% t/ t$ C
  52.             g_sendbuf<i> = TEXT_TO_SEND[k];
    : {9 p/ @- F+ I4 P1 [4 |. a
  53.             k++;
    ) p( X# M9 S. P5 ~
  54.         }+ W) Z1 l8 h5 k' D9 w. |, |9 m) @  J# O
  55.     }
    ; m+ l( o; q5 k
  56.      i = 0;* |) d% _- U% o6 N
  57.     while (1); j- _9 S/ g0 V( O
  58.     {' m5 }2 K0 q6 M4 h- B! ?
  59.         key = key_scan(0);
    % Y0 C$ ^0 p0 Q6 e

  60. 1 \+ [/ d& C2 q' G% |  a
  61.         if (key == KEY0_PRES)   /* KEY0按下 */
    : }$ A4 [  G5 I
  62.         {
    " s  e0 ~2 d; N+ x1 O3 m* C' Q: j
  63.             printf("\r\nDMA DATA:\r\n");& ^- \  o7 u0 f+ b2 Z
  64.             lcd_show_string(30, 130, 200, 16, 16, "Start Transimit....", BLUE);
    / S- v3 W. q' Q, K3 ]% {
  65.             lcd_show_string(30, 150, 200, 16, 16, "   %", BLUE);    /* 显示百分号 */</i></i></i>
复制代码
  1. /* 开始一次DMA传输! */4 W) N% ^' B/ X8 U9 `0 P8 z7 A
  2. HAL_UART_Transmit_DMA(&g_uart1_handle,g_sendbuf,SEND_BUF_SIZE);" E7 C+ g8 c  [8 H7 _+ H

  3. 2 B$ S# p" ^" A' w; f
  4.         /* 等待DMA传输完成,此时我们来做另外一些事情,比如点灯  
    # A. S+ Y9 _( d) M( {
  5.          * 实际应用中,传输数据期间,可以执行另外的任务 */
复制代码
  1.             while (1)
    + I2 ^' |0 R: @- \7 z
  2.             {
    $ O) q9 e' K+ t- Z
  3. /* 等待DMA1_Steam7传输完成 *// E  Y0 m3 i4 E; b, m+ ?
  4.                 if (__HAL_DMA_GET_FLAG(&g_dma_handle, DMA_FLAG_TCIF3_7))
    1 W. _" Q+ j; q; A+ n$ a
  5.                 {
      u8 e8 K- `( N8 i6 K) R& T& d5 l
  6. /* 清除DMA1_Steam7传输完成标志 */
    5 q% j3 \; n0 G$ U+ k6 O
  7.                     __HAL_DMA_CLEAR_FLAG(&g_dma_handle, DMA_FLAG_TCIF3_7); 7 v5 E$ f. w% y- U9 e9 o
  8.                     HAL_UART_DMAStop(&g_uart1_handle);  /* 传输完成以后关闭串口DMA */8 r# O( s6 r1 H+ U: h% q
  9.                     break;$ G6 W5 I/ |* F) X
  10.                 }1 H  [3 V5 `! K# K, R+ B0 d
  11.                 Pro =__HAL_DMA_GET_COUNTER(&g_dma_handle);/*得到当前还剩余多少个数据*/1 h5 c" \; A4 |7 C
  12.                 len = SEND_BUF_SIZE;        /* 总长度 */
    3 a9 G1 ], g0 a2 R
  13.                 pro = 1 - (pro / len);      /* 得到百分比 *// z* d$ g# X4 j0 A
  14.                 pro *= 100;                 /* 扩大100倍 */
    % N+ B4 x# d- y. ~6 N$ `
  15.                 lcd_show_num(30, 150, pro, 3, 16, BLUE);
    % t& _! b5 L, Y0 X; F+ a; F- S7 p
  16.             } ; x1 P1 _' G$ L& @. |
  17.             lcd_show_num(30, 150, 100, 3, 16, BLUE);    /* 显示100% */
    / X) ?  S: G5 f& c/ |  P( m5 S' m
  18. /* 提示传送完成 */
    9 Q0 D7 V6 Y. r" o9 Q
  19.             lcd_show_string(30, 130, 200, 16, 16, "Transimit Finished!", BLUE);   y( R6 }7 J  ]5 E7 b8 g' ?8 Y+ A$ x
  20.         }+ }. I2 E1 W9 H: @% w

  21. 0 b- P8 q5 q* `0 x
  22.         i++;
    # @: `6 ~! l1 k* T0 x$ j
  23.         delay_ms(10);
    . p. d) k6 p) p" s' F
  24. " w8 N% s2 G7 x# O# D
  25.         if (i == 20)
    : v3 D0 v* @# R$ C3 Y
  26.         {
    8 d- b  W( L& \( {( p# [' P( {0 j% ?
  27.             LED0_TOGGLE();  /* LED0闪烁,提示系统正在运行 */
    ' M6 z6 {- l- x: d" d: @9 F
  28.             i = 0;
    - L' L# t/ j9 U' k/ Q6 p5 V
  29.         }- v7 f3 k& z# T
  30.     }
    4 j; H2 W, y# }+ N6 v7 R
  31. }
      [4 {8 ~) ~3 Z" Q
复制代码

9 k0 l; \) z2 w$ j5 [; _6 gmain函数的流程大致是:先初始化发送数据缓冲区g_sendbuf的值,然后通过KEY0开启串口DMA发送,在发送过程中,通过__HAL_DMA_GET_COUNTER(&g_dma_handle)获取当前还剩余的数据量来计算传输百分比,最后在传输结束之后清除相应标志位,提示已经传输完成。" ^, s2 \- t+ P( l. B
30.4 下载验证
/ \/ k% |, _5 }1 i将程序下载到开发板后,可以看到LED0不停的闪烁,提示程序已经在运行了。LCD显示的内容如图30.4.1所示:% B. Q* n( b: t8 g; p; p

* @: Y2 O8 U9 L* m* \/ Q1 Y b77609f35088495a8a95df9fde89b0e1.png : t- \8 L* b5 Q# A) h4 M. u$ R2 z) Z

8 B8 s/ Q0 k* W$ [图30.4.1 DMA实验测试图
+ ]1 k; k0 Y! c8 }/ V* P3 I0 L6 W# z我们打开串口调试助手,然后按KEY0,可以看到串口显示如图30.4.2所示的内容:5 m. h* A# J' @2 n

* R8 h- F  T6 T$ e: X8 W0 C3 J6 t 30577e604ee9429ca4cd3183f7f8f9c1.png ( }& Q( L9 F2 |  Y, @
3 Z8 M# h+ G. L
图30.4.2 串口收到的数据内容4 r' b  K, f" g) F
可以看到串口收到了开发板发送过来的数据,同时可以看到TFTLCD上显示了进度等信息,如图30.4.3所示:
" |* Y% Z& f, b& n3 p
( h; ?0 U( `! b' U% h7 Z# T e2ba7dbae1f845368d134d17790fd844.png % J9 P  K( B! U. c
' [1 F2 F7 j/ z( s. {, z
30.4.3 DMA串口数据传输中
* G' j& T6 r9 n) P+ F. `; J0 m至此,我们整个DMA实验就结束了,希望大家通过本章的学习,掌握STM32H750的DMA使用。DMA是个非常好的功能,它不但能减轻CPU负担,还能提高数据传输速度,合理的应用DMA,往往能让你的程序设计变得简单。3 b' s. E$ \' Z+ E
————————————————7 j3 Z9 N1 C5 [& I
版权声明:正点原子. l( L1 ~. Q3 y! X" [

9 K7 x& E7 U7 S/ Y1 R+ f: `, s( _& H- V. |) G7 v
0 M6 u2 I" A! @% X& u, S- K
收藏 评论0 发布时间:2022-10-7 20:07

举报

0个回答

所属标签

相似分享

官网相关资源

关于
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
意法半导体官网
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
官方最新发布
STM32N6 AI生态系统
STM32MCU,MPU高性能GUI
ST ACEPACK电源模块
意法半导体生物传感器
STM32Cube扩展软件包
关注我们
st-img 微信公众号
st-img 手机版