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

【经验分享】STM32使用DMA发送串口数据

[复制链接]
STMCU小助手 发布时间:2022-4-16 21:00
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 7e3832c538e49222cdf7e700940ac823.png   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
  1. void UART_Init(void)
    $ H6 Q5 u% `2 M; @+ H
  2. {6 j4 w3 A8 j* `: Z
  3.   USART_InitTypeDef USART_InitStructure;
    . d& ^" n. x0 v9 I" p& V/ \! b0 T
  4.   GPIO_InitTypeDef GPIO_InitStructure;
      w5 H) G& E9 F) ?' f
  5.   NVIC_InitTypeDef NVIC_InitStructure;" `) M# w, Z1 L
  6. : E4 J" P6 G7 e) n
  7.   /* Enable GPIO clock */
    , [' D0 Y1 R  ^
  8.   RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);
    + {) h6 Q& s$ C/ G& d' O8 p5 c
  9.   /* Enable UART1 clock */
    5 Y+ A2 T$ M. n4 Z
  10.   RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);
    ' q2 I4 @' ]1 U2 X( {' \: O
  11.   /* Connect PXx to USARTx_Tx*/
    8 p; b9 ?* W+ K
  12.   GPIO_PinAFConfig(GPIOA, 9, GPIO_AF_USART1);
    ! b' `* E5 H# \' W* j4 O

  13. $ V2 ]( e, p7 F
  14.   /* Connect PXx to USARTx_Rx*/
    - J0 i+ e( h( A4 `- C4 p5 @& N
  15.   GPIO_PinAFConfig(GPIOA, 10, GPIO_AF_USART1);; Y( b  e( y( @- Q2 l- }
  16. 1 ~( [3 Q8 {4 x! J9 a
  17.   /* Configure USART Tx as alternate function  */5 r& i4 D! w+ m9 v
  18.   GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
    9 k. j* x2 V$ Y; J5 R& i4 H( W
  19.   GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;
    4 [& z7 W; V: F0 X7 e
  20.   GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;7 A4 ]  `+ K2 _

  21. / {. ]9 i: H, Q$ U
  22.   GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;! H( F; f: ]4 a$ t
  23.   GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;( G: v4 I1 z5 ?9 t* x
  24.   GPIO_Init(GPIOA, &GPIO_InitStructure);
    4 F7 S+ E( F  Z* ^- Z% p

  25. $ _; h  M. c& A
  26.   /* Configure USART Rx as alternate function  */
    9 x0 H( v1 m1 K% e
  27.   GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;" u2 x( C- G! _
  28.   GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
    / m, b# O, F( I# y1 [
  29.   GPIO_Init(GPIOA, &GPIO_InitStructure);
    1 v  A% l) Y4 x& r2 n8 ]; B0 b6 h

  30. " B, m8 i# B8 Q! s9 z: N# x1 Q
  31.   USART_InitStructure.USART_BaudRate = 115200;5 s5 m# ~, `, l6 F8 A
  32.   USART_InitStructure.USART_WordLength = USART_WordLength_8b;3 u5 ]4 b' r: Y. t3 E+ F/ G* r
  33.   USART_InitStructure.USART_StopBits = USART_StopBits_1;" T# F# T- X5 w0 |
  34.   USART_InitStructure.USART_Parity = USART_Parity_No;
    0 E- h. O. T; M+ |: x
  35.   USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;- M$ i1 ]) v$ i, [
  36.   USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;& b+ K7 X% I" J" J' y) Y3 \
  37. : `2 W5 n' j* A% r: F
  38.   /* USART configuration */
    $ {; ~) f+ c5 J  r1 ^+ n
  39.   USART_Init(USART1, &USART_InitStructure);5 g& B/ C0 o/ v2 o7 S. ^/ G
  40. 7 Z7 n6 R! z+ e+ {7 b4 ], D$ h
  41.   USART_ITConfig(USART1, USART_IT_IDLE, ENABLE);6 c2 M. a1 A% h8 W+ I. q

  42. 8 u# R% h7 v+ \
  43.   /* Enable the USARTx Interrupt */. n9 k, V" F4 o/ V" w7 t
  44.   NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;; h/ o8 c0 a( q( N' `+ S" ?
  45.   NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority =0;
    8 t: x) v5 _0 ^" @
  46.   NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
    7 Q6 }% x, i# H# _( A* Z: {$ ]* i; v
  47.   NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
    3 y$ {5 j* e2 u% \) r9 X1 G& ^
  48.   NVIC_Init(&NVIC_InitStructure);
      Y! W% d  l, t4 U3 e) F0 V- a- E
  49. + f* P5 a! @9 C
  50.   /*使能串口DMA接收*/. Y/ e6 ^2 @9 R2 V
  51.   USART_DMACmd(USART1, USART_DMAReq_Rx, ENABLE);
    5 z, p" Q2 Y# n+ f
  52.   /*使能串口DMA发送*/
    2 A% ~# `& w* f: u# P1 @. W
  53.   USART_DMACmd(USART1, USART_DMAReq_Tx, ENABLE);
    6 o8 Z9 y7 c2 i7 S
  54. 2 \1 @, A; l+ `% k* H
  55.   /* Enable USART */4 `- f1 g0 o- I2 y+ J
  56.   USART_Cmd(USART1, ENABLE);0 `  ?# Q) }% x4 t- E
  57. }
复制代码

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
  1. void Uart_Send_DMA_Config(void)
    ' d* c  x* A" A! l7 L/ G
  2. {
    7 A3 u- t, g( O/ Q# L  E7 n
  3.   DMA_InitTypeDef  DMA_InitStructure;1 m# Z8 x7 D  _! g3 e
  4. + c# f, m: b" t8 l6 z9 \0 I
  5.   /* Enable DMA clock */$ @8 I; p0 |9 d& l; j* [5 d: w
  6.   RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA2, ENABLE);
    2 V% T# U% v; e: X) v! d

  7. 8 b* \, |8 d4 v# g$ P
  8.   /* Reset DMA Stream registers (for debug purpose) */" U: s9 [$ a2 s1 y( |
  9.   DMA_DeInit(DMA2_Stream7);
    6 t9 T7 @3 \  \& g

  10. 2 H2 ^; V" y8 U0 h* n0 I3 c
  11.   /* Check if the DMA Stream is disabled before enabling it.
    " V0 O! x- u0 f
  12.      Note that this step is useful when the same Stream is used multiple times:  O5 u4 T7 i' c4 H8 e- y1 m
  13.      enabled, then disabled then re-enabled... In this case, the DMA Stream disable
    1 r) f  i- r  y) P6 W* [# I
  14.      will be effective only at the end of the ongoing data transfer and it will
    + Z6 y$ U, S( }/ b. R
  15.      not be possible to re-configure it before making sure that the Enable bit % k+ ?+ ]5 e8 X/ |$ w
  16.      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
  17.      be bypassed. */
    6 g, Y" T) A( I: T& {' D# h
  18.   while (DMA_GetCmdStatus(DMA2_Stream7) != DISABLE)
    " Q( M* s3 _0 e5 I& O1 G/ S0 ]
  19.   {; @; q9 b6 \6 Y9 G$ }7 J
  20.   }
    $ o  m) V' v  }1 N6 ~* X
  21. . E* X6 u# k2 ~4 n- T% S9 C" D
  22.   /* Configure DMA Stream */) X. c5 E6 u) j* q# |, h
  23.   DMA_InitStructure.DMA_Channel = DMA_Channel_4;  //DMA请求发出通道
    0 u3 [# T' a+ z, g
  24.   DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&USART1->DR;//配置外设地址8 u# j4 L0 C# V$ N5 O7 v
  25.   DMA_InitStructure.DMA_Memory0BaseAddr = (uint32_t)UART_Buffer;//配置存储器地址
    % J( f; r* _3 ?2 H
  26.   DMA_InitStructure.DMA_DIR = DMA_DIR_MemoryToPeripheral;//传输方向配置
    7 j+ r" O/ g9 w% X& \/ R
  27.   DMA_InitStructure.DMA_BufferSize = (uint32_t)UART_RX_LEN;//传输大小4 t$ L+ P# K8 j) A: a% C
  28.   DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;//外设地址不变5 H6 Y. ~% J; c, D# v
  29.   DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;//memory地址自增
    / B1 a& `. Z- L, P5 B) Z
  30.   DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;//外设地址数据单位
    9 U$ Z. J7 |2 K- Y: f" ~: g
  31.   DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;//memory地址数据单位& H8 h7 L% w$ B/ h& L, C' f1 E
  32.   DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;//DMA模式:正常模式
    1 n- |/ G4 k8 g. O. a
  33.   DMA_InitStructure.DMA_Priority = DMA_Priority_High;//优先级:高5 ~* c4 C+ O  [6 Y$ x
  34.   DMA_InitStructure.DMA_FIFOMode = DMA_FIFOMode_Disable;//FIFO 模式不使能.         
    " R* H" E- `' P3 I/ |0 x
  35.   DMA_InitStructure.DMA_FIFOThreshold = DMA_FIFOThreshold_Full;// FIFO 阈值选择$ K, R+ M' `1 V0 e1 _
  36.   DMA_InitStructure.DMA_MemoryBurst = DMA_MemoryBurst_Single;//存储器突发模式选择,可选单次模式、 4 节拍的增量突发模式、 8 节拍的增量突发模式或 16 节拍的增量突发模式。
    3 A. `: i" E4 x8 c1 y
  37.   DMA_InitStructure.DMA_PeripheralBurst = DMA_PeripheralBurst_Single;//外设突发模式选择,可选单次模式、 4 节拍的增量突发模式、 8 节拍的增量突发模式或 16 节拍的增量突发模式。
    ' v, g( Z' p) o* }, O; n$ Z5 U
  38.   DMA_Init(DMA2_Stream7, &DMA_InitStructure); 2 o! Z& f8 A6 t* C9 ?/ ?# K

  39. # O9 I% S8 ], k- N0 s
  40.   /* DMA Stream enable */3 @( O/ V! [, ]0 ]
  41. //  DMA_Cmd(DMA2_Stream7, ENABLE);: K3 a' T6 Y* B
  42. }
复制代码

$ 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
  1. void USART1_IRQHandler(void)3 Y( X% y5 L* B9 h% X
  2. {
    1 {: y- v# H2 Q- I
  3.   uint8_t temp;& T2 u! s  B* k2 K
  4.   if(USART_GetFlagStatus(USART1, USART_FLAG_IDLE) == SET)
    & ~. M* U! i5 k# n. E: j2 G
  5.   {8 E' q2 J' [8 L- f9 j
  6.     DealWith_UartData();- \/ N6 O$ n9 m  ]! R
  7. //    USART_ClearFlag(USART1, USART_FLAG_IDLE);
    2 i, Z2 _# n, d
  8.     temp = USART1->SR;  
    % U; H# i' G0 u  ^$ I2 w
  9.     temp = USART1->DR; //清USART_IT_IDLE标志  ' k8 w$ {- O, d
  10.   }8 \- Y+ q8 E( R1 R" A
  11. }0 q6 ~" O2 ]' d( ~
  12. 0 n9 S$ k9 A1 ]( H
  13. void DealWith_UartData()
    5 u$ K2 g: b' S, y# q
  14. {5 p; }( J3 }) `' `- D) d
  15.   DMA_Cmd(DMA2_Stream2, DISABLE);" d- o! o% P, w8 @! U% K7 _3 g
  16.   UART_Receive_flg = 1;
    - w( e( e- q) V! e6 W
  17.   UART_Receive_len = UART_RX_LEN - DMA_GetCurrDataCounter(DMA2_Stream2);
    6 c2 E' Z) ~7 z, A9 k
  18.   UART_Buffer[UART_Receive_len] = 0;: [: A5 u4 C) y' ]( `- _9 I' b
  19.   DMA_SetCurrDataCounter(DMA2_Stream2,UART_RX_LEN);
    ( o. t* q) ^; ^1 o& K. B2 H1 ~
  20.   DMA_ClearFlag(DMA2_Stream2, DMA_FLAG_TCIF2);
    # w% ]: {% W, H- C
  21.   DMA_Cmd(DMA2_Stream2, ENABLE);7 w+ m2 A/ k) Y1 x
  22. }
    ( g, U! l# W7 u- ^. [1 ~
  23. * E7 Y$ f. k  z# w
  24. int main(void)6 m- j3 A, ~" S8 s7 @
  25. {
    9 `: }) K; J2 H1 u2 ?8 a
  26.   UART_Receive_flg = 0;
    % ]9 C3 C) @* G, @! U# Q( e( v" d
  27. 4 }! ^8 @# K1 i4 l: q: r: W6 H
  28.   Uart_Reveice_DMA_Config();! X9 x) c- ?- V% [  Y
  29.   Uart_Send_DMA_Config();
    3 Q$ e/ l* H% `( T
  30.   UART_Init();
    $ K+ j$ g5 U& _4 {( V' T* y
  31. + {& _" _4 L& Z4 \
  32.   while (1)
    / f0 p: a2 R5 Q7 p: Z: v
  33.   {
    4 E; n, Y: ]5 h. I$ M
  34.     if(UART_Receive_flg)( X6 y$ p! r. @* U+ ]: v
  35.     {  |. ~$ z% N" r% p3 l9 k% n& J
  36.       UART_Receive_flg = 0;2 j. u& K# j8 `; u, S1 l- I
  37.       Uart_Send_DMA_Start();/ ^% f$ P7 t! \1 ~6 l
  38.     }
    ; Z' T: V+ F8 ]0 r/ d( Z
  39.   }
    4 l& w7 z; v' z& Q7 m
  40. }
    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
  1. void Uart_Send_DMA_Start(void)
    : ]& L' f2 S) W& B) ^
  2. {
    4 h& G; z" z/ O5 J: o" r' M
  3.   DMA_SetCurrDataCounter(DMA2_Stream7,UART_Receive_len);
      s* E4 s% a# b& L9 C9 @( J2 z
  4.   DMA_ClearFlag(DMA2_Stream7, DMA_FLAG_TCIF7);- o  S0 r7 m" j4 d: w( G0 y
  5.   /* DMA Stream enable */
    8 H8 ^: Y: _6 j& U; i2 o% ^& f
  6.   DMA_Cmd(DMA2_Stream7, ENABLE);! N' m- a3 g: N- u7 ?  z; H& [
  7. }
复制代码
  |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
收藏 评论0 发布时间:2022-4-16 21:00

举报

0个回答
关于
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
意法半导体官网
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
官方最新发布
STM32N6 AI生态系统
STM32MCU,MPU高性能GUI
ST ACEPACK电源模块
意法半导体生物传感器
STM32Cube扩展软件包
关注我们
st-img 微信公众号
st-img 手机版