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

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

[复制链接]
STMCU小助手 发布时间:2022-4-16 21:00
1、概述% B0 v0 B' ~0 N! }
. h/ q  z% M. r0 a; D# _5 r: o3 U
使用DMA发送数据,首先我们要确认使用的串口有没有DMA。
' ?4 t+ {) p) P" f1 U  o4 C1 O
8 n2 |/ p3 r4 k( P9 r我们使用USART1串口外设,从数据手册中可以查到,USART1的发送和接收都是支持DMA的,使用的是DMA2., j. x# T# P: o0 G: n8 ]  @' @& h

% K' _6 Z5 O! v" _6 G- x 7e3832c538e49222cdf7e700940ac823.png ' {) E5 V( r5 v0 @9 k4 h! t- W; Z

( F; _# \& c: x接下来就是撸代码的时刻了# W; F$ L5 _) |1 x/ h
8 u0 J9 y) E1 e9 M* Y$ B3 Q  @
02、代码
* e6 F; w/ h. a' ]$ y# P6 ~
DMA串口发送的代码是在上一篇文章DMA串口接收的基础上修改的。/ o/ {  T+ l9 l$ k+ B' g

2 m- v, p- q& E( B5 v3 C/ H
  1. void UART_Init(void)( U/ W. \) C( K7 \+ X+ F  ~6 e& t
  2. {
    $ c7 ~/ M; N7 D9 E# u: J7 _7 b
  3.   USART_InitTypeDef USART_InitStructure;" _6 Y. C4 ~/ v# F
  4.   GPIO_InitTypeDef GPIO_InitStructure;
    ; F% d1 G( G# u2 \; S, K& {
  5.   NVIC_InitTypeDef NVIC_InitStructure;
    ) X, S9 N  F, v3 s
  6. / o7 A3 S: j6 m+ K& }2 j7 S
  7.   /* Enable GPIO clock */# V- s* n2 V% W  w5 E6 N/ A  D6 l
  8.   RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);4 {, k/ Q' |: F% Y! b/ }2 A
  9.   /* Enable UART1 clock */
    ) P) q6 O4 o. E; e  Z$ L
  10.   RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);
      W" p" V3 f; Y2 O3 m9 y
  11.   /* Connect PXx to USARTx_Tx*/
    / n! h1 c  n' H
  12.   GPIO_PinAFConfig(GPIOA, 9, GPIO_AF_USART1);
    1 p0 j5 j+ q  E( K% o( @

  13. 3 `, Y+ G) Q1 s4 w  p
  14.   /* Connect PXx to USARTx_Rx*/, a2 u4 q2 h( g6 j
  15.   GPIO_PinAFConfig(GPIOA, 10, GPIO_AF_USART1);  j1 F6 B0 t% p( v! m: h0 q: P# x
  16. ; g* A3 t! S3 |3 U  j
  17.   /* Configure USART Tx as alternate function  */- j2 ]$ F( C/ O1 U. V
  18.   GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;' T1 ?/ I( L' R2 ~0 B
  19.   GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;" @& W" [8 Z+ Z' W& n
  20.   GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;! Q0 j. e! L# @' |8 \) r( T

  21. ' a2 r2 l5 f- l3 d. ]% K/ \
  22.   GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
    5 l+ r" p9 c7 T: G. I
  23.   GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;% {) y5 o) }1 }$ l& H9 Y0 C  q0 N
  24.   GPIO_Init(GPIOA, &GPIO_InitStructure);( }9 G- L& ^8 x, c- Z9 j; m# i2 c
  25. 6 X4 w0 E0 W7 P8 X3 P% {
  26.   /* Configure USART Rx as alternate function  */  u$ ^# }+ K+ ]1 B/ r0 J
  27.   GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
    / o! G% U8 L% t( c/ l
  28.   GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
    ) W- k, E" U% Q/ ~! N
  29.   GPIO_Init(GPIOA, &GPIO_InitStructure);) C0 `1 B# E6 S
  30.   K7 s+ L% ~! Z1 b- n! k
  31.   USART_InitStructure.USART_BaudRate = 115200;# Z6 O- W; B9 T6 p# G. u
  32.   USART_InitStructure.USART_WordLength = USART_WordLength_8b;6 t: ?; I! t+ ?+ l: J1 E, f
  33.   USART_InitStructure.USART_StopBits = USART_StopBits_1;
    & P9 X/ b1 a* `
  34.   USART_InitStructure.USART_Parity = USART_Parity_No;
    9 f. J# X0 X5 `9 f
  35.   USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;& _3 C1 J9 ^" n
  36.   USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;8 z5 f1 `& V) K: h  g

  37. : [4 x4 I: D3 R( F+ E
  38.   /* USART configuration */3 ~) G$ _! [. D: u4 k- V
  39.   USART_Init(USART1, &USART_InitStructure);
    / j* H1 v' u7 d. _3 w
  40. + n2 ]' ]5 r' a$ [- \  q  {
  41.   USART_ITConfig(USART1, USART_IT_IDLE, ENABLE);% u9 U2 S9 Q& _6 {
  42. $ [' s2 }* p' w; g( t
  43.   /* Enable the USARTx Interrupt */
    - ?$ |6 k2 r/ |: [6 o' r$ t
  44.   NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;
    ' F2 P3 {6 Q6 t' Y4 g- e, A" W
  45.   NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority =0;
    & C; \9 B. P0 ]% G/ M. Q; D
  46.   NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
    * Z8 N7 }% k0 L% X9 W& f
  47.   NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;) o6 \& v6 T6 p, U  F, z, ?
  48.   NVIC_Init(&NVIC_InitStructure);
    ) Q9 }, f$ v* J/ r5 V9 `9 K

  49. ) c. w  W( r& A0 m) t, E
  50.   /*使能串口DMA接收*/, B% ~# M- r" D
  51.   USART_DMACmd(USART1, USART_DMAReq_Rx, ENABLE);" R7 ]" C7 o7 i! ]
  52.   /*使能串口DMA发送*/
      q  [$ q' E1 I3 C0 \
  53.   USART_DMACmd(USART1, USART_DMAReq_Tx, ENABLE);
    & W7 V3 |+ h: k1 n$ i

  54. : R- |# v# q' I
  55.   /* Enable USART */* ~9 }. T1 X, X/ k: F
  56.   USART_Cmd(USART1, ENABLE);
    + t) M8 ~" ?) r  s" e4 a
  57. }
复制代码
+ U2 A. {" b6 d% i1 h9 Y

( O3 G1 H4 w$ m- c在这里除了常规的串口配置,我们需要配置串口的DMA发送,和串口DMA接收一样的API函数,参数修改为USART_DMAReq_Tx即可。
* y9 g* D) x3 S, x4 b" t
' l: @6 b3 ^& o& ~0 z串口DMA发送配置* o5 Y" m* H& k9 G4 n9 [
; Z+ ~) z# x' n. ^7 s6 n
  1. void Uart_Send_DMA_Config(void)
    # {1 f* D" N4 I3 I9 X
  2. {' `- [6 S' y! s" c7 U& y7 P0 u0 C
  3.   DMA_InitTypeDef  DMA_InitStructure;
      C; D! A( \+ D9 F9 M

  4. * A& M! ~0 J" G" d! m; `( H3 x$ J: x
  5.   /* Enable DMA clock */
    ! L+ G9 v. p0 s7 z) N- ?+ e0 U- t
  6.   RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA2, ENABLE);/ b4 R! s' `- o2 W1 q! n$ l

  7. / _9 @' J4 R# i. G4 Q0 o1 |' {
  8.   /* Reset DMA Stream registers (for debug purpose) */$ _1 H+ r6 M& d: p0 H5 s7 z
  9.   DMA_DeInit(DMA2_Stream7);
    ( Y0 L  x+ q/ J: l5 x' a

  10. 5 G+ @# W5 X' ]& d: g$ k
  11.   /* Check if the DMA Stream is disabled before enabling it.& J; b# G3 q" H5 i
  12.      Note that this step is useful when the same Stream is used multiple times:; U: Q4 p5 S0 R  G, b
  13.      enabled, then disabled then re-enabled... In this case, the DMA Stream disable
    0 e8 v5 I* B# T& ^  K! c
  14.      will be effective only at the end of the ongoing data transfer and it will ) f+ p5 L5 j8 |* |  d
  15.      not be possible to re-configure it before making sure that the Enable bit
    ) _# Y* f, q7 Q. s2 Z
  16.      has been cleared by hardware. If the Stream is used only once, this step might ! \- U& l5 ?! G) ?. V; x
  17.      be bypassed. *// D6 C% _  H' f5 h4 ^
  18.   while (DMA_GetCmdStatus(DMA2_Stream7) != DISABLE)
    7 E) y# D# d. o) D% j7 l/ z
  19.   {
    ' M# h5 Y9 Z0 P. h, W$ B, q* V
  20.   }/ R- ^; h) G6 S0 T  x

  21. # T( y$ I9 q2 k' d
  22.   /* Configure DMA Stream */+ X# p1 I4 ]- L
  23.   DMA_InitStructure.DMA_Channel = DMA_Channel_4;  //DMA请求发出通道" w8 V  h' R/ i, L
  24.   DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&USART1->DR;//配置外设地址' \0 e7 g3 ~& E
  25.   DMA_InitStructure.DMA_Memory0BaseAddr = (uint32_t)UART_Buffer;//配置存储器地址, \9 g+ W. f; f! |+ x; s( w' n& B
  26.   DMA_InitStructure.DMA_DIR = DMA_DIR_MemoryToPeripheral;//传输方向配置0 q$ O4 E- `- |1 S* I, a4 ^0 Z3 ~
  27.   DMA_InitStructure.DMA_BufferSize = (uint32_t)UART_RX_LEN;//传输大小& t/ x* c6 I: S* {" f
  28.   DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;//外设地址不变' q( }4 C- ]" i* ^- D3 M' M6 [
  29.   DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;//memory地址自增
    " {& Y1 \- G, f  j& P' a% I" A
  30.   DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;//外设地址数据单位! Z$ `& C. F' O; d) M
  31.   DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;//memory地址数据单位
    ' V% ]1 U5 z) ~+ Q
  32.   DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;//DMA模式:正常模式* [8 g8 E6 c- [5 f. e( W8 N
  33.   DMA_InitStructure.DMA_Priority = DMA_Priority_High;//优先级:高1 q# H6 r& P, Q: c' z
  34.   DMA_InitStructure.DMA_FIFOMode = DMA_FIFOMode_Disable;//FIFO 模式不使能.          9 g/ f; r4 ?, D+ f3 [6 M( k
  35.   DMA_InitStructure.DMA_FIFOThreshold = DMA_FIFOThreshold_Full;// FIFO 阈值选择
    7 i+ A) n1 J/ n- e2 y" V0 Y
  36.   DMA_InitStructure.DMA_MemoryBurst = DMA_MemoryBurst_Single;//存储器突发模式选择,可选单次模式、 4 节拍的增量突发模式、 8 节拍的增量突发模式或 16 节拍的增量突发模式。
    7 `; S: ^5 n, P* b. N3 P
  37.   DMA_InitStructure.DMA_PeripheralBurst = DMA_PeripheralBurst_Single;//外设突发模式选择,可选单次模式、 4 节拍的增量突发模式、 8 节拍的增量突发模式或 16 节拍的增量突发模式。! L, \7 d) R5 g! F
  38.   DMA_Init(DMA2_Stream7, &DMA_InitStructure);
    ( u- d2 ~6 t9 M  `" M9 c9 p
  39. 2 w, D0 [/ P' @. h
  40.   /* DMA Stream enable */
    - B* _  u1 P0 i0 R/ W
  41. //  DMA_Cmd(DMA2_Stream7, ENABLE);% o  j; g( ^, F5 l% ~; y
  42. }
复制代码
/ ?, L% S, ~/ A. Z& y

  L: `8 X/ ?6 s, x5 s这里也是常规的DMA配置流程,不明白的同学请看文章《STM32DMA详解》,这里值得注意的是,配置完成并没有使能DMA2_Stream7,使能了就会立即将UART_Buffer的数据发送出去。. |1 K  |8 J; [+ A% B+ Z

& `: s/ {5 y" K' ]7 b' q$ K其他代码处理% r* c1 G8 v9 h* W/ y

. F- P0 h! q2 ^
  1. void USART1_IRQHandler(void)
    # q2 a2 f! x7 H, `
  2. {
      U$ Q, w6 s  o) ~8 [  G- h/ }
  3.   uint8_t temp;
    ( I, X5 Z0 P, m+ Q- `. Y% j/ d' u
  4.   if(USART_GetFlagStatus(USART1, USART_FLAG_IDLE) == SET)
    0 X8 l+ X& |6 E2 P/ v
  5.   {9 }7 M5 w$ S* b7 h! A2 X7 [
  6.     DealWith_UartData();' e* f) W5 c. j* Y
  7. //    USART_ClearFlag(USART1, USART_FLAG_IDLE);
    % l! [6 p  L7 P% A! \( q
  8.     temp = USART1->SR;  6 H- j* v, X7 v# I: P$ x' m1 W
  9.     temp = USART1->DR; //清USART_IT_IDLE标志  * [+ `( m; r: z
  10.   }4 g; ?( M' z$ E8 e
  11. }5 F* b5 B$ I" H; ~" K- Y8 n* F
  12. * j$ G. M+ W2 D0 S
  13. void DealWith_UartData()- z5 n% E  l& H2 q
  14. {
    4 V+ c# U' @9 N5 _1 D( C* p
  15.   DMA_Cmd(DMA2_Stream2, DISABLE);- P* d. Z# C5 t" a: a6 E
  16.   UART_Receive_flg = 1;: s! ]- D+ t% W2 o- s7 j$ f" i1 r
  17.   UART_Receive_len = UART_RX_LEN - DMA_GetCurrDataCounter(DMA2_Stream2);
    1 S; P1 t+ @* z8 t- t/ |
  18.   UART_Buffer[UART_Receive_len] = 0;
    % f( q. c' H0 d8 S0 z! _& x
  19.   DMA_SetCurrDataCounter(DMA2_Stream2,UART_RX_LEN); 5 R) h$ x, G0 K2 ?: ]" i2 F
  20.   DMA_ClearFlag(DMA2_Stream2, DMA_FLAG_TCIF2);) p- C1 n( E, Q' B
  21.   DMA_Cmd(DMA2_Stream2, ENABLE);9 \  O- ~2 }1 S$ [1 ]8 y) {) l1 X
  22. }/ {/ a1 m2 h# t" c' ]

  23. 4 S& O( T; D: {. M
  24. int main(void)
    3 o% E# `5 J; K
  25. {, b, H% m% a+ ^
  26.   UART_Receive_flg = 0;
    7 Y) d# a3 H$ I3 C( R3 I
  27. 8 M( e" }' Y* t! k8 |% X9 S* ~
  28.   Uart_Reveice_DMA_Config();
    / o0 O6 M3 Z  v" V( b; G
  29.   Uart_Send_DMA_Config();# a. z7 L* l' y# F6 X
  30.   UART_Init();
    ; S. Q3 @* C* B% b! B

  31. ( L6 s( P3 i5 }; ]6 Q2 x) s! D
  32.   while (1)
    ) K  v$ P' x, g# f! ?  z8 \
  33.   {
    ! ^1 L7 O% C( s, C
  34.     if(UART_Receive_flg)
      f) k0 i: S! l8 M/ W
  35.     {: Z& T# t1 C1 o$ `# f
  36.       UART_Receive_flg = 0;
    - z' Y* i  E: [: @5 v# S
  37.       Uart_Send_DMA_Start();
    6 g* S7 R+ s  A: K
  38.     }
    7 S3 _5 G+ N; G& v+ b6 ~
  39.   }
    9 K4 L8 f3 v- A5 a
  40. }
    ; \: f/ }% K( K
复制代码

* I/ r5 E  {" Q* X; I+ n! b: ]9 ?上面3个函数,简单逻辑就是,当串口使用DMA接收了一定量的数据,就会通过串口DMA发送出去,串口DMA发送的代码如下:
. B  B$ q' u8 a  ^; l: ^0 ], h
; }* L8 Q1 @* s# h0 Z5 n" F
  1. void Uart_Send_DMA_Start(void): V1 Q; @3 U! c
  2. {
    5 \  j( X! s6 Y
  3.   DMA_SetCurrDataCounter(DMA2_Stream7,UART_Receive_len);
    ! d2 z6 t1 L4 z6 `% M
  4.   DMA_ClearFlag(DMA2_Stream7, DMA_FLAG_TCIF7);+ ?  L, N$ i  k; J
  5.   /* DMA Stream enable */
    ' m& V+ ~. T5 I1 |( W3 }
  6.   DMA_Cmd(DMA2_Stream7, ENABLE);4 I1 G% n- b6 r5 i; F* K5 `
  7. }
复制代码
; K  V; H: K9 i' L/ x3 L# [! W3 D0 W
03、后记/ c$ B' {6 k; M5 _3 A% r7 O7 f+ u
这一篇很简单,就是DMA使用的一个延伸,上面说了这么多,也贴了很多代码,不可能将所有代码全部贴出来,作为软件工程师,还是在IDE里看代码方便,如果感兴趣的话,可以到下面github链接下载代码,Keil和IAR的工程文件都有。
, x3 U7 T* E; ^1 B; |' A
+ ]7 B; p2 f. t3 @+ Y7 n7 O5 ?( s; W7 ]4 a/ j
收藏 评论0 发布时间:2022-4-16 21:00

举报

0个回答

所属标签

相似技术帖

关于
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
意法半导体官网
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
官方最新发布
人形机器人运动控制、感知与智能配电
半导体创新技术与应用方向
EE架构与软件定义汽车
12V/48V 汽车智能配电(SPD)
区域控制单元(ZCU)与分区架构
关注我们
st-img 微信公众号
st-img 手机版