一、CubeMX的配置
% ^* d n) j3 D& i依次点击Configuration->DMA即可进入DMA口详细配置界面。
+ S2 \* D: D! g
/ S4 S- s; W; _9 U3 F0 D( j$ i% `) b; H
点击【Add】添加DMA传输请求,然后选择USART1_TX请求;( _8 A# Z1 m' e, _/ U; h' b1 Y
7 D. \8 J$ y6 C; a }7 y4 x
" G/ R8 h, x f) ?. \: S
3 Z) o6 ^$ |: s" a9 f其他设置如下即可3 K5 g9 m) A; ]% h4 Z
- [0 o0 Z# C* r8 d! ]& u
& g# U- V0 B$ Z0 l% [
% i8 q# J1 H2 w) D L! X" ]然后依旧是工程设置里,将HAL改成HAL/ A9 k$ C( p$ s+ x- z1 B9 A
* T+ \- ^! l+ |% ~
- E( } H8 ~- p# V
5 x/ g6 B% }. m! t$ g. {1 Q, e' I4 z然后便可以生成代码 G( W, r9 P) ?5 ? G5 B! ?
: C* K0 x! Q. c: d- _8 Y
! {1 f7 S" G% e5 D8 M( D! n
5 e6 _) l8 Y; C二、用户代码修改2 O, \) Q2 J. k! w
代码生成后打开工程,我们可以看到,在static void MX_USART1_UART_Init(void)函数中多了这些和USART_TX的DMA相关配置代码% s& K1 g4 ?. x3 @9 r# ^2 L5 Z+ x
5 o9 Y- s- }, m% j" d; Z8 D
- x: I1 z; }7 X' Q0 f7 }) I5 Q
+ h4 Y+ M, n0 Y4 j. o' } p
然后我们将其复制,在usart.c文件中添加static void USART1_DMA_Init(void)函数并粘贴,另外将static void MX_DMA_Init(void) 函数中的DMA时钟使能代码复制到USART1_DMA_Init()函数头部;
: E' p7 G+ b: G7 Y' B6 n+ t$ |0 x9 I- A# k
% B. G0 G q: i" P, y- f
: T4 l' H w% o- W另外添加外设和内存地址设置函数到USART1_DMA_Init()函数中,得到
/ n& _9 f. a8 {. S4 U7 _+ R2 o6 } W* ]- U- S8 A+ T
- //USART1_TX DMA Init
$ @% u8 b2 ~' h+ l- Q; C' ^ - static void USART1_DMA_Init(void)6 U* N+ r8 R* V! T2 z
- {
+ l- O0 g) `, @5 W" P% c - //使能DMA1时钟9 r$ p3 l6 Z8 }, F
- LL_AHB1_GRP1_EnableClock(LL_AHB1_GRP1_PERIPH_DMA1);) N9 q- Z2 ~% [' B' W- m0 C! U- L
- //DMA1通道4选择为USART1_TX请求5 A. Z* \8 n6 Z7 X/ W
- LL_DMA_SetPeriphRequest(DMA1, LL_DMA_CHANNEL_4, LL_DMA_REQUEST_2);
6 @ q5 M* Y' j# i5 Y, L - //数据传输方向为存储器到外设
# C/ @( L* z9 [. X! [& k6 \ - LL_DMA_SetDataTransferDirection(DMA1, LL_DMA_CHANNEL_4, LL_DMA_DIRECTION_MEMORY_TO_PERIPH);
( ]3 A( \7 z0 [ - //通道优先级
7 i, }; n6 ^. r0 I& W, f - LL_DMA_SetChannelPriorityLevel(DMA1, LL_DMA_CHANNEL_4, LL_DMA_PRIORITY_LOW);* U8 {& } g* a% ?! t9 j
- //不执行循环操作
( C" h0 t! e. E% I/ W - LL_DMA_SetMode(DMA1, LL_DMA_CHANNEL_4, LL_DMA_MODE_NORMAL);' ]7 u; A& W0 q _2 |) r: ^
- //不执行外设地址增量操作
; O3 e5 v" H% z! r" V, ` - LL_DMA_SetPeriphIncMode(DMA1, LL_DMA_CHANNEL_4, LL_DMA_PERIPH_NOINCREMENT); O) R) g* h' X) _
- //执行存储器地址增量操作) r I. G: [; W" L5 O
- LL_DMA_SetMemoryIncMode(DMA1, LL_DMA_CHANNEL_4, LL_DMA_MEMORY_INCREMENT);2 ?7 R9 ] G7 Q3 @
- //外设数据宽度$ k% ?0 v% I+ u" S
- LL_DMA_SetPeriphSize(DMA1, LL_DMA_CHANNEL_4, LL_DMA_PDATAALIGN_BYTE);2 T% u9 w. X1 H% Z4 B
- //存储器数据宽度
+ b$ z) ]9 R$ N9 p! M. y - LL_DMA_SetMemorySize(DMA1, LL_DMA_CHANNEL_4, LL_DMA_MDATAALIGN_BYTE);
5 _% ~3 Q) ~$ W2 ]& O" F - //DMA内存基地址: N# u& l) U) e+ P) g
- LL_DMA_SetMemoryAddress(DMA1, LL_DMA_CHANNEL_4,(u32)USART1_TX_BUF);
f1 p/ R8 n% k) T @* c - //DMA外设基地址
1 i) U: r1 l' K - LL_DMA_SetPeriphAddress(DMA1, LL_DMA_CHANNEL_4,(u32)&USART1->TDR);. m' h/ U5 n/ \9 Z$ m* [
- }
复制代码 $ \- q6 z( z- ?7 o. n1 @$ z0 R$ v
然后将其添加到uart_init()函数中,并添加串口1的DMA发送使能函数,如下图; q6 G0 D& a5 J9 B- M
2 n5 v! b, u3 Q# o4 N7 @
! q: L! j: O% E/ j% |0 H
: F" F% ^; ~3 u9 D可见,我们需要声明USART1_DMA_EN,我们在usart.c头部添加一下代码:0 `. D7 x9 j! T, j1 N, u
0 q- p& V' Y- c% C, m9 ?
* a; i2 C& N9 y5 x
" \* y9 i/ k9 t8 U2 k4 U- L! M然后重写一个串口1打印函数:8 p8 Z5 m$ Q9 q
5 N# j& ~7 g) Q
- //串口1,printf 函数 j- o5 { B. ^9 w% B9 F N
- //确保一次发送数据不超过USART1_TX_LEN字节9 @: `" h A: g+ H+ v6 j
- void u1_printf(char* fmt,...) ' g6 e0 B- k7 K; U7 [# v
- {
' s9 N; _$ C7 T2 p! p. X - static u8 first = 1;
: s1 a4 X5 ^* C3 u4 [' m - u16 i;
0 I9 b/ X# R& G0 Q( s2 z - va_list ap;
. {+ q0 |. ]2 ^" W( D/ @& P$ m4 k - va_start(ap,fmt);
% B+ ? J7 N6 O3 s: `) N0 {3 {8 [ -
3 O2 i3 m: A; u$ `! m; r6 k - if(first == 0)
7 T3 c3 Q- T9 L: m2 Q - while(LL_DMA_IsActiveFlag_TC4(DMA1) == RESET); //等待通道4传输完成- q1 T+ ?& s1 U& O
- else4 m' C/ O/ z# ~6 H
- first = 0;
' }+ i% v2 G4 ^' J3 I1 s -
# z( u/ p! ^% V+ H: ^* p - vsprintf((char*)USART1_TX_BUF,fmt,ap);
1 z2 K, s2 s* v" W - va_end(ap);
6 i4 e/ T" A$ T( b, Q. E - USART1_TX_BUF[USART1_TX_LEN-1] = '\0';
' ~: o& S3 o; @4 E" P) ]) X0 [" I J - i=strlen((const char*)USART1_TX_BUF); //此次发送数据的长度
' x( O, m. k5 I; N% ~
- i& T4 E. J4 O" z \- LL_DMA_ClearFlag_TC4(DMA1); //清除通道4传输完成标志
. {0 @, r9 Q3 B& w# k: A - LL_DMA_DisableChannel(DMA1,LL_DMA_CHANNEL_4 ); //关闭USART1 TX DMA1 所指示的通道
n9 Y9 [, `, ~6 L) E8 j& ? - LL_DMA_SetDataLength(DMA1,LL_DMA_CHANNEL_4,i); //DMA通道的DMA缓存的大小
9 h) Y" [. a. |8 S% c5 | - LL_DMA_EnableChannel(DMA1, LL_DMA_CHANNEL_4); //使能USART1 TX DMA1 所指示的通道
% O# K/ ^4 C) n- y" [4 z - }
复制代码 ) U1 G0 \) N& w2 O. `4 A
然后将主函数中的printf()换成u1_printf()5 p! V! m0 o7 C4 j- S+ ?# I
2 O1 S8 n9 N8 m
2 I k+ |+ }% }
$ R6 |# k$ m7 n7 @1 w$ n1 B编译下载到小熊派开发板,通过串口调试助手可以看到
) q9 D" O: r& E* Y4 L) ]
" \8 n7 a9 d6 g p! P/ Y
) ?/ O' l( N+ B6 b. L9 @( M2 g8 I' y5 J% X! w. N( X! S
三、代码的优化修改
# c8 w8 w' v5 u4 a8 ^在stm32l4xx_ll_dma.c文件中我发现了LL_DMA_DeInit()和LL_DMA_Init()两个函数,和标识外设库的及其相似,所以我想着使用这两个函数重写USART1_DMA_Init()函数,重写后如下
$ {9 O; M# ?3 r7 P: P$ t2 t, G8 B' T* Q# v( w% l1 b/ \" B
- //USART1_TX DMA Init5 G* \ R5 m- D. \) b, g
- static void USART1_DMA_Init(void)& L4 q. }' x& `9 ]0 U% M* S* t* M
- {; T; x1 ~. E6 i5 g- q
- LL_DMA_InitTypeDef DMA_InitStructure;9 [5 J5 G. [( t0 c
- 6 a+ N4 e/ z! d) e
- LL_AHB1_GRP1_EnableClock(LL_AHB1_GRP1_PERIPH_DMA1); //使能DMA1时钟
# O& K. Y. E' n3 B% S# }8 P - LL_DMA_DeInit(DMA1,LL_DMA_CHANNEL_4);//将DMA1的通道4寄存器重设为缺省值
; `; o$ F, G4 q8 z9 H+ N& g* e -
3 R. b- h6 g$ p) B" z& r3 \: X - DMA_InitStructure.PeriphRequest=LL_DMA_REQUEST_2; //选择为USART1_TX请求6 H) Y2 }8 p; I$ d4 E
- DMA_InitStructure.Priority=LL_DMA_PRIORITY_LOW; //通道优先级/ r: a5 B% ~1 Z5 C2 S. ~8 J
- DMA_InitStructure.Direction=LL_DMA_DIRECTION_MEMORY_TO_PERIPH;//数据传输方向为存储器到外设
/ q" r; e9 } S - DMA_InitStructure.MemoryOrM2MDstAddress=(u32)USART1_TX_BUF; //DMA内存基地址
( i" P; G$ D7 T4 s* D' h - DMA_InitStructure.MemoryOrM2MDstDataSize=LL_DMA_MDATAALIGN_BYTE;//存储器数据宽度
4 s& h3 b$ ^/ C* g/ _ - DMA_InitStructure.MemoryOrM2MDstIncMode=LL_DMA_MEMORY_INCREMENT;//执行存储器地址增量操作7 U- b& F* v0 }+ ^4 `+ Q
- DMA_InitStructure.Mode=LL_DMA_MODE_NORMAL; //正常模式,不执行循环操作
% K8 N) h G7 Q) d! ]7 K5 k2 l - DMA_InitStructure.NbData=USART1_TX_LEN; //数据传输量
% R0 U p+ j; M6 A! j2 w - DMA_InitStructure.PeriphOrM2MSrcAddress=(u32)&USART1->TDR; //DMA外设基地址
7 Z+ Y8 Y: x1 V/ ?- ^8 w# g" V1 Q - DMA_InitStructure.PeriphOrM2MSrcDataSize=LL_DMA_PDATAALIGN_BYTE; //外设数据宽度
8 G$ h% q* Y* i$ e# e/ F6 O - DMA_InitStructure.PeriphOrM2MSrcIncMode=LL_DMA_PERIPH_NOINCREMENT; //不执行外设地址增量操作
; [ ^, G6 \( p5 f- u- e. b& Y7 R - LL_DMA_Init(DMA1, LL_DMA_CHANNEL_4,&DMA_InitStructure);
# z) ?+ I* k' w: W: j1 U - }
复制代码
/ g" q2 ?4 L3 O* O' e, k是不是有一种莫名的熟悉感。# X- k9 y7 [( P& |. h
同样,串口助手验证成功9 Y& e- G0 L! H; G
* n' ~* v3 a+ o6 R. a- R
5 f. b3 Z4 }6 n D( ~+ E. S( F( M
: U. ~: y- W# x0 k c7 N四、源码下载: Y. h; w+ [& J" W- Z* O; V; w
————————————————, n9 V6 A8 {. @
转载:Willliam_william
, o7 w9 F8 G" ?' W' R& B+ k$ P6 |1 t v
2 q$ d* M5 v$ L6 e |