你的浏览器版本过低,可能导致网站不能正常访问!
为了你能正常使用网站功能,请使用这些浏览器。

STM32使用DMA发送串口数据

[复制链接]
STMCU小助手 发布时间:2021-7-20 13:45
01概述
' @+ o) Z7 s( u$ M! |' t+ J9 E- J9 [- I* q+ B( x# y

6 P% m: K6 j  q& b  K" T上一篇文章《STM32使用DMA接收串口数据》讲解了如何使用DMA接收数据,使用DMA外设和串口外设,使用的中断是串口空闲中断。本篇文章主要讲解使用DMA发送数据,不会讲解基础的串口和DMA知识,直接上代码,如果有同学对DMA和串口都不熟悉,建议看一下上篇文章《STM32使用DMA接收串口数据》
* b8 n. Y1 W; f: S8 N. F使用DMA发送数据,首先我们要确认使用的串口有没有DMA。" o+ |/ A: Y' ^  Q* S5 ]; L1 r! c
我们使用USART1串口外设,从数据手册中可以查到,USART1的发送和接收都是支持DMA的,使用的是DMA2.
  ?+ s9 Z% P) Q( N% R5 U 1.jpg
, m; [* r7 U/ R2 L接下来就是撸代码的时刻了
; T. W( _2 R) |% B- f8 U% t- D& |3 I! Y& b  x9 r, [
  [' B, D; [+ ?9 ?( \: H( Y
* f/ g2 e0 }- {, O7 W
) t- f6 R( d; k/ `- m1 z
02代码0 {/ Q, [' g* g4 F7 |+ G
' d( B- w* w( [& d/ r, ~9 r( J

# h" c" D7 F) V5 @# _* \DMA串口发送的代码是在上一篇文章DMA串口接收的基础上修改的。
9 k/ R, {, i& u6 }9 h; b2 Z" }9 O* C
  1. <font face="微软雅黑" size="3">void UART_Init(void)5 T* }7 F2 [- `( h/ q
  2. {* [0 ?' E& l( C4 X' h
  3.   USART_InitTypeDef USART_InitStructure;+ y8 H7 ~8 B1 ^+ X
  4.   GPIO_InitTypeDef GPIO_InitStructure;) w" y) o! K4 \
  5.   NVIC_InitTypeDef NVIC_InitStructure;# J6 n  k5 z& q: O* N# s# ]

  6. ' b1 ^. e$ m3 A" N- T% v: s
  7.   /* Enable GPIO clock */$ M% n" a0 T6 ?& G$ m. l* y3 I
  8.   RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);0 r' b2 H; Y3 [0 ^, E0 J2 X
  9.   /* Enable UART1 clock */3 ~7 ?; B3 q) {9 \0 v% A
  10.   RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);
    2 Q+ \/ O% i3 X/ {
  11.   /* Connect PXx to USARTx_Tx*/
    $ |/ n& y3 j- e( u3 j' g9 b
  12.   GPIO_PinAFConfig(GPIOA, 9, GPIO_AF_USART1);
    2 H5 ?3 p; j# n3 p: j
  13. 1 r4 x( ^4 Z- z
  14.   /* Connect PXx to USARTx_Rx*/0 K2 O) N5 H" v) u/ H! S2 t% a
  15.   GPIO_PinAFConfig(GPIOA, 10, GPIO_AF_USART1);9 B$ R+ N) C5 B) i* s! s5 l

  16. 6 z' I/ h, p4 a/ N/ T5 r
  17.   /* Configure USART Tx as alternate function  */3 K% s9 e. G9 p2 D: S
  18.   GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
    - E' F4 r* ~; O6 v4 K# B
  19.   GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;
    # m- Z) y& _; C' y5 ~! L
  20.   GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;) |7 n& ?6 R6 ~, W- F* G
  21. / p- X7 j' p( W
  22.   GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;, Y9 h% L! `4 \% `
  23.   GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    0 R3 h# f$ g3 B; e, k; i
  24.   GPIO_Init(GPIOA, &GPIO_InitStructure);
    % h* i* F7 c, K% b6 P) g* Z9 A
  25. # ]) M2 `; P/ ?+ V
  26.   /* Configure USART Rx as alternate function  */9 u8 |+ Y4 ?. D4 ^. d) |0 u. Q2 i
  27.   GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;- N  @" B- U* X" q
  28.   GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;& |; E: S# N9 V: b- o! ?
  29.   GPIO_Init(GPIOA, &GPIO_InitStructure);9 l# g5 @5 y2 z6 J- k5 y' i

  30. 3 r$ Z) I$ o6 A8 J+ S$ a
  31.   USART_InitStructure.USART_BaudRate = 115200;  d; u* u; L/ F& j8 \% m
  32.   USART_InitStructure.USART_WordLength = USART_WordLength_8b;
    ) b: b6 Y! M" L* `" F
  33.   USART_InitStructure.USART_StopBits = USART_StopBits_1;
    3 q" S- `% [& q$ E% {. W7 b0 {! z0 g
  34.   USART_InitStructure.USART_Parity = USART_Parity_No;
    1 N3 D& C' E) X/ h( }& K  K9 J
  35.   USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;) z: H% f) K+ `2 ?, y2 O
  36.   USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
    ) q+ E8 F- W  i; ~% p5 M% c

  37. 2 M; F+ N3 e/ }) n6 L
  38.   /* USART configuration */" v5 G1 B: o7 A; ~# [  ]
  39.   USART_Init(USART1, &USART_InitStructure);
    0 }) u* t9 R, X7 k0 }) x3 A4 G" p

  40. 1 K( i1 n4 K% h- {: Y1 F% t% q# w$ w, B
  41.   USART_ITConfig(USART1, USART_IT_IDLE, ENABLE);; I& h2 i  J) q0 g8 _$ p. L# l

  42. 5 N9 f1 C# |! d
  43.   /* Enable the USARTx Interrupt */9 s: k: e' ]! E# |/ s  y
  44.   NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;
    & l5 I0 w4 h' Z
  45.   NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority =0;
    # i3 j/ c) e/ l* K) w
  46.   NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
    + \5 U* \  D: p, O6 Q5 B* P, O/ q
  47.   NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;0 ^: ~/ u( _1 d' x  R# z
  48.   NVIC_Init(&NVIC_InitStructure);
    ( j- r* f8 ?0 |# C, J
  49. 1 ?) i% q" D0 _$ X0 i7 _' w* A) d
  50.   /*使能串口DMA接收*/
    ! i; y2 g) c" E9 n- ?! K
  51.   USART_DMACmd(USART1, USART_DMAReq_Rx, ENABLE);. M3 ^' w. p5 l( U3 c. u0 z  H* S# B
  52.   /*使能串口DMA发送*/
    9 P! g( w' c# |! m' R
  53.   USART_DMACmd(USART1, USART_DMAReq_Tx, ENABLE);' p# @1 u- S: ]
  54.   j$ E4 F+ G2 n/ }; P% p
  55.   /* Enable USART */& m+ U$ s; ], ]1 g4 E4 G; v
  56.   USART_Cmd(USART1, ENABLE);/ C6 z6 h$ n+ @7 k( w  ^
  57. }</font>
复制代码
在这里除了常规的串口配置,我们需要配置串口的DMA发送,和串口DMA接收一样的API函数,参数修改为USART_DMAReq_Tx即可。
! r, B  z. X! \3 o( m# m7 B串口DMA发送配置
4 T, C0 d" q, Y' w( f" z/ k
  1. <font face="微软雅黑" size="3">void Uart_Send_DMA_Config(void)6 J, Y: v* m7 C& Z
  2. {
    9 H& J" |+ q' W) ]; ^! M
  3.   DMA_InitTypeDef  DMA_InitStructure;
    3 O5 q) T" d# {# y
  4. $ c3 ^" R  _; I4 ^/ M6 e. q) {
  5.   /* Enable DMA clock */
    . _. t! z- N5 e% ^' F6 i# ~
  6.   RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA2, ENABLE);2 V, i& Y' c1 C9 P  z2 a
  7. , @- \6 n4 \- p/ P) s1 x4 ?1 F' [
  8.   /* Reset DMA Stream registers (for debug purpose) */4 {6 V% s: v5 D8 ^" W7 z1 Q+ \
  9.   DMA_DeInit(DMA2_Stream7);9 o/ H  y3 Z5 J

  10. 0 f! u9 s. I- n) F5 A
  11.   /* Check if the DMA Stream is disabled before enabling it.
    6 ~# z( c" S# r, u- h  X: a: X' b
  12.      Note that this step is useful when the same Stream is used multiple times:
    6 |$ ^! P# d9 M
  13.      enabled, then disabled then re-enabled... In this case, the DMA Stream disable+ x# s8 j. D: y6 F( E* \
  14.      will be effective only at the end of the ongoing data transfer and it will 2 T0 Y  R* J9 M
  15.      not be possible to re-configure it before making sure that the Enable bit
    - i1 [$ g9 ^, g3 y
  16.      has been cleared by hardware. If the Stream is used only once, this step might
    2 h4 K1 A" H6 N: d. t& g0 D
  17.      be bypassed. */
    ; Z* H3 H8 d! Y  _" `$ U2 h, H
  18.   while (DMA_GetCmdStatus(DMA2_Stream7) != DISABLE)
    / T. w7 V+ C1 O2 E& y; s" b1 d5 d
  19.   {% z8 o7 n. \/ P2 I  ?9 ~
  20.   }
    9 \+ D9 N  p; m6 e4 b' i
  21. 9 v& @  Z; [2 t% R
  22.   /* Configure DMA Stream */
    0 a8 m" @- }1 t' c! _
  23.   DMA_InitStructure.DMA_Channel = DMA_Channel_4;  //DMA请求发出通道
    : ^8 T; n5 |$ f9 J+ k
  24.   DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&USART1->DR;//配置外设地址. }8 P# [8 j8 M) K' X* @, D% U
  25.   DMA_InitStructure.DMA_Memory0BaseAddr = (uint32_t)UART_Buffer;//配置存储器地址
    * o$ l) U0 i; ]: y9 n; ^4 y0 ?
  26.   DMA_InitStructure.DMA_DIR = DMA_DIR_MemoryToPeripheral;//传输方向配置
    # B: ?( e" ~. ^" l! ?( p+ Y1 o
  27.   DMA_InitStructure.DMA_BufferSize = (uint32_t)UART_RX_LEN;//传输大小" \: ^' M5 o" y1 O# ~. e/ A
  28.   DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;//外设地址不变
    & c7 n" W7 e5 `! n' i1 N2 T/ Z8 Q, y
  29.   DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;//memory地址自增7 I' p; `- I$ H$ M3 {% P
  30.   DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;//外设地址数据单位, |4 U! |  A6 E/ T; ^% A' _
  31.   DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;//memory地址数据单位& Z( I2 Q8 ^, l3 i! e
  32.   DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;//DMA模式:正常模式
    & Y" x' x7 Z' D& y* s/ ]
  33.   DMA_InitStructure.DMA_Priority = DMA_Priority_High;//优先级:高
    " @, s, P6 ]* C' u* a& t
  34.   DMA_InitStructure.DMA_FIFOMode = DMA_FIFOMode_Disable;//FIFO 模式不使能.          9 n! T1 r1 R- |, h7 M5 }# u+ _
  35.   DMA_InitStructure.DMA_FIFOThreshold = DMA_FIFOThreshold_Full;// FIFO 阈值选择
    . p0 J& ?, V. z
  36.   DMA_InitStructure.DMA_MemoryBurst = DMA_MemoryBurst_Single;//存储器突发模式选择,可选单次模式、 4 节拍的增量突发模式、 8 节拍的增量突发模式或 16 节拍的增量突发模式。
    ; Y0 e- p% M3 O
  37.   DMA_InitStructure.DMA_PeripheralBurst = DMA_PeripheralBurst_Single;//外设突发模式选择,可选单次模式、 4 节拍的增量突发模式、 8 节拍的增量突发模式或 16 节拍的增量突发模式。
    ) ]6 d. M2 t" C/ u: y: b
  38.   DMA_Init(DMA2_Stream7, &DMA_InitStructure); ' g; W- X# R6 {+ D6 a
  39. 6 T' b6 d$ B9 F+ I9 l
  40.   /* DMA Stream enable */# ^! d; W1 x1 K; [( D
  41. //  DMA_Cmd(DMA2_Stream7, ENABLE);; F0 L: H1 k' a
  42. }</font>
复制代码
这里也是常规的DMA配置流程,不明白的同学请看文章《STM32DMA详解》,这里值得注意的是,配置完成并没有使能DMA2_Stream7,使能了就会立即将UART_Buffer的数据发送出去。0 c% k0 w1 Y5 d5 W- y& S
其他代码处理) l4 E" f  O2 a
  1. <font face="微软雅黑" size="3">void USART1_IRQHandler(void)
    6 e: z& e% j$ w" u5 D& s
  2. {( k  @+ Z8 S. Y5 Y% @
  3.   uint8_t temp;
    1 K5 K( u% \0 K* f! K3 p
  4.   if(USART_GetFlagStatus(USART1, USART_FLAG_IDLE) == SET)& ~' ?& N! N* m
  5.   {
    , j. w' L2 L: S  U1 J7 }5 I& }
  6.     DealWith_UartData();
    ) ]; G2 @5 W+ ^2 x; k7 g, i
  7. //    USART_ClearFlag(USART1, USART_FLAG_IDLE);
    # h' }8 s& b4 I) t1 z5 M) U" K
  8.     temp = USART1->SR;  ; G2 m' X0 m5 j# N3 M
  9.     temp = USART1->DR; //清USART_IT_IDLE标志    y1 B/ h* ]! n
  10.   }
    " O9 I, y, M; |/ w# P; u, m
  11. }
    2 v' W" K' c  n; y  H& u  _
  12. # S8 r  G. Y" @& k% @
  13. void DealWith_UartData()
    / l/ c; Z, O+ F, Z
  14. {
    3 @4 I4 H3 z7 o& |2 `3 P+ _4 E
  15.   DMA_Cmd(DMA2_Stream2, DISABLE);
    ( }4 n+ V2 U' x7 X7 U, R
  16.   UART_Receive_flg = 1;
    - X! @0 M; I! Q4 h6 x
  17.   UART_Receive_len = UART_RX_LEN - DMA_GetCurrDataCounter(DMA2_Stream2);# {8 f& L' F5 i! g1 y9 j9 b
  18.   UART_Buffer[UART_Receive_len] = 0;* V* g* y5 y+ Y1 V! A& p7 @* ^
  19.   DMA_SetCurrDataCounter(DMA2_Stream2,UART_RX_LEN);
    ! R3 k4 |) _3 H5 a# I7 z% {
  20.   DMA_ClearFlag(DMA2_Stream2, DMA_FLAG_TCIF2);* g: [2 j9 z( d- t5 ]! T& V7 \
  21.   DMA_Cmd(DMA2_Stream2, ENABLE);) s$ v1 C& @8 U8 E7 g/ [; u" u
  22. }
    & {& g6 q/ I9 M" \" [# f) y5 L& X

  23. : f) s( |; B- E5 L. G# W: k) N& O
  24. int main(void). A$ x% c+ P' b$ j
  25. {
    7 W) k1 ?$ M5 g( T8 @: ^
  26.   UART_Receive_flg = 0;. \  g/ u9 M) \
  27. + Y! j! s* A7 _+ x
  28.   Uart_Reveice_DMA_Config();+ {; {+ }6 F/ V% ~% `, ]
  29.   Uart_Send_DMA_Config();
    6 S% u. S# ^' E- H
  30.   UART_Init();
    , d  k" E2 |0 Z" p) \$ w

  31. ! U9 l' I4 |) }/ E2 l/ i
  32.   while (1)
    % p" S( K" S8 n9 ~6 q- y' F0 n" P
  33.   {5 g$ o8 w$ g% o5 V/ T
  34.     if(UART_Receive_flg); p" z8 `& L+ u* _
  35.     {
    / H" o1 f1 a, N
  36.       UART_Receive_flg = 0;7 t2 i& w0 t) W! u' D; }
  37.       Uart_Send_DMA_Start();
    $ H" y4 n: b- w, Z/ u
  38.     }( u4 B, E! j3 E$ u# S9 O
  39.   }
      q, a4 \( K/ E# t0 F: S; D
  40. }</font>
复制代码
上面3个函数,简单逻辑就是,当串口使用DMA接收了一定量的数据,就会通过串口DMA发送出去,串口DMA发送的代码如下:! }* V' n+ |( p# Q# V' c& F
  1. <font face="微软雅黑" size="3">void Uart_Send_DMA_Start(void)' o) O$ N* w7 m
  2. {& r7 @9 M; Q1 N2 {7 @6 F
  3.   DMA_SetCurrDataCounter(DMA2_Stream7,UART_Receive_len);
    2 c$ X$ j. E! N* V
  4.   DMA_ClearFlag(DMA2_Stream7, DMA_FLAG_TCIF7);
    7 e' e" Z% ~2 r  w- V: m
  5.   /* DMA Stream enable */
    6 T& {( i: x! [
  6.   DMA_Cmd(DMA2_Stream7, ENABLE);, S8 f' W+ Q' p  G6 X
  7. }</font>
复制代码

" {- C9 V/ w. p9 V: t6 r
* O! \1 l, g; u; s) f% {9 v7 m
收藏 评论0 发布时间:2021-7-20 13:45

举报

0个回答
关于意法半导体
我们是谁
投资者关系
意法半导体可持续发展举措
创新和工艺
招聘信息
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
关注我们
st-img 微信公众号
st-img 手机版