01概述
* O' [; d( d) O, c1 s& H5 Y' J( ~, c0 m
$ [9 j8 T5 h; ?3 W
上一篇文章《STM32使用DMA接收串口数据》讲解了如何使用DMA接收数据,使用DMA外设和串口外设,使用的中断是串口空闲中断。本篇文章主要讲解使用DMA发送数据,不会讲解基础的串口和DMA知识,直接上代码,如果有同学对DMA和串口都不熟悉,建议看一下上篇文章《STM32使用DMA接收串口数据》。& s) I% N' m. G# M/ ~0 N7 Z
使用DMA发送数据,首先我们要确认使用的串口有没有DMA。
' p1 R3 Q4 F. S2 b我们使用USART1串口外设,从数据手册中可以查到,USART1的发送和接收都是支持DMA的,使用的是DMA2.
( R: N5 |( o* s
) b" }. y4 Q' g5 n; I接下来就是撸代码的时刻了
$ O# U% I' B" X5 s6 ~" C, P. N
8 m5 J" p% v2 J- w) A* Q0 T2 t( P' I- r# T' G
0 @8 E/ a& p1 H2 x' |, p, m+ k2 W6 m9 [* C. Q! h+ d z' f
02代码
% R8 F% F7 m" K- U$ W5 q; R/ m# y# o* N9 w& I: b
' A& J9 u1 I* _- gDMA串口发送的代码是在上一篇文章DMA串口接收的基础上修改的。, k7 Y6 y- j7 B0 R- `+ c" j0 B6 ^
- <font face="微软雅黑" size="3">void UART_Init(void)2 K& B: z- N/ V) z) Q
- {& {0 C9 q& W, l) O. f
- USART_InitTypeDef USART_InitStructure;
* F3 D0 D( [( m9 V5 x7 } - GPIO_InitTypeDef GPIO_InitStructure;
, d1 Q2 e m/ `1 e# I/ I% i - NVIC_InitTypeDef NVIC_InitStructure;" M( C! r" @9 p" L
, N4 a. F0 a* q- /* Enable GPIO clock */. x9 O5 _/ @9 j- u, p
- RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);
% N- n+ ?! U0 I- v5 x( O0 i - /* Enable UART1 clock */3 F6 \$ y; E5 t
- RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);
9 m/ L4 f$ ?, N1 o7 E& V2 {8 X - /* Connect PXx to USARTx_Tx*/+ T* W* b4 a" Y# D j+ Z
- GPIO_PinAFConfig(GPIOA, 9, GPIO_AF_USART1);
; I- m* |* p! u) I: N. C - , ~! y- [, g: m" M
- /* Connect PXx to USARTx_Rx*/
8 r1 C8 S' K( N7 o, Z6 N - GPIO_PinAFConfig(GPIOA, 10, GPIO_AF_USART1);, C! U$ @# D( B1 M1 @2 o
- , q9 |& D/ p; `3 W
- /* Configure USART Tx as alternate function */2 n- G) g; i* W1 G5 q( R/ T
- GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
+ Y% h9 S) p7 O" Y: c - GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;8 n9 @& @) Y/ ]- \" H2 M7 ^
- GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;7 F% O1 g7 i* q8 n/ Y& i2 l
- 2 K0 ~$ ]2 o, r8 A2 l3 C
- GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
9 A8 |% S$ f" @, u; ]+ _ - GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
( M; B+ ^1 h1 P - GPIO_Init(GPIOA, &GPIO_InitStructure);- b4 m1 O6 L4 q# J5 b; S. s
- T4 @, a2 e8 f: l4 P1 q
- /* Configure USART Rx as alternate function */. T' t: e9 E1 z* f* L) x
- GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;3 _3 |& ~% E" ~ P+ q, X' Z
- GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;/ R8 d _+ @$ ~- d6 W& ^
- GPIO_Init(GPIOA, &GPIO_InitStructure);
! n2 t$ Z& ~9 @6 x
$ u% f! \; Z5 W5 e- [- USART_InitStructure.USART_BaudRate = 115200;4 R( x% w& v6 i, ]- }' W& {
- USART_InitStructure.USART_WordLength = USART_WordLength_8b;
; I) G4 M! l( X- k0 m8 n' s; ` - USART_InitStructure.USART_StopBits = USART_StopBits_1;# d; m! ?- [- ~2 U9 {& Z
- USART_InitStructure.USART_Parity = USART_Parity_No;
6 g4 z! Q3 N, R7 H9 n! s - USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
2 S; R5 u; \& i% c2 p# a - USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
$ u" m+ k2 G6 q9 J8 |
/ ~' L, J2 B- g+ s& o1 W' ^) C- /* USART configuration */
; H$ a9 t- ~7 }, D! m5 n - USART_Init(USART1, &USART_InitStructure);
H% m0 l+ h' x; ]6 M! L, h - , N" c0 `+ H# l( q( G
- USART_ITConfig(USART1, USART_IT_IDLE, ENABLE);
% g/ C) M+ E- q" b1 f1 m - 1 L6 Q& I0 H$ j! F6 T7 y! a7 H+ o
- /* Enable the USARTx Interrupt */
6 Q* K" H8 @" L% V3 y0 [5 ^ - NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;7 W3 _/ E1 J/ y; B
- NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority =0;
4 M W4 ~1 p: x9 u - NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;/ w9 Y5 ]' w: E$ H
- NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;5 y: c$ |- m# ^5 F7 K" V
- NVIC_Init(&NVIC_InitStructure);
) Y% A4 U6 `3 l( N - - i2 B; y, O, K# }& Y1 S2 A7 O% g8 X
- /*使能串口DMA接收*/7 r4 M& D, t+ l! y; |$ q$ J" m
- USART_DMACmd(USART1, USART_DMAReq_Rx, ENABLE);2 _. Z9 [ `/ B. D7 K$ a/ c: M
- /*使能串口DMA发送*/
K* M7 F6 w5 k( y: I4 c+ D - USART_DMACmd(USART1, USART_DMAReq_Tx, ENABLE);6 U: t0 P$ V, @- X
- ; P) c4 S2 p, h) J& [
- /* Enable USART */
2 Q" h. i- D L0 [! J3 z0 s* E - USART_Cmd(USART1, ENABLE);
3 Z' c, c' z1 b# U) t - }</font>
复制代码 在这里除了常规的串口配置,我们需要配置串口的DMA发送,和串口DMA接收一样的API函数,参数修改为USART_DMAReq_Tx即可。
2 F- K. s1 G ]% z7 Z% e串口DMA发送配置
3 W @: y3 Q% E+ g- <font face="微软雅黑" size="3">void Uart_Send_DMA_Config(void)
/ c; ~# V& x& L! z% g! O - {2 \% R( y1 _& h6 X* D! p0 d% Q
- DMA_InitTypeDef DMA_InitStructure;
! b) Y: v4 i5 C0 F" \4 A - ) z- _, _0 @9 R3 c1 [2 G
- /* Enable DMA clock */
# r B# o7 i% r' `' m1 l - RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA2, ENABLE);
. i4 ?* Y/ U8 ?) p! L
; z4 ]$ @- x$ m" U- /* Reset DMA Stream registers (for debug purpose) */
" d+ [, v8 v1 [3 J4 x$ n - DMA_DeInit(DMA2_Stream7);% S Z1 r1 \- V( q9 n, G
. S6 p+ i; P& w5 q- /* Check if the DMA Stream is disabled before enabling it.9 a5 `5 J( h( @& U
- Note that this step is useful when the same Stream is used multiple times:; N6 s# C& v2 f# d7 k E2 E
- enabled, then disabled then re-enabled... In this case, the DMA Stream disable
2 ]) `0 W7 o* I' _3 P# U) K - will be effective only at the end of the ongoing data transfer and it will
$ F$ W5 H9 ]9 e0 S( p) ?$ ? - not be possible to re-configure it before making sure that the Enable bit
% b! G7 |" D: O; W9 l7 { - has been cleared by hardware. If the Stream is used only once, this step might
- G" P& l$ j1 {5 z - be bypassed. */
6 W# l- b* X" Q, | - while (DMA_GetCmdStatus(DMA2_Stream7) != DISABLE)
0 z3 T2 m! o7 c - {
3 ], Z. a# J+ M }. m; J9 I4 S. | - }
6 b2 _5 _: C& G& ? - $ k+ R$ ]% _# s0 \
- /* Configure DMA Stream */
8 W1 d# l' ^: t5 m4 T - DMA_InitStructure.DMA_Channel = DMA_Channel_4; //DMA请求发出通道( x3 C" m, A; O0 b5 Z
- DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&USART1->DR;//配置外设地址 z1 |! `5 v% ]6 A6 g( g& ?
- DMA_InitStructure.DMA_Memory0BaseAddr = (uint32_t)UART_Buffer;//配置存储器地址/ M: C' P* h, B$ O
- DMA_InitStructure.DMA_DIR = DMA_DIR_MemoryToPeripheral;//传输方向配置! e4 X x' g! k# b7 `# ?' M
- DMA_InitStructure.DMA_BufferSize = (uint32_t)UART_RX_LEN;//传输大小; {2 l6 ^- X) o9 C) `
- DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;//外设地址不变& N% z! ?$ |$ n& R9 H3 U
- DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;//memory地址自增' x/ ]4 ]; T$ B" z8 i' H0 z% l p
- DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;//外设地址数据单位6 v1 B: V; o- U" ~9 }
- DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;//memory地址数据单位
$ i, y1 Z' e/ O2 o, ^7 ? - DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;//DMA模式:正常模式! v& v$ ?; T" s6 V
- DMA_InitStructure.DMA_Priority = DMA_Priority_High;//优先级:高
) o% z. T4 ?+ @% z# E( T' E - DMA_InitStructure.DMA_FIFOMode = DMA_FIFOMode_Disable;//FIFO 模式不使能.
6 y7 ^+ Q' Q% l8 g" s* v; u5 c - DMA_InitStructure.DMA_FIFOThreshold = DMA_FIFOThreshold_Full;// FIFO 阈值选择
& I. Q2 R+ p9 h - DMA_InitStructure.DMA_MemoryBurst = DMA_MemoryBurst_Single;//存储器突发模式选择,可选单次模式、 4 节拍的增量突发模式、 8 节拍的增量突发模式或 16 节拍的增量突发模式。3 p" f' v5 Z" x) o, }; e, b
- DMA_InitStructure.DMA_PeripheralBurst = DMA_PeripheralBurst_Single;//外设突发模式选择,可选单次模式、 4 节拍的增量突发模式、 8 节拍的增量突发模式或 16 节拍的增量突发模式。
" {: s2 S [' |( P9 D - DMA_Init(DMA2_Stream7, &DMA_InitStructure);
5 D4 P( o V* R) T6 S# N - # ~, u3 k# }; t6 [# }
- /* DMA Stream enable */
+ J: q* Y4 d7 O% I9 j - // DMA_Cmd(DMA2_Stream7, ENABLE);
: Q7 Y1 N8 k" v2 A - }</font>
复制代码 这里也是常规的DMA配置流程,不明白的同学请看文章《STM32DMA详解》,这里值得注意的是,配置完成并没有使能DMA2_Stream7,使能了就会立即将UART_Buffer的数据发送出去。
8 Z$ ~1 y" N- X" t, b其他代码处理
7 p4 a9 d) A' ^* f# m- <font face="微软雅黑" size="3">void USART1_IRQHandler(void)' F( g) G% R Y) C2 F
- {
7 r. ?& [) E) S6 a - uint8_t temp;
! T) G& b: H! V, P8 x& X; i$ ~5 i - if(USART_GetFlagStatus(USART1, USART_FLAG_IDLE) == SET)7 f6 R8 ]- ~4 W2 R+ \+ B ^
- {/ U5 \) ]" a9 s' S$ k
- DealWith_UartData();
9 D3 Q2 T5 B6 f8 y% }( W9 d/ d. N% i - // USART_ClearFlag(USART1, USART_FLAG_IDLE);
2 ]8 e" e5 i! Z% O - temp = USART1->SR; + P$ r1 Z8 ]6 S. X% e8 \& a
- temp = USART1->DR; //清USART_IT_IDLE标志
/ g; v! ?' A8 N; ?8 G - }( K7 G/ t" y: U
- }; ] d3 h* p/ p& L0 Z
4 H5 v n! H$ R, c$ m- O- void DealWith_UartData()
# _" X) F$ O8 P0 V3 i- e - {* t2 u% V' c& ]- g2 {
- DMA_Cmd(DMA2_Stream2, DISABLE); H5 h/ ]: z8 P6 g8 E6 ?
- UART_Receive_flg = 1;
! W) @4 a! x3 B - UART_Receive_len = UART_RX_LEN - DMA_GetCurrDataCounter(DMA2_Stream2);
6 A8 K: h# B7 l8 d4 k# s) U4 @ - UART_Buffer[UART_Receive_len] = 0;
% [9 y k" G8 |* U V% |: O | - DMA_SetCurrDataCounter(DMA2_Stream2,UART_RX_LEN); + @! N6 i" s+ L; f
- DMA_ClearFlag(DMA2_Stream2, DMA_FLAG_TCIF2);4 f+ u6 |1 \- h
- DMA_Cmd(DMA2_Stream2, ENABLE);8 T% E6 u2 c* x% l
- }) Q' m$ w$ U* V! m! ~# w, h
- $ x/ E" Y6 a0 X
- int main(void)1 w3 b; L: L: s
- {8 _5 m0 a/ {) }# \9 V
- UART_Receive_flg = 0;5 P* l2 @. E/ e! [
! P) w5 q6 Z& t+ [" H; G+ J% q2 Y. j- Uart_Reveice_DMA_Config();
0 S& m$ f3 l8 X0 E/ \! H( V - Uart_Send_DMA_Config();
: H5 R, F# T) w7 X: W - UART_Init();* q N- |0 ]- c3 s
' D: j- ?) u* k1 W- while (1)! G% q, B9 E7 }8 a8 Z. x
- {9 F l0 n% d, j3 O* y% @
- if(UART_Receive_flg)7 o* U9 [$ T0 c5 o! [
- {
' X2 R' ]7 y: ^ - UART_Receive_flg = 0;
2 f ?/ h) W/ z% y: O" Q' a" M - Uart_Send_DMA_Start();
0 G4 ?7 P! ~" u - }
+ b+ u% y1 _* u2 ~ - }
' [) [6 l% ^8 }: w4 X3 v - }</font>
复制代码 上面3个函数,简单逻辑就是,当串口使用DMA接收了一定量的数据,就会通过串口DMA发送出去,串口DMA发送的代码如下:) p4 t- k( Q0 A+ x( b1 E
- <font face="微软雅黑" size="3">void Uart_Send_DMA_Start(void)
1 m0 K. N2 `; j" {, P$ {. U - {
* o+ ?( T- {1 Q( |# y/ X4 |% @ - DMA_SetCurrDataCounter(DMA2_Stream7,UART_Receive_len);
9 b1 ^5 W8 h4 C" `9 C& ]; ~ - DMA_ClearFlag(DMA2_Stream7, DMA_FLAG_TCIF7);+ U4 }) Q* g& O) p. m4 \
- /* DMA Stream enable */8 f8 S7 o: y( k& K
- DMA_Cmd(DMA2_Stream7, ENABLE);2 n9 k; `; K6 }
- }</font>
复制代码
# S: }# `: E' {# u1 P8 j" U0 ?- [( D
|