1、概述* l) i! ?. z9 S2 ]% f( m2 P, d
! r$ H' T2 q7 n; |6 d. |使用DMA发送数据,首先我们要确认使用的串口有没有DMA。
2 \& E6 g! U0 J1 J% c2 ^7 h* [* [
我们使用USART1串口外设,从数据手册中可以查到,USART1的发送和接收都是支持DMA的,使用的是DMA2.9 p n6 f0 x- g, s u
/ ?/ a4 c" {% o1 ~3 T
Q5 F3 W/ d5 ]+ p
7 ?0 H4 `. b E; t接下来就是撸代码的时刻了
& Z7 M2 d4 g5 `7 e% i$ y' U: [6 t% ^/ S$ e' Z
02、代码1 f9 t0 y! b& m) m" B( B d
DMA串口发送的代码是在上一篇文章DMA串口接收的基础上修改的。
7 W- b3 l- s3 ` ^! X! u9 i3 K! Y7 m' U. z s3 b5 L4 P% c C3 w
- void UART_Init(void)
$ H6 Q5 u% `2 M; @+ H - {6 j4 w3 A8 j* `: Z
- USART_InitTypeDef USART_InitStructure;
. d& ^" n. x0 v9 I" p& V/ \! b0 T - GPIO_InitTypeDef GPIO_InitStructure;
w5 H) G& E9 F) ?' f - NVIC_InitTypeDef NVIC_InitStructure;" `) M# w, Z1 L
- : E4 J" P6 G7 e) n
- /* Enable GPIO clock */
, [' D0 Y1 R ^ - RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);
+ {) h6 Q& s$ C/ G& d' O8 p5 c - /* Enable UART1 clock */
5 Y+ A2 T$ M. n4 Z - RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);
' q2 I4 @' ]1 U2 X( {' \: O - /* Connect PXx to USARTx_Tx*/
8 p; b9 ?* W+ K - GPIO_PinAFConfig(GPIOA, 9, GPIO_AF_USART1);
! b' `* E5 H# \' W* j4 O
$ V2 ]( e, p7 F- /* Connect PXx to USARTx_Rx*/
- J0 i+ e( h( A4 `- C4 p5 @& N - GPIO_PinAFConfig(GPIOA, 10, GPIO_AF_USART1);; Y( b e( y( @- Q2 l- }
- 1 ~( [3 Q8 {4 x! J9 a
- /* Configure USART Tx as alternate function */5 r& i4 D! w+ m9 v
- GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
9 k. j* x2 V$ Y; J5 R& i4 H( W - GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;
4 [& z7 W; V: F0 X7 e - GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;7 A4 ] `+ K2 _
/ {. ]9 i: H, Q$ U- GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;! H( F; f: ]4 a$ t
- GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;( G: v4 I1 z5 ?9 t* x
- GPIO_Init(GPIOA, &GPIO_InitStructure);
4 F7 S+ E( F Z* ^- Z% p
$ _; h M. c& A- /* Configure USART Rx as alternate function */
9 x0 H( v1 m1 K% e - GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;" u2 x( C- G! _
- GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
/ m, b# O, F( I# y1 [ - GPIO_Init(GPIOA, &GPIO_InitStructure);
1 v A% l) Y4 x& r2 n8 ]; B0 b6 h
" B, m8 i# B8 Q! s9 z: N# x1 Q- USART_InitStructure.USART_BaudRate = 115200;5 s5 m# ~, `, l6 F8 A
- USART_InitStructure.USART_WordLength = USART_WordLength_8b;3 u5 ]4 b' r: Y. t3 E+ F/ G* r
- USART_InitStructure.USART_StopBits = USART_StopBits_1;" T# F# T- X5 w0 |
- USART_InitStructure.USART_Parity = USART_Parity_No;
0 E- h. O. T; M+ |: x - USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;- M$ i1 ]) v$ i, [
- USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;& b+ K7 X% I" J" J' y) Y3 \
- : `2 W5 n' j* A% r: F
- /* USART configuration */
$ {; ~) f+ c5 J r1 ^+ n - USART_Init(USART1, &USART_InitStructure);5 g& B/ C0 o/ v2 o7 S. ^/ G
- 7 Z7 n6 R! z+ e+ {7 b4 ], D$ h
- USART_ITConfig(USART1, USART_IT_IDLE, ENABLE);6 c2 M. a1 A% h8 W+ I. q
8 u# R% h7 v+ \- /* Enable the USARTx Interrupt */. n9 k, V" F4 o/ V" w7 t
- NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;; h/ o8 c0 a( q( N' `+ S" ?
- NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority =0;
8 t: x) v5 _0 ^" @ - NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
7 Q6 }% x, i# H# _( A* Z: {$ ]* i; v - NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
3 y$ {5 j* e2 u% \) r9 X1 G& ^ - NVIC_Init(&NVIC_InitStructure);
Y! W% d l, t4 U3 e) F0 V- a- E - + f* P5 a! @9 C
- /*使能串口DMA接收*/. Y/ e6 ^2 @9 R2 V
- USART_DMACmd(USART1, USART_DMAReq_Rx, ENABLE);
5 z, p" Q2 Y# n+ f - /*使能串口DMA发送*/
2 A% ~# `& w* f: u# P1 @. W - USART_DMACmd(USART1, USART_DMAReq_Tx, ENABLE);
6 o8 Z9 y7 c2 i7 S - 2 \1 @, A; l+ `% k* H
- /* Enable USART */4 `- f1 g0 o- I2 y+ J
- USART_Cmd(USART1, ENABLE);0 ` ?# Q) }% x4 t- E
- }
复制代码
0 D: l$ F* r" G4 T4 a
2 E/ G+ X/ H5 Y' ?/ N S; ~在这里除了常规的串口配置,我们需要配置串口的DMA发送,和串口DMA接收一样的API函数,参数修改为USART_DMAReq_Tx即可。
. W6 _" B' k% i. Z4 H, a9 u
. F8 D3 C+ M$ ?/ b& n4 r ]串口DMA发送配置- D' u( r+ M; x0 P4 L5 E0 G% n
/ z% i/ _. b; _5 A- void Uart_Send_DMA_Config(void)
' d* c x* A" A! l7 L/ G - {
7 A3 u- t, g( O/ Q# L E7 n - DMA_InitTypeDef DMA_InitStructure;1 m# Z8 x7 D _! g3 e
- + c# f, m: b" t8 l6 z9 \0 I
- /* Enable DMA clock */$ @8 I; p0 |9 d& l; j* [5 d: w
- RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA2, ENABLE);
2 V% T# U% v; e: X) v! d
8 b* \, |8 d4 v# g$ P- /* Reset DMA Stream registers (for debug purpose) */" U: s9 [$ a2 s1 y( |
- DMA_DeInit(DMA2_Stream7);
6 t9 T7 @3 \ \& g
2 H2 ^; V" y8 U0 h* n0 I3 c- /* Check if the DMA Stream is disabled before enabling it.
" V0 O! x- u0 f - Note that this step is useful when the same Stream is used multiple times: O5 u4 T7 i' c4 H8 e- y1 m
- enabled, then disabled then re-enabled... In this case, the DMA Stream disable
1 r) f i- r y) P6 W* [# I - will be effective only at the end of the ongoing data transfer and it will
+ Z6 y$ U, S( }/ b. R - not be possible to re-configure it before making sure that the Enable bit % k+ ?+ ]5 e8 X/ |$ w
- has been cleared by hardware. If the Stream is used only once, this step might
6 Q4 T- e# k4 z6 |- q e" }7 O - be bypassed. */
6 g, Y" T) A( I: T& {' D# h - while (DMA_GetCmdStatus(DMA2_Stream7) != DISABLE)
" Q( M* s3 _0 e5 I& O1 G/ S0 ] - {; @; q9 b6 \6 Y9 G$ }7 J
- }
$ o m) V' v }1 N6 ~* X - . E* X6 u# k2 ~4 n- T% S9 C" D
- /* Configure DMA Stream */) X. c5 E6 u) j* q# |, h
- DMA_InitStructure.DMA_Channel = DMA_Channel_4; //DMA请求发出通道
0 u3 [# T' a+ z, g - DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&USART1->DR;//配置外设地址8 u# j4 L0 C# V$ N5 O7 v
- DMA_InitStructure.DMA_Memory0BaseAddr = (uint32_t)UART_Buffer;//配置存储器地址
% J( f; r* _3 ?2 H - DMA_InitStructure.DMA_DIR = DMA_DIR_MemoryToPeripheral;//传输方向配置
7 j+ r" O/ g9 w% X& \/ R - DMA_InitStructure.DMA_BufferSize = (uint32_t)UART_RX_LEN;//传输大小4 t$ L+ P# K8 j) A: a% C
- DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;//外设地址不变5 H6 Y. ~% J; c, D# v
- DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;//memory地址自增
/ B1 a& `. Z- L, P5 B) Z - DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;//外设地址数据单位
9 U$ Z. J7 |2 K- Y: f" ~: g - DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;//memory地址数据单位& H8 h7 L% w$ B/ h& L, C' f1 E
- DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;//DMA模式:正常模式
1 n- |/ G4 k8 g. O. a - DMA_InitStructure.DMA_Priority = DMA_Priority_High;//优先级:高5 ~* c4 C+ O [6 Y$ x
- DMA_InitStructure.DMA_FIFOMode = DMA_FIFOMode_Disable;//FIFO 模式不使能.
" R* H" E- `' P3 I/ |0 x - DMA_InitStructure.DMA_FIFOThreshold = DMA_FIFOThreshold_Full;// FIFO 阈值选择$ K, R+ M' `1 V0 e1 _
- DMA_InitStructure.DMA_MemoryBurst = DMA_MemoryBurst_Single;//存储器突发模式选择,可选单次模式、 4 节拍的增量突发模式、 8 节拍的增量突发模式或 16 节拍的增量突发模式。
3 A. `: i" E4 x8 c1 y - DMA_InitStructure.DMA_PeripheralBurst = DMA_PeripheralBurst_Single;//外设突发模式选择,可选单次模式、 4 节拍的增量突发模式、 8 节拍的增量突发模式或 16 节拍的增量突发模式。
' v, g( Z' p) o* }, O; n$ Z5 U - DMA_Init(DMA2_Stream7, &DMA_InitStructure); 2 o! Z& f8 A6 t* C9 ?/ ?# K
# O9 I% S8 ], k- N0 s- /* DMA Stream enable */3 @( O/ V! [, ]0 ]
- // DMA_Cmd(DMA2_Stream7, ENABLE);: K3 a' T6 Y* B
- }
复制代码
$ A$ Y7 A5 S+ m% M5 A6 D4 T+ P& U. t( \% `. K
这里也是常规的DMA配置流程,不明白的同学请看文章《STM32DMA详解》,这里值得注意的是,配置完成并没有使能DMA2_Stream7,使能了就会立即将UART_Buffer的数据发送出去。
$ C3 I- r! A9 I0 l/ O6 M( A4 Y' ^. S: [+ Q- y$ E$ ]. _
其他代码处理
! G9 O( o: O& S( a. h! ]# o- B' o" R
- void USART1_IRQHandler(void)3 Y( X% y5 L* B9 h% X
- {
1 {: y- v# H2 Q- I - uint8_t temp;& T2 u! s B* k2 K
- if(USART_GetFlagStatus(USART1, USART_FLAG_IDLE) == SET)
& ~. M* U! i5 k# n. E: j2 G - {8 E' q2 J' [8 L- f9 j
- DealWith_UartData();- \/ N6 O$ n9 m ]! R
- // USART_ClearFlag(USART1, USART_FLAG_IDLE);
2 i, Z2 _# n, d - temp = USART1->SR;
% U; H# i' G0 u ^$ I2 w - temp = USART1->DR; //清USART_IT_IDLE标志 ' k8 w$ {- O, d
- }8 \- Y+ q8 E( R1 R" A
- }0 q6 ~" O2 ]' d( ~
- 0 n9 S$ k9 A1 ]( H
- void DealWith_UartData()
5 u$ K2 g: b' S, y# q - {5 p; }( J3 }) `' `- D) d
- DMA_Cmd(DMA2_Stream2, DISABLE);" d- o! o% P, w8 @! U% K7 _3 g
- UART_Receive_flg = 1;
- w( e( e- q) V! e6 W - UART_Receive_len = UART_RX_LEN - DMA_GetCurrDataCounter(DMA2_Stream2);
6 c2 E' Z) ~7 z, A9 k - UART_Buffer[UART_Receive_len] = 0;: [: A5 u4 C) y' ]( `- _9 I' b
- DMA_SetCurrDataCounter(DMA2_Stream2,UART_RX_LEN);
( o. t* q) ^; ^1 o& K. B2 H1 ~ - DMA_ClearFlag(DMA2_Stream2, DMA_FLAG_TCIF2);
# w% ]: {% W, H- C - DMA_Cmd(DMA2_Stream2, ENABLE);7 w+ m2 A/ k) Y1 x
- }
( g, U! l# W7 u- ^. [1 ~ - * E7 Y$ f. k z# w
- int main(void)6 m- j3 A, ~" S8 s7 @
- {
9 `: }) K; J2 H1 u2 ?8 a - UART_Receive_flg = 0;
% ]9 C3 C) @* G, @! U# Q( e( v" d - 4 }! ^8 @# K1 i4 l: q: r: W6 H
- Uart_Reveice_DMA_Config();! X9 x) c- ?- V% [ Y
- Uart_Send_DMA_Config();
3 Q$ e/ l* H% `( T - UART_Init();
$ K+ j$ g5 U& _4 {( V' T* y - + {& _" _4 L& Z4 \
- while (1)
/ f0 p: a2 R5 Q7 p: Z: v - {
4 E; n, Y: ]5 h. I$ M - if(UART_Receive_flg)( X6 y$ p! r. @* U+ ]: v
- { |. ~$ z% N" r% p3 l9 k% n& J
- UART_Receive_flg = 0;2 j. u& K# j8 `; u, S1 l- I
- Uart_Send_DMA_Start();/ ^% f$ P7 t! \1 ~6 l
- }
; Z' T: V+ F8 ]0 r/ d( Z - }
4 l& w7 z; v' z& Q7 m - }
1 Z$ |& f G# U8 [8 K1 G! _! t" @
复制代码 2 M! ^% Z! |, s/ O
上面3个函数,简单逻辑就是,当串口使用DMA接收了一定量的数据,就会通过串口DMA发送出去,串口DMA发送的代码如下:/ V' ^" ~- V+ u( D" ^/ z
8 K4 j/ w' ]! u. T- void Uart_Send_DMA_Start(void)
: ]& L' f2 S) W& B) ^ - {
4 h& G; z" z/ O5 J: o" r' M - DMA_SetCurrDataCounter(DMA2_Stream7,UART_Receive_len);
s* E4 s% a# b& L9 C9 @( J2 z - DMA_ClearFlag(DMA2_Stream7, DMA_FLAG_TCIF7);- o S0 r7 m" j4 d: w( G0 y
- /* DMA Stream enable */
8 H8 ^: Y: _6 j& U; i2 o% ^& f - DMA_Cmd(DMA2_Stream7, ENABLE);! N' m- a3 g: N- u7 ? z; H& [
- }
复制代码 |1 k( B. N: L, k8 O) R4 Q8 B3 H9 M
03、后记
! W" d4 K, e* ]/ a! y, \1 c这一篇很简单,就是DMA使用的一个延伸,上面说了这么多,也贴了很多代码,不可能将所有代码全部贴出来,作为软件工程师,还是在IDE里看代码方便,如果感兴趣的话,可以到下面github链接下载代码,Keil和IAR的工程文件都有。$ T4 O, h/ v" w2 _# i; D
+ M$ }- V7 P* m; Q
: |# y+ w2 B* G% l" E* J
|