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

STM32使用DMA发送串口数据

[复制链接]
STMCU小助手 发布时间:2021-7-20 13:45
01概述! y; c! [& S: L9 e( U3 D' A7 ~( I
) t, \7 u. m7 q. N

" d$ `0 |% b& A) f' ]6 E$ L上一篇文章《STM32使用DMA接收串口数据》讲解了如何使用DMA接收数据,使用DMA外设和串口外设,使用的中断是串口空闲中断。本篇文章主要讲解使用DMA发送数据,不会讲解基础的串口和DMA知识,直接上代码,如果有同学对DMA和串口都不熟悉,建议看一下上篇文章《STM32使用DMA接收串口数据》
' ?! D: ?  Y* H7 W* n使用DMA发送数据,首先我们要确认使用的串口有没有DMA。" F/ P) b5 T. F( e2 E+ m2 O
我们使用USART1串口外设,从数据手册中可以查到,USART1的发送和接收都是支持DMA的,使用的是DMA2.0 D$ `2 T+ V1 @( i! v
1.jpg
: t2 p/ j! Y0 G接下来就是撸代码的时刻了& e1 M$ e! ]+ G5 f* i' n/ _; o( g; h- o
; V: F+ E# S% L: ^. Q: Q) \, t
4 {6 X* _- G' m

' h$ A3 S/ l$ m$ V* j
' C6 x/ B4 k) B5 U: c
02代码+ |. b% j- g( |6 V' y- z, t
5 _. @) i6 Y2 Y  K

  X+ j4 r/ F' d, JDMA串口发送的代码是在上一篇文章DMA串口接收的基础上修改的。, F6 q4 A" Q6 k' s
  1. <font face="微软雅黑" size="3">void UART_Init(void)0 I& d4 G& \8 m2 w, e
  2. {
    1 d( H6 `# I5 h+ _- c9 I
  3.   USART_InitTypeDef USART_InitStructure;
    & Z% S4 o5 ]9 b1 F1 w
  4.   GPIO_InitTypeDef GPIO_InitStructure;$ Y/ P: h: o% _
  5.   NVIC_InitTypeDef NVIC_InitStructure;/ p$ m3 ]$ y' t+ C. g
  6. 9 l1 P8 h3 Z/ t/ _9 n
  7.   /* Enable GPIO clock */
    * l, F1 ^7 `1 K1 i- ]$ k
  8.   RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);
    1 l7 Y, p' r- [8 j' ]$ B
  9.   /* Enable UART1 clock */! d% s' }5 V7 T$ h0 E/ Q
  10.   RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);
    " Y6 d: o6 D1 L5 @! X' s7 Y
  11.   /* Connect PXx to USARTx_Tx*/; |. o/ ~3 n7 O
  12.   GPIO_PinAFConfig(GPIOA, 9, GPIO_AF_USART1);+ a7 |; E6 w9 k3 R% [
  13. 2 ~1 {- h, n. N- r3 {" e! J
  14.   /* Connect PXx to USARTx_Rx*/
    - l$ X( b9 c& Q' C& e: a7 E4 t
  15.   GPIO_PinAFConfig(GPIOA, 10, GPIO_AF_USART1);
    , t1 ]" v5 _' M! X: x% d( |. J
  16. / L7 q3 G5 _+ W0 v% ~
  17.   /* Configure USART Tx as alternate function  */4 S+ I5 p* x, V6 f, o9 j& a
  18.   GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
    ( {. i, p3 T9 b! o3 L. \7 Y
  19.   GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;5 c8 W( ^/ m7 [" w& N% E
  20.   GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;! Y( L/ E+ f  E$ E6 i* J

  21. 5 b- `: J& h# V3 `* A
  22.   GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
    - \) Y- p. R* S8 {2 y8 w1 H$ N
  23.   GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    0 C* _2 h& m8 j! }1 A& o$ ]# V; H
  24.   GPIO_Init(GPIOA, &GPIO_InitStructure);
    ) M  {/ @! \) h. i$ [, X- C& W  ^- G$ v

  25. , N: n& K% G$ @; ]" q9 H8 U' N8 r
  26.   /* Configure USART Rx as alternate function  */! ?' f: v, V6 w; l6 A% R
  27.   GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;2 b1 e- a! ^7 Z! q7 L
  28.   GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
    . M& e, M  }& a% H/ X* |! O+ q' ?3 W
  29.   GPIO_Init(GPIOA, &GPIO_InitStructure);
    9 u8 o7 l2 x7 x* N9 ^% `1 K/ Z
  30. # M4 b- X) V: X
  31.   USART_InitStructure.USART_BaudRate = 115200;0 t6 @; i  l  u. K4 |
  32.   USART_InitStructure.USART_WordLength = USART_WordLength_8b;, I7 p" o# m0 X8 f( P
  33.   USART_InitStructure.USART_StopBits = USART_StopBits_1;
    8 N( t; I- w( t1 e( f& r% t
  34.   USART_InitStructure.USART_Parity = USART_Parity_No;
    9 q- a) }: j$ T' C' ^! n; x+ I0 K
  35.   USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
      P  y! V$ E8 q, R/ j
  36.   USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;; s+ T6 T- ~7 O3 J* I8 s

  37. 4 k* y. @: h: s% u+ G
  38.   /* USART configuration */) {% v' E0 ~7 w- {0 }# W
  39.   USART_Init(USART1, &USART_InitStructure);
    ( q$ m9 D& H) T2 @+ I' {
  40. ' w; f) c1 F' p! e) i7 \
  41.   USART_ITConfig(USART1, USART_IT_IDLE, ENABLE);
    4 }& [( N  J( V4 A( @4 e$ u1 W
  42. * v9 a  M1 C( v+ w. _
  43.   /* Enable the USARTx Interrupt */
    " u& r$ z/ j1 O0 a
  44.   NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;0 m: Q" V4 u3 P" J; b+ f
  45.   NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority =0;
    4 I" D) M- D0 ]7 T; ]0 }' y
  46.   NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;6 y, P& f! V. \! b
  47.   NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
    ) M  W( d2 u) Z2 X$ w
  48.   NVIC_Init(&NVIC_InitStructure);
    ' P% {1 g  X" l
  49. ' T) `5 F* R+ v, k6 Z7 S
  50.   /*使能串口DMA接收*/! A( ]  {( r) d) i% S7 Q
  51.   USART_DMACmd(USART1, USART_DMAReq_Rx, ENABLE);% z* q/ j. U' t7 X
  52.   /*使能串口DMA发送*/
    6 `2 k; u! O$ @9 ~
  53.   USART_DMACmd(USART1, USART_DMAReq_Tx, ENABLE);
    7 S7 d6 j+ u7 v; _+ }7 o+ N' Z2 `6 W

  54. 7 }, @3 ]7 ?( z3 _  ?* X" N- Z3 o
  55.   /* Enable USART */
    4 a2 u: _" \+ f# }" g
  56.   USART_Cmd(USART1, ENABLE);3 K8 q5 }/ G0 [
  57. }</font>
复制代码
在这里除了常规的串口配置,我们需要配置串口的DMA发送,和串口DMA接收一样的API函数,参数修改为USART_DMAReq_Tx即可。0 k* `! H4 c1 j$ ]- r" ~& \
串口DMA发送配置: c# ~8 A& e7 x
  1. <font face="微软雅黑" size="3">void Uart_Send_DMA_Config(void)/ v8 Z. }( d4 h0 {6 P; }. s% d0 s$ ]
  2. {
      d$ `! K4 E1 o7 F
  3.   DMA_InitTypeDef  DMA_InitStructure;; S0 H3 Y- a7 f& F- p- U- W, |8 m$ Q
  4. 8 h' ]6 M( n$ x3 w% a) i$ d' t
  5.   /* Enable DMA clock */
    4 K! F- z% J( j( T
  6.   RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA2, ENABLE);
    3 C* `' E. h- L" [# T* m) ?: F
  7. $ h( [4 w: }/ t. M" p; J+ m* v
  8.   /* Reset DMA Stream registers (for debug purpose) */
    , h! R# p6 a* H" S7 L5 O  p' v! O& k
  9.   DMA_DeInit(DMA2_Stream7);
    6 H9 V; p1 ~0 t* \8 u1 ]9 q1 S
  10. % ]$ V, O) d" I
  11.   /* Check if the DMA Stream is disabled before enabling it.
    3 y1 {! f  J4 |, V5 x
  12.      Note that this step is useful when the same Stream is used multiple times:
    " I0 N' P5 F0 l
  13.      enabled, then disabled then re-enabled... In this case, the DMA Stream disable
    ) P7 v: N" f* T8 C; T
  14.      will be effective only at the end of the ongoing data transfer and it will ; l/ ?! o5 g3 Q/ {0 U
  15.      not be possible to re-configure it before making sure that the Enable bit
      S0 T- Q$ R% B+ q3 X$ B
  16.      has been cleared by hardware. If the Stream is used only once, this step might
    , ]  e3 N! k- r+ `2 a5 w
  17.      be bypassed. */9 i3 t. E9 E) I# w8 s2 C
  18.   while (DMA_GetCmdStatus(DMA2_Stream7) != DISABLE)( ], v. f/ @. u2 `+ u
  19.   {
    # ~1 s8 _& n- {7 T  U
  20.   }. F- t+ @# v/ x

  21. 5 A) P6 [' n5 p; x
  22.   /* Configure DMA Stream *// p# }# o8 T# O( y8 @# b  f1 }
  23.   DMA_InitStructure.DMA_Channel = DMA_Channel_4;  //DMA请求发出通道
    $ K$ c) x! |8 ~6 v4 w5 r
  24.   DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&USART1->DR;//配置外设地址* ^# c+ E% X, G! u8 p
  25.   DMA_InitStructure.DMA_Memory0BaseAddr = (uint32_t)UART_Buffer;//配置存储器地址4 `' F, F2 q1 r8 {
  26.   DMA_InitStructure.DMA_DIR = DMA_DIR_MemoryToPeripheral;//传输方向配置
    1 A. f! H$ C/ X3 d9 ^5 \
  27.   DMA_InitStructure.DMA_BufferSize = (uint32_t)UART_RX_LEN;//传输大小
    : _, G/ ]/ n* [. H/ [
  28.   DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;//外设地址不变. ]9 x4 x. A% K) J3 W9 C# t8 f. \+ r
  29.   DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;//memory地址自增1 [0 U& f( I/ }: q9 O8 D$ d2 V4 P
  30.   DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;//外设地址数据单位$ K, K0 s* I) E  [7 J; A
  31.   DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;//memory地址数据单位. n# B* p! {1 E+ {. E
  32.   DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;//DMA模式:正常模式
    ; [& B' c0 }1 q
  33.   DMA_InitStructure.DMA_Priority = DMA_Priority_High;//优先级:高
    3 S: p( C4 O7 V7 M( `+ u
  34.   DMA_InitStructure.DMA_FIFOMode = DMA_FIFOMode_Disable;//FIFO 模式不使能.         
    9 j/ ^" W0 X! |) N& g
  35.   DMA_InitStructure.DMA_FIFOThreshold = DMA_FIFOThreshold_Full;// FIFO 阈值选择
    / m2 V% G; t+ Y! p3 R
  36.   DMA_InitStructure.DMA_MemoryBurst = DMA_MemoryBurst_Single;//存储器突发模式选择,可选单次模式、 4 节拍的增量突发模式、 8 节拍的增量突发模式或 16 节拍的增量突发模式。
    # ]7 g. X" |* N
  37.   DMA_InitStructure.DMA_PeripheralBurst = DMA_PeripheralBurst_Single;//外设突发模式选择,可选单次模式、 4 节拍的增量突发模式、 8 节拍的增量突发模式或 16 节拍的增量突发模式。- D3 k8 I/ g! ^6 y9 J- _4 u
  38.   DMA_Init(DMA2_Stream7, &DMA_InitStructure); , N# ^# H  P) A9 ?$ C

  39. 0 X6 }& ]6 B4 D) f% {
  40.   /* DMA Stream enable */
    , l, X) w) X1 D! @
  41. //  DMA_Cmd(DMA2_Stream7, ENABLE);
    , x; Y  a% ]2 e/ T/ r! G
  42. }</font>
复制代码
这里也是常规的DMA配置流程,不明白的同学请看文章《STM32DMA详解》,这里值得注意的是,配置完成并没有使能DMA2_Stream7,使能了就会立即将UART_Buffer的数据发送出去。2 l' I9 B/ j* W4 f9 o6 g
其他代码处理
8 s2 n  Y/ W9 i
  1. <font face="微软雅黑" size="3">void USART1_IRQHandler(void)
    ( v( g! {! s: Y  Q$ F0 }
  2. {5 ?- _" u2 l+ L8 q- A
  3.   uint8_t temp;
    5 H0 V0 D! h; X$ l' J
  4.   if(USART_GetFlagStatus(USART1, USART_FLAG_IDLE) == SET)0 q/ G8 a0 @( ~) c3 \3 o  V
  5.   {
    3 w2 w) ^& t# S( g
  6.     DealWith_UartData();& f' _" a+ r& X, J1 ]( K
  7. //    USART_ClearFlag(USART1, USART_FLAG_IDLE);( C3 ]8 m% t+ t0 Z, ^3 Y
  8.     temp = USART1->SR;  $ W8 E& g: @6 U* [1 g& w2 N8 c6 F# l
  9.     temp = USART1->DR; //清USART_IT_IDLE标志  
      z: l2 K& N/ L2 G
  10.   }
    1 d- a6 V" O! @7 G% Q
  11. }! Y* d* ]# s, O$ j8 z
  12. ) w9 q( h$ F) f1 A0 ^3 L# W
  13. void DealWith_UartData()( q1 p/ s; i# u8 f3 {5 J
  14. {
    ; V) ?+ O" ]/ d# f3 L; _5 e6 x
  15.   DMA_Cmd(DMA2_Stream2, DISABLE);, Z, K: D# i1 D  @: [9 L4 ]
  16.   UART_Receive_flg = 1;. A  {1 i7 ?4 U. N- G5 a
  17.   UART_Receive_len = UART_RX_LEN - DMA_GetCurrDataCounter(DMA2_Stream2);
    $ ^7 K8 d. [& g( t5 `' f! j
  18.   UART_Buffer[UART_Receive_len] = 0;' H. M! M4 Q# ~6 n4 g7 ]+ H
  19.   DMA_SetCurrDataCounter(DMA2_Stream2,UART_RX_LEN); : ~& h* X. a: E' v  o$ k) F7 y
  20.   DMA_ClearFlag(DMA2_Stream2, DMA_FLAG_TCIF2);
    + Y% r9 u! b: U, c2 d$ e$ k7 E" E
  21.   DMA_Cmd(DMA2_Stream2, ENABLE);; X5 s( Q; A2 ~5 n! w9 x
  22. }6 K% m# n7 ?- o6 }1 \

  23. & {* \4 Z7 F% F
  24. int main(void)
    ! g; j( V5 e, H: ^5 v* L
  25. {9 J; Z& P: {! E0 _
  26.   UART_Receive_flg = 0;  I# E: p' R- a8 t! a5 |: H; X
  27. ) o* }7 o* C7 p" m) v! O3 U
  28.   Uart_Reveice_DMA_Config();& h. u3 U  j* ]4 V" z8 q
  29.   Uart_Send_DMA_Config();
    0 f  e4 S' \- S
  30.   UART_Init();- r2 R, Q3 Q; t; E0 F7 n0 \8 s+ Z$ L
  31. . K" w) d, i% [/ |
  32.   while (1)
    0 b; h6 z- s; n: L, @
  33.   {; C6 x: Y' r* i
  34.     if(UART_Receive_flg)2 V5 t' F" l7 u7 \' C
  35.     {. a) f6 u! n% q+ V
  36.       UART_Receive_flg = 0;
    ( }8 E) i8 y: y5 F
  37.       Uart_Send_DMA_Start();3 _1 O" C1 L5 k$ i% S
  38.     }: T, p1 d5 {- f6 l
  39.   }% E9 F! X7 G1 ]6 o: h
  40. }</font>
复制代码
上面3个函数,简单逻辑就是,当串口使用DMA接收了一定量的数据,就会通过串口DMA发送出去,串口DMA发送的代码如下:# F1 G0 T( H& P* O8 ?
  1. <font face="微软雅黑" size="3">void Uart_Send_DMA_Start(void)
    7 M5 j6 M* N0 J) v
  2. {
    4 |( }7 ~3 z% p! @
  3.   DMA_SetCurrDataCounter(DMA2_Stream7,UART_Receive_len);
    ' l& F" R5 W- b. `
  4.   DMA_ClearFlag(DMA2_Stream7, DMA_FLAG_TCIF7);% W/ Q2 k* G4 C5 E. l" u" V8 @/ p- H
  5.   /* DMA Stream enable */
    4 L! y1 u+ u' [
  6.   DMA_Cmd(DMA2_Stream7, ENABLE);
    ) g1 \( w! v" n' O
  7. }</font>
复制代码

% X' o: C+ q+ o/ o9 g) X& @, d9 V/ X) ?% k9 x1 j" t9 G
收藏 评论0 发布时间:2021-7-20 13:45

举报

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