01概述! y; c! [& S: L9 e( U3 D' A7 ~( I
) t, \7 u. m7 q. N
" d$ `0 |% b& A) f' ]6 E$ L上一篇文章《STM32使用DMA接收串口数据》讲解了如何使用DMA接收数据,使用DMA外设和串口外设,使用的中断是串口空闲中断。本篇文章主要讲解使用DMA发送数据,不会讲解基础的串口和DMA知识,直接上代码,如果有同学对DMA和串口都不熟悉,建议看一下上篇文章《STM32使用DMA接收串口数据》。
' ?! D: ? Y* H7 W* n使用DMA发送数据,首先我们要确认使用的串口有没有DMA。" F/ P) b5 T. F( e2 E+ m2 O
我们使用USART1串口外设,从数据手册中可以查到,USART1的发送和接收都是支持DMA的,使用的是DMA2.0 D$ `2 T+ V1 @( i! v
: t2 p/ j! Y0 G接下来就是撸代码的时刻了& e1 M$ e! ]+ G5 f* i' n/ _; o( g; h- o
; V: F+ E# S% L: ^. Q: Q) \, t
4 {6 X* _- G' m
' h$ A3 S/ l$ m$ V* j' C6 x/ B4 k) B5 U: c
02代码+ |. b% j- g( |6 V' y- z, t
5 _. @) i6 Y2 Y K
X+ j4 r/ F' d, JDMA串口发送的代码是在上一篇文章DMA串口接收的基础上修改的。, F6 q4 A" Q6 k' s
- <font face="微软雅黑" size="3">void UART_Init(void)0 I& d4 G& \8 m2 w, e
- {
1 d( H6 `# I5 h+ _- c9 I - USART_InitTypeDef USART_InitStructure;
& Z% S4 o5 ]9 b1 F1 w - GPIO_InitTypeDef GPIO_InitStructure;$ Y/ P: h: o% _
- NVIC_InitTypeDef NVIC_InitStructure;/ p$ m3 ]$ y' t+ C. g
- 9 l1 P8 h3 Z/ t/ _9 n
- /* Enable GPIO clock */
* l, F1 ^7 `1 K1 i- ]$ k - RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);
1 l7 Y, p' r- [8 j' ]$ B - /* Enable UART1 clock */! d% s' }5 V7 T$ h0 E/ Q
- RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);
" Y6 d: o6 D1 L5 @! X' s7 Y - /* Connect PXx to USARTx_Tx*/; |. o/ ~3 n7 O
- GPIO_PinAFConfig(GPIOA, 9, GPIO_AF_USART1);+ a7 |; E6 w9 k3 R% [
- 2 ~1 {- h, n. N- r3 {" e! J
- /* Connect PXx to USARTx_Rx*/
- l$ X( b9 c& Q' C& e: a7 E4 t - GPIO_PinAFConfig(GPIOA, 10, GPIO_AF_USART1);
, t1 ]" v5 _' M! X: x% d( |. J - / L7 q3 G5 _+ W0 v% ~
- /* Configure USART Tx as alternate function */4 S+ I5 p* x, V6 f, o9 j& a
- GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
( {. i, p3 T9 b! o3 L. \7 Y - GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;5 c8 W( ^/ m7 [" w& N% E
- GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;! Y( L/ E+ f E$ E6 i* J
5 b- `: J& h# V3 `* A- GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
- \) Y- p. R* S8 {2 y8 w1 H$ N - GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
0 C* _2 h& m8 j! }1 A& o$ ]# V; H - GPIO_Init(GPIOA, &GPIO_InitStructure);
) M {/ @! \) h. i$ [, X- C& W ^- G$ v
, N: n& K% G$ @; ]" q9 H8 U' N8 r- /* Configure USART Rx as alternate function */! ?' f: v, V6 w; l6 A% R
- GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;2 b1 e- a! ^7 Z! q7 L
- GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
. M& e, M }& a% H/ X* |! O+ q' ?3 W - GPIO_Init(GPIOA, &GPIO_InitStructure);
9 u8 o7 l2 x7 x* N9 ^% `1 K/ Z - # M4 b- X) V: X
- USART_InitStructure.USART_BaudRate = 115200;0 t6 @; i l u. K4 |
- USART_InitStructure.USART_WordLength = USART_WordLength_8b;, I7 p" o# m0 X8 f( P
- USART_InitStructure.USART_StopBits = USART_StopBits_1;
8 N( t; I- w( t1 e( f& r% t - USART_InitStructure.USART_Parity = USART_Parity_No;
9 q- a) }: j$ T' C' ^! n; x+ I0 K - USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
P y! V$ E8 q, R/ j - USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;; s+ T6 T- ~7 O3 J* I8 s
4 k* y. @: h: s% u+ G- /* USART configuration */) {% v' E0 ~7 w- {0 }# W
- USART_Init(USART1, &USART_InitStructure);
( q$ m9 D& H) T2 @+ I' { - ' w; f) c1 F' p! e) i7 \
- USART_ITConfig(USART1, USART_IT_IDLE, ENABLE);
4 }& [( N J( V4 A( @4 e$ u1 W - * v9 a M1 C( v+ w. _
- /* Enable the USARTx Interrupt */
" u& r$ z/ j1 O0 a - NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;0 m: Q" V4 u3 P" J; b+ f
- NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority =0;
4 I" D) M- D0 ]7 T; ]0 }' y - NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;6 y, P& f! V. \! b
- NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
) M W( d2 u) Z2 X$ w - NVIC_Init(&NVIC_InitStructure);
' P% {1 g X" l - ' T) `5 F* R+ v, k6 Z7 S
- /*使能串口DMA接收*/! A( ] {( r) d) i% S7 Q
- USART_DMACmd(USART1, USART_DMAReq_Rx, ENABLE);% z* q/ j. U' t7 X
- /*使能串口DMA发送*/
6 `2 k; u! O$ @9 ~ - USART_DMACmd(USART1, USART_DMAReq_Tx, ENABLE);
7 S7 d6 j+ u7 v; _+ }7 o+ N' Z2 `6 W
7 }, @3 ]7 ?( z3 _ ?* X" N- Z3 o- /* Enable USART */
4 a2 u: _" \+ f# }" g - USART_Cmd(USART1, ENABLE);3 K8 q5 }/ G0 [
- }</font>
复制代码 在这里除了常规的串口配置,我们需要配置串口的DMA发送,和串口DMA接收一样的API函数,参数修改为USART_DMAReq_Tx即可。0 k* `! H4 c1 j$ ]- r" ~& \
串口DMA发送配置: c# ~8 A& e7 x
- <font face="微软雅黑" size="3">void Uart_Send_DMA_Config(void)/ v8 Z. }( d4 h0 {6 P; }. s% d0 s$ ]
- {
d$ `! K4 E1 o7 F - DMA_InitTypeDef DMA_InitStructure;; S0 H3 Y- a7 f& F- p- U- W, |8 m$ Q
- 8 h' ]6 M( n$ x3 w% a) i$ d' t
- /* Enable DMA clock */
4 K! F- z% J( j( T - RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA2, ENABLE);
3 C* `' E. h- L" [# T* m) ?: F - $ h( [4 w: }/ t. M" p; J+ m* v
- /* Reset DMA Stream registers (for debug purpose) */
, h! R# p6 a* H" S7 L5 O p' v! O& k - DMA_DeInit(DMA2_Stream7);
6 H9 V; p1 ~0 t* \8 u1 ]9 q1 S - % ]$ V, O) d" I
- /* Check if the DMA Stream is disabled before enabling it.
3 y1 {! f J4 |, V5 x - Note that this step is useful when the same Stream is used multiple times:
" I0 N' P5 F0 l - enabled, then disabled then re-enabled... In this case, the DMA Stream disable
) P7 v: N" f* T8 C; T - will be effective only at the end of the ongoing data transfer and it will ; l/ ?! o5 g3 Q/ {0 U
- not be possible to re-configure it before making sure that the Enable bit
S0 T- Q$ R% B+ q3 X$ B - has been cleared by hardware. If the Stream is used only once, this step might
, ] e3 N! k- r+ `2 a5 w - be bypassed. */9 i3 t. E9 E) I# w8 s2 C
- while (DMA_GetCmdStatus(DMA2_Stream7) != DISABLE)( ], v. f/ @. u2 `+ u
- {
# ~1 s8 _& n- {7 T U - }. F- t+ @# v/ x
5 A) P6 [' n5 p; x- /* Configure DMA Stream *// p# }# o8 T# O( y8 @# b f1 }
- DMA_InitStructure.DMA_Channel = DMA_Channel_4; //DMA请求发出通道
$ K$ c) x! |8 ~6 v4 w5 r - DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&USART1->DR;//配置外设地址* ^# c+ E% X, G! u8 p
- DMA_InitStructure.DMA_Memory0BaseAddr = (uint32_t)UART_Buffer;//配置存储器地址4 `' F, F2 q1 r8 {
- DMA_InitStructure.DMA_DIR = DMA_DIR_MemoryToPeripheral;//传输方向配置
1 A. f! H$ C/ X3 d9 ^5 \ - DMA_InitStructure.DMA_BufferSize = (uint32_t)UART_RX_LEN;//传输大小
: _, G/ ]/ n* [. H/ [ - DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;//外设地址不变. ]9 x4 x. A% K) J3 W9 C# t8 f. \+ r
- DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;//memory地址自增1 [0 U& f( I/ }: q9 O8 D$ d2 V4 P
- DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;//外设地址数据单位$ K, K0 s* I) E [7 J; A
- DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;//memory地址数据单位. n# B* p! {1 E+ {. E
- DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;//DMA模式:正常模式
; [& B' c0 }1 q - DMA_InitStructure.DMA_Priority = DMA_Priority_High;//优先级:高
3 S: p( C4 O7 V7 M( `+ u - DMA_InitStructure.DMA_FIFOMode = DMA_FIFOMode_Disable;//FIFO 模式不使能.
9 j/ ^" W0 X! |) N& g - DMA_InitStructure.DMA_FIFOThreshold = DMA_FIFOThreshold_Full;// FIFO 阈值选择
/ m2 V% G; t+ Y! p3 R - DMA_InitStructure.DMA_MemoryBurst = DMA_MemoryBurst_Single;//存储器突发模式选择,可选单次模式、 4 节拍的增量突发模式、 8 节拍的增量突发模式或 16 节拍的增量突发模式。
# ]7 g. X" |* N - DMA_InitStructure.DMA_PeripheralBurst = DMA_PeripheralBurst_Single;//外设突发模式选择,可选单次模式、 4 节拍的增量突发模式、 8 节拍的增量突发模式或 16 节拍的增量突发模式。- D3 k8 I/ g! ^6 y9 J- _4 u
- DMA_Init(DMA2_Stream7, &DMA_InitStructure); , N# ^# H P) A9 ?$ C
0 X6 }& ]6 B4 D) f% {- /* DMA Stream enable */
, l, X) w) X1 D! @ - // DMA_Cmd(DMA2_Stream7, ENABLE);
, x; Y a% ]2 e/ T/ r! G - }</font>
复制代码 这里也是常规的DMA配置流程,不明白的同学请看文章《STM32DMA详解》,这里值得注意的是,配置完成并没有使能DMA2_Stream7,使能了就会立即将UART_Buffer的数据发送出去。2 l' I9 B/ j* W4 f9 o6 g
其他代码处理
8 s2 n Y/ W9 i- <font face="微软雅黑" size="3">void USART1_IRQHandler(void)
( v( g! {! s: Y Q$ F0 } - {5 ?- _" u2 l+ L8 q- A
- uint8_t temp;
5 H0 V0 D! h; X$ l' J - if(USART_GetFlagStatus(USART1, USART_FLAG_IDLE) == SET)0 q/ G8 a0 @( ~) c3 \3 o V
- {
3 w2 w) ^& t# S( g - DealWith_UartData();& f' _" a+ r& X, J1 ]( K
- // USART_ClearFlag(USART1, USART_FLAG_IDLE);( C3 ]8 m% t+ t0 Z, ^3 Y
- temp = USART1->SR; $ W8 E& g: @6 U* [1 g& w2 N8 c6 F# l
- temp = USART1->DR; //清USART_IT_IDLE标志
z: l2 K& N/ L2 G - }
1 d- a6 V" O! @7 G% Q - }! Y* d* ]# s, O$ j8 z
- ) w9 q( h$ F) f1 A0 ^3 L# W
- void DealWith_UartData()( q1 p/ s; i# u8 f3 {5 J
- {
; V) ?+ O" ]/ d# f3 L; _5 e6 x - DMA_Cmd(DMA2_Stream2, DISABLE);, Z, K: D# i1 D @: [9 L4 ]
- UART_Receive_flg = 1;. A {1 i7 ?4 U. N- G5 a
- UART_Receive_len = UART_RX_LEN - DMA_GetCurrDataCounter(DMA2_Stream2);
$ ^7 K8 d. [& g( t5 `' f! j - UART_Buffer[UART_Receive_len] = 0;' H. M! M4 Q# ~6 n4 g7 ]+ H
- DMA_SetCurrDataCounter(DMA2_Stream2,UART_RX_LEN); : ~& h* X. a: E' v o$ k) F7 y
- DMA_ClearFlag(DMA2_Stream2, DMA_FLAG_TCIF2);
+ Y% r9 u! b: U, c2 d$ e$ k7 E" E - DMA_Cmd(DMA2_Stream2, ENABLE);; X5 s( Q; A2 ~5 n! w9 x
- }6 K% m# n7 ?- o6 }1 \
& {* \4 Z7 F% F- int main(void)
! g; j( V5 e, H: ^5 v* L - {9 J; Z& P: {! E0 _
- UART_Receive_flg = 0; I# E: p' R- a8 t! a5 |: H; X
- ) o* }7 o* C7 p" m) v! O3 U
- Uart_Reveice_DMA_Config();& h. u3 U j* ]4 V" z8 q
- Uart_Send_DMA_Config();
0 f e4 S' \- S - UART_Init();- r2 R, Q3 Q; t; E0 F7 n0 \8 s+ Z$ L
- . K" w) d, i% [/ |
- while (1)
0 b; h6 z- s; n: L, @ - {; C6 x: Y' r* i
- if(UART_Receive_flg)2 V5 t' F" l7 u7 \' C
- {. a) f6 u! n% q+ V
- UART_Receive_flg = 0;
( }8 E) i8 y: y5 F - Uart_Send_DMA_Start();3 _1 O" C1 L5 k$ i% S
- }: T, p1 d5 {- f6 l
- }% E9 F! X7 G1 ]6 o: h
- }</font>
复制代码 上面3个函数,简单逻辑就是,当串口使用DMA接收了一定量的数据,就会通过串口DMA发送出去,串口DMA发送的代码如下:# F1 G0 T( H& P* O8 ?
- <font face="微软雅黑" size="3">void Uart_Send_DMA_Start(void)
7 M5 j6 M* N0 J) v - {
4 |( }7 ~3 z% p! @ - DMA_SetCurrDataCounter(DMA2_Stream7,UART_Receive_len);
' l& F" R5 W- b. ` - DMA_ClearFlag(DMA2_Stream7, DMA_FLAG_TCIF7);% W/ Q2 k* G4 C5 E. l" u" V8 @/ p- H
- /* DMA Stream enable */
4 L! y1 u+ u' [ - DMA_Cmd(DMA2_Stream7, ENABLE);
) g1 \( w! v" n' O - }</font>
复制代码
% X' o: C+ q+ o/ o9 g) X& @, d9 V/ X) ?% k9 x1 j" t9 G
|