01概述! o5 F: {9 n4 Q
1 U% k( |9 f( m3 w8 w7 k
; ~9 }; B/ i2 K$ i7 c上一篇文章《STM32使用DMA接收串口数据》讲解了如何使用DMA接收数据,使用DMA外设和串口外设,使用的中断是串口空闲中断。本篇文章主要讲解使用DMA发送数据,不会讲解基础的串口和DMA知识,直接上代码,如果有同学对DMA和串口都不熟悉,建议看一下上篇文章《STM32使用DMA接收串口数据》。
' v! q5 u |7 ]8 S2 _5 G使用DMA发送数据,首先我们要确认使用的串口有没有DMA。
" Q& ^: U4 D% M& r ]我们使用USART1串口外设,从数据手册中可以查到,USART1的发送和接收都是支持DMA的,使用的是DMA2.+ q! m* x5 Z) K% \; ~9 X5 n3 T
4 Q+ \/ w h& z
接下来就是撸代码的时刻了% D, K3 \! G( k# R
) U2 |" O" h8 f$ x; B2 r
' V- z- p3 D( _4 G9 u2 F& M3 p
+ S$ b: F$ ?( f
+ e2 s$ J- d5 I& V! u
02代码5 O5 o5 ? p) e' q. ]6 U
+ [0 @; A& O- k Q8 s& Q$ `% a
0 B5 T5 b% F, r) C$ m8 qDMA串口发送的代码是在上一篇文章DMA串口接收的基础上修改的。
7 C6 d5 Z5 ^' j* l! R' L2 B- <font face="微软雅黑" size="3">void UART_Init(void)
9 y: ^( C5 E/ U2 E6 z3 T; | - {! i' `/ ?- A# e$ Y
- USART_InitTypeDef USART_InitStructure;, A% Q$ r, k2 }4 }6 K
- GPIO_InitTypeDef GPIO_InitStructure;
9 b) h" ?" {& l H6 i - NVIC_InitTypeDef NVIC_InitStructure;
& I, I/ u/ P6 M( a! c& L+ Z% }) \# _ - / Z( ^4 G' N2 N3 K) f
- /* Enable GPIO clock */7 R' h/ B' G8 q3 N" w _0 ^
- RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);
+ D k: R; W0 H5 p - /* Enable UART1 clock */6 {* _2 l7 R0 E( ]5 M7 c9 M1 s
- RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);
; I9 B' E5 B# i& H9 |2 B - /* Connect PXx to USARTx_Tx*/1 k# l: c$ d9 y0 Y; U1 W
- GPIO_PinAFConfig(GPIOA, 9, GPIO_AF_USART1);" n/ H! }; V& N! G2 C7 j% S
- , S* U: J1 ^" u5 N6 k
- /* Connect PXx to USARTx_Rx*/
9 y" K" W% F5 d. t# u" J" B5 P2 d - GPIO_PinAFConfig(GPIOA, 10, GPIO_AF_USART1);
" C5 n4 N) c. G; n$ w" I1 e( x
* F% S* T/ C8 o0 c- /* Configure USART Tx as alternate function */
1 K' s1 r6 z- I- J1 Q - GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;# C0 O& L) v7 h$ N5 A1 }( b' A
- GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;, `1 N: J! Q% `1 C1 \
- GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;$ y8 S: ^- Z) z' x
- # I+ Y: V0 b+ }6 t
- GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
2 X7 Q) \3 ^% y8 Q) j2 L - GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
2 V. _ P3 p( S, C - GPIO_Init(GPIOA, &GPIO_InitStructure);8 ]+ T- H$ A8 k- {
3 J: F( L- `4 D4 Q- ?) ?+ @! H) M- /* Configure USART Rx as alternate function */
8 E, k! g+ F. s: Q$ J, h - GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
* G" b/ K+ T* G8 P2 j: K0 H2 ^7 D - GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
) a7 Q3 G( m3 b! }# _" w - GPIO_Init(GPIOA, &GPIO_InitStructure);
* J8 F) a7 t/ H: u4 D, ]( i
e& O; f" j" k+ T* z- USART_InitStructure.USART_BaudRate = 115200;) z3 O4 l! Q/ X% g7 B+ v: g1 b6 L
- USART_InitStructure.USART_WordLength = USART_WordLength_8b;0 U) r+ `# W/ T0 x4 J5 D" p
- USART_InitStructure.USART_StopBits = USART_StopBits_1;
" Q! b% g/ ~( A0 x4 Z! x8 R - USART_InitStructure.USART_Parity = USART_Parity_No;
. h: \$ W. E7 u! S! A" ^ - USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;! Z) n/ E! y5 V5 _+ u) U0 s% c
- USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
& @4 Z( P6 C! y. C3 G# v3 J3 ? - 5 @8 m) y: R: K+ S. Y
- /* USART configuration */
9 `/ P8 K) d. }' f - USART_Init(USART1, &USART_InitStructure);; e! k. o) u% \8 Y; i
. l& ~8 c+ i* O5 R- g& S% Z* n* a- USART_ITConfig(USART1, USART_IT_IDLE, ENABLE);. D8 {; w2 v7 l* E9 p/ j5 N) k
9 i/ |4 j% F9 R% \: }1 x* H9 w8 P: S- /* Enable the USARTx Interrupt */
# L* r& F: U/ n, z& _- o - NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;
4 M* q+ h+ O: ? - NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority =0;5 D- V! L# h* H8 w& S& j4 A, h! H
- NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;2 ^& ]6 }! g" b2 ^4 E
- NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
0 f8 A; P$ A! P! ?6 j - NVIC_Init(&NVIC_InitStructure);' E* e+ }" x$ x$ V \
- 5 W* `6 m! J1 J, ]2 _9 _
- /*使能串口DMA接收*/2 E5 x$ z$ @* }) j/ m' }4 S' A% n/ F
- USART_DMACmd(USART1, USART_DMAReq_Rx, ENABLE);4 T$ r* _9 l: Z. o: |
- /*使能串口DMA发送*/
2 X+ g+ w( \* J5 }4 `5 a0 V8 W - USART_DMACmd(USART1, USART_DMAReq_Tx, ENABLE);
" c; o# a* M: S( i1 k0 E
( P0 `% L. E: U2 @) o- /* Enable USART */
6 |+ t/ J$ c$ W2 F: R) i - USART_Cmd(USART1, ENABLE);6 b5 [3 X, ?# z3 x0 Q0 ~- s9 b
- }</font>
复制代码 在这里除了常规的串口配置,我们需要配置串口的DMA发送,和串口DMA接收一样的API函数,参数修改为USART_DMAReq_Tx即可。8 K- Y& {6 U6 j- v9 t1 q
串口DMA发送配置
8 k1 A) y; V8 v" i# l+ q2 P- <font face="微软雅黑" size="3">void Uart_Send_DMA_Config(void)
) j' I! G, ?6 B! l - {$ |- S) z1 x$ r5 a
- DMA_InitTypeDef DMA_InitStructure;9 a( D5 \# N9 ~$ R9 T5 m" q' w
! w$ @6 p' Y1 H2 @5 H- /* Enable DMA clock */2 z! v- l2 a: j
- RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA2, ENABLE);
0 r) e7 _1 C& H4 X5 \1 q7 V - ) d, f) R/ i- U( \/ X' `
- /* Reset DMA Stream registers (for debug purpose) */
" i7 B# _2 s$ ^ ~ - DMA_DeInit(DMA2_Stream7); F" \* h: ?0 }* y2 h
- * {6 P, G Y$ r! H% f' q( Z
- /* Check if the DMA Stream is disabled before enabling it.5 a3 h G1 T% ~7 R& L& _% H
- Note that this step is useful when the same Stream is used multiple times:
Y; n2 j8 U+ E$ p - enabled, then disabled then re-enabled... In this case, the DMA Stream disable
& t9 Q, c$ n( K8 r# ?5 y - will be effective only at the end of the ongoing data transfer and it will 3 _& y i3 P/ c. I9 a/ G- v3 T
- not be possible to re-configure it before making sure that the Enable bit
* R* |( u8 G- [$ e$ N" R - has been cleared by hardware. If the Stream is used only once, this step might
7 r5 C$ \& i$ ^) g - be bypassed. */
! Y9 [' b4 j$ y; ]+ }6 [/ s - while (DMA_GetCmdStatus(DMA2_Stream7) != DISABLE)
+ S B |$ j7 k; u! y3 {0 k - {
8 ~- }# p" H, @& u1 b; @+ O+ W - }0 x; z7 a: |6 x6 U
- 5 ]5 e# ?8 k* N* k7 S9 i5 B: F
- /* Configure DMA Stream */
* C r ?' V# ]$ x8 V - DMA_InitStructure.DMA_Channel = DMA_Channel_4; //DMA请求发出通道1 {: |" {4 l* @9 H% \) \
- DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&USART1->DR;//配置外设地址! |! t" y. a5 B3 b+ l8 l% W, X- W! }
- DMA_InitStructure.DMA_Memory0BaseAddr = (uint32_t)UART_Buffer;//配置存储器地址
; v" Z+ {& G. N8 M& }; v - DMA_InitStructure.DMA_DIR = DMA_DIR_MemoryToPeripheral;//传输方向配置8 {9 v1 _! b/ a* w% N7 \. X
- DMA_InitStructure.DMA_BufferSize = (uint32_t)UART_RX_LEN;//传输大小$ v6 k0 y" i) n; k0 |) P0 L
- DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;//外设地址不变
7 u' Q7 R+ b: O, v) a* K% |6 \ - DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;//memory地址自增
4 |& _+ v7 u3 U& d: k, l0 U. f4 E - DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;//外设地址数据单位: V) o+ J4 d; o! l7 L& I) f
- DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;//memory地址数据单位' a% c4 W/ K; `3 |+ ]: j
- DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;//DMA模式:正常模式$ H2 U) a5 b! f( I, ^8 b) p
- DMA_InitStructure.DMA_Priority = DMA_Priority_High;//优先级:高
6 `- D/ o2 j$ g9 r7 _& t - DMA_InitStructure.DMA_FIFOMode = DMA_FIFOMode_Disable;//FIFO 模式不使能. . E3 ]+ o: M( q9 ~
- DMA_InitStructure.DMA_FIFOThreshold = DMA_FIFOThreshold_Full;// FIFO 阈值选择+ g! V2 u. v( f3 v
- DMA_InitStructure.DMA_MemoryBurst = DMA_MemoryBurst_Single;//存储器突发模式选择,可选单次模式、 4 节拍的增量突发模式、 8 节拍的增量突发模式或 16 节拍的增量突发模式。
' k; @/ ~+ A* o* M$ C - DMA_InitStructure.DMA_PeripheralBurst = DMA_PeripheralBurst_Single;//外设突发模式选择,可选单次模式、 4 节拍的增量突发模式、 8 节拍的增量突发模式或 16 节拍的增量突发模式。
5 j; D/ |. A, w. `7 Y - DMA_Init(DMA2_Stream7, &DMA_InitStructure);
7 @. O$ x, i7 o& ^
& `. W( `0 U2 ~/ c- /* DMA Stream enable */
% Q& x2 t* s z - // DMA_Cmd(DMA2_Stream7, ENABLE);
5 ~$ j9 ~6 ?- H! ~ - }</font>
复制代码 这里也是常规的DMA配置流程,不明白的同学请看文章《STM32DMA详解》,这里值得注意的是,配置完成并没有使能DMA2_Stream7,使能了就会立即将UART_Buffer的数据发送出去。1 F5 u7 e: {6 y- j
其他代码处理
! y0 @8 ^0 u. o+ S' l9 O2 ?- <font face="微软雅黑" size="3">void USART1_IRQHandler(void)
3 E$ K" T2 X# o! S" c) { - {/ M% L% F2 O3 C6 Z
- uint8_t temp;
3 L x( p5 Q8 E% ] - if(USART_GetFlagStatus(USART1, USART_FLAG_IDLE) == SET)& t8 m- u& r8 [' M1 [
- {
]# f; w4 c+ l5 F - DealWith_UartData();; H8 h9 q$ [& c$ n9 b: |
- // USART_ClearFlag(USART1, USART_FLAG_IDLE);( ]8 e* w7 \$ O# k6 R
- temp = USART1->SR; ; Y5 u2 ^; ~/ f
- temp = USART1->DR; //清USART_IT_IDLE标志 % b+ n1 s" S& \3 Q
- }- D- b1 W, D J7 z7 T4 W9 h
- }
/ ^- s9 U% e K. s0 {2 `
* P+ I) H4 P/ I4 @7 [3 \7 g% m- void DealWith_UartData()
5 N( z: Z8 r! Q# q* o: ? - {5 F! T0 o- P3 a$ R8 H
- DMA_Cmd(DMA2_Stream2, DISABLE);. Q* D) c9 |. X8 \
- UART_Receive_flg = 1;; g- I/ R+ K1 I0 D0 Z
- UART_Receive_len = UART_RX_LEN - DMA_GetCurrDataCounter(DMA2_Stream2);' J! P7 L8 Q$ t, i. b
- UART_Buffer[UART_Receive_len] = 0;# F7 A8 x0 w) i
- DMA_SetCurrDataCounter(DMA2_Stream2,UART_RX_LEN);
1 v. Z0 Y+ D& \7 Z1 K - DMA_ClearFlag(DMA2_Stream2, DMA_FLAG_TCIF2);
; k3 H: z3 ]/ a, d - DMA_Cmd(DMA2_Stream2, ENABLE);: D' v9 \) |9 V. I; u' @
- }
! h/ H9 G* K2 n' P9 q7 p - ! \* [) V" O6 i7 |6 a+ a. b/ X- o
- int main(void)
1 P+ Q( P7 V- x - {3 Z# f" x8 R ]7 k: f
- UART_Receive_flg = 0;& k0 v$ Z X4 P& C8 w
- : A5 \( Y6 I! d0 E/ {7 h3 O6 v
- Uart_Reveice_DMA_Config();
$ y: g2 e. Z" u k - Uart_Send_DMA_Config();5 ^1 E( \9 X' m% l5 z7 _: P
- UART_Init(); @2 u7 E& f( N$ v; k; ~
- 8 Q( ~9 C5 |! J' F& `$ h% Y( d
- while (1)
, B7 W: K3 C; R p+ w - {
9 q* h3 u* V3 y, O" N - if(UART_Receive_flg)1 O6 x* t4 t$ M) f: |
- {* t# B+ C4 R: J; T9 @) ~
- UART_Receive_flg = 0;; G0 L4 L t" f
- Uart_Send_DMA_Start();# y3 h' e4 E% h& k" n N5 g
- }
6 `: s2 K& E f, r! M - }
& w! ]: v w, h0 C5 n( [2 B - }</font>
复制代码 上面3个函数,简单逻辑就是,当串口使用DMA接收了一定量的数据,就会通过串口DMA发送出去,串口DMA发送的代码如下:
6 q; q: Q9 x# y q- <font face="微软雅黑" size="3">void Uart_Send_DMA_Start(void)+ Z/ z6 T! S; o' ~. i
- {7 g, R$ Q% [% A9 P0 y. a) K' M
- DMA_SetCurrDataCounter(DMA2_Stream7,UART_Receive_len); 3 ^: M$ e g, L& U
- DMA_ClearFlag(DMA2_Stream7, DMA_FLAG_TCIF7);% D! ^7 ^1 d- t8 h w% P9 R z% D
- /* DMA Stream enable */0 M: W4 {6 o @4 z7 A& {8 i, ~, N5 n( M
- DMA_Cmd(DMA2_Stream7, ENABLE);+ A; S: z5 X9 w- f
- }</font>
复制代码
0 i6 ^2 W6 m+ m H
$ |8 m" j7 k! c4 ^ |