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

【经验分享】STM32F0 DMA串口接收数据

[复制链接]
STMCU小助手 发布时间:2021-11-24 13:30
, O$ m& ^, U) G0 e4 m
aHR0cHM6Ly9tbWJpei5xcGljLmNuL3N6X21tYml6X3BuZy91Y2RZbUdYTGlhOThxM0tXM2pWY0ZMdU9m.jpg
; b) X8 }' j+ C1 c' U7 k

  \# B4 Y$ C) a& h. oDMA,全称Direct Memory Access,即直接存储器访问。DMA传输将数据从一个地址空间复制到另一个地址空间,提供在外设和存储器之间或者存储器和存储器之间的高速数据传输。当CPU初始化这个传输动作,传输动作本身是由DMA控制器来实现和完成的。DMA传输方式无需CPU直接控制传输,也没有中断处理方式那样保留现场和恢复现场过程,通过硬件为RAM和IO设备开辟一条直接传输数据的通道,使得CPU的效率大大提高。
* t$ M" j" S0 g: d6 x. l; X/ l! E) M1 I3 `/ Z6 y9 X) c
主要特点:
1 A' A+ M$ G# i$ d3 Z& S  s: K•DMA上多达7个可独立配置的通道(请求)
7 Z9 o" p. Z. r2 B, |* K! L/ R% n6 s; m) C, D* q
•每个通道都连接到专用的硬件DMA请求,软件触发为' U; J* {% W( l" ~# L/ a' G8 I( d& t
4 a/ e0 Q6 e; _3 l' ?
每个频道也都支持。该配置由软件完成。0 `6 L7 C9 f- m) o3 a; @" w# B. G/ X
! K, p3 |$ T6 G2 Z- a  M
•DMA通道的请求之间的优先级是软件可编程的(4
# b1 g+ }/ d% E' Z* ]  `& F- Q, S# e+ h1 v+ v% r
级别由非常高,高,中,低)或硬件组成(在相等的情况下)
2 \& C) H5 K: ^- P( ?: k3 ~  p3 l
: c. J1 b3 u: T& N$ g(请求1的优先级高于请求2的优先级,依此类推): `* c$ O7 f% o0 S( r( f+ i5 D

% ?) N: ^( t0 m4 S2 k4 X3 x/ K  _•独立的源和目标传输大小(字节,半字,字),模拟' t. U' _) p1 O3 Y* ~- w& K* ]
# R2 f+ s! E/ J# _/ ~
包装和拆箱。源/目标地址必须与数据对齐9 F& Q! N+ `9 D7 w% t
" d  Y1 ]4 L( o8 D! n7 i6 ]
尺寸。
& X: Z- r; v; @2 H
: U4 t! `2 N) @- f( q8 U•支持循环缓冲区管理, W- G6 w7 a0 L: u2 a
' P- ~0 l. x3 h4 L* K# h, H
•3个事件标志(DMA半传输,DMA传输完成和DMA传输错误)* P9 ^; n$ Y1 ]: r9 j
+ }2 F# D: R1 H3 n/ e3 \3 F
在每个通道的单个中断请求中进行逻辑或运算2 v. x2 @, z! a! y. F/ J; ~, k& X* T* x2 A

: s+ P+ T3 Q& L& t5 i•内存到内存的传输
' [) J6 L6 i/ W" H  T% n; R
" I" t( ]" _+ s- K•外围到内存和内存到外围,以及外围到外围
( U/ F# |1 P9 Y+ l3 }/ ?  J& P5 U
* y1 r% z5 a) e$ ?2 b2 i2 y+ D) s转移
" C5 E) M. B) a4 U8 Q' O/ h* x1 T$ I( ^/ o! ^7 k8 }
•访问闪存,SRAM,APB和AHB外设作为源和目标: ^! V8 L- t  @: \+ E
( Z9 ~9 |/ ]) n0 _; f: f8 v! X
•可编程的数据传输数量:最多655358 v' C5 T% M+ m* q

8 Y0 k: U! N: yDMA通道对应的外设情况(F0系列):4 A5 u: f, f& v/ c. G2 w
! s2 J: J' n- e& L! z0 E
aHR0cHM6Ly9tbWJpei5xcGljLmNuL3N6X21tYml6X3BuZy91Y2RZbUdYTGlhOThxM0tXM2pWY0ZMdU9m.png
) f! k7 P/ t% q) ?( P1 n: L

5 _8 }& P8 m; b6 P% P* {+ o
aHR0cHM6Ly9tbWJpei5xcGljLmNuL3N6X21tYml6X3BuZy91Y2RZbUdYTGlhOThxM0tXM2pWY0ZMdU9m.jpg
7 N3 Y  C/ |. u1 Q% A5 Y; s

* u* P" b" _$ x& k$ t6 C8 c4 o
7 f+ H: x2 p4 p8 _' w' p& w
' b- y/ B* j1 R# J3 e% g% O5 J% b( j    很多博文会讲解里面的详细定义,对于每一处寄存器的详细操作指导,所以我就不会去多写了。
" |' n# U% E$ ]' k8 }) V. q$ T/ n# J8 Q5 I2 m5 h3 h( E5 [
    我只想表达,DMA是一种可以快速相关外设数据交互的一种方法,我们学习哪里用它,怎么用它,至于细节学习,大家去网上所搜DMA相信息即可。    ; e( b' `' E  Z7 S

3 U& S$ c+ c) h) @; B% X" I    之前讲过DMA的数据发送,现在补充上DMA的接收数据部分。/ C, ?# s6 Q$ \' Y6 z, |
# k1 v3 m2 L' d. G# j; I
  利用DMA接收串口数据的配置,大致分为:1.初始化串口并开启DMAR接收功能,配置DMA的外设到内存的数据接收功能,2.等待串口中断提示,并进行处理数据,3.清空DMA,重新等待数据3 S) E5 Q9 v; x! m4 y# A" G5 H% {
! [2 E) b! o3 n
01 配置串口与DMA) J7 [5 X/ s; W7 S4 |- o7 o
    其中USART2->CR3寄存器的第6 bit用来设置DMA的接收配置,此处比较重要我们设置为USART_CR3_DMAR。 USART2->CR1寄存器中开启帧信息接收完成之后的中断,USART_CR1_IDLEIE,这处可以帮助我们节省CPU的不必要开支,开启此处中断类型,我们只需要在每帧信息接收完成之后,usart才触发中断,我们再解析。其余为正常的usart配置。9 Z, g" P4 J: w5 V- b: i% `

# I0 ]; J7 t$ t! W( f' X% J. k/ ]
aHR0cHM6Ly9tbWJpei5xcGljLmNuL3N6X21tYml6X3BuZy91Y2RZbUdYTGlhOThxM0tXM2pWY0ZMdU9m.png
) W- K- k/ L5 W4 c
" \, {$ D8 Z! U$ K0 s) _. j) d/ M
  1. GPIOD->MODER |=   GPIO_MODER_MODER5_1|  //USART2_TX- t$ ^( D( v1 u' r; C2 U* |
  2.                   GPIO_MODER_MODER6_1;  //USART2_RX  # W8 i- R* ]5 x0 w/ S
  3.   /* set baudrate */
    * Z8 }9 d. U5 @7 l1 f
  4.   USART2->BRR = USART_Baudrate;//115200
    8 O% x: Z1 j/ b5 Y' D
  5.   USART2->CR3 |= USART_CR3_DMAT|USART_CR3_DMAR;//USART_CR3_OVRDIS;// 不需要覆写- M+ q; {& I8 j$ W6 x) p

  6. ( B/ A6 I$ ^2 _! l
  7.   /*enable usart2 and enable tx*/
    1 w: F6 J* w; x  ?
  8.   USART2->CR1 |= USART_CR1_UE | USART_CR1_TE | USART_CR1_RE | USART_CR1_IDLEIE;
复制代码
: M% k9 Y) C5 g
配置DMA:2 f) ~( m; z: G6 }3 _  V
& J8 \4 [: E7 s
    首先根据上方的映射表,我们初始化了usart2,而usart2 RX对应的DMA通道为DMA1 channel5,所以我们进行DMA1 ch5通道配置。
" @; B5 `# V) E5 v' O0 k  f( N3 z$ y; G, \7 Z
第一个为DMA1_Channel5->CPAR寄存器,在下表可知,用来设置对应接收数据的外设的地址,而USART的接收数据的寄存器为RDR,所以寄存器配置为DMA1_Channel5->CPAR = (uint32_t)&(USART2->RDR);
' i0 {1 z" D# r4 ]
, ?& [9 a9 K9 c1 u0 x" C
aHR0cHM6Ly9tbWJpei5xcGljLmNuL3N6X21tYml6X3BuZy91Y2RZbUdYTGlhOThxM0tXM2pWY0ZMdU9m.jpg

, N* j3 @" A1 l9 @1 S4 G
+ I  G& U: h; c  j3 J+ d- {7 R' V, E, E+ M5 R: Z* P' F
! Q' H+ c( f/ @  A6 H+ e
    设置需要放数据的指定内存位置,根据指导手册可知CMAR为DMA通道用来放置数据的内存地址,所以此处设置为我们定义好的变量的地址。
# k$ @8 N% o: p9 T; ^( U: |1 Y9 w) U' |# V& h% I; x
aHR0cHM6Ly9tbWJpei5xcGljLmNuL3N6X21tYml6X3BuZy91Y2RZbUdYTGlhOThxM0tXM2pWY0ZMdU9m.jpg

+ t2 {! Q# S$ \, u* e, L! b, z" E  l3 A8 W
    设置单次传送数据量的大小,此处最大可设置为65535byte的数据大小。! L. n; n9 R0 y; P" E! P, x) u9 s

- m" V4 W# \& _  J) z6 H7 x
aHR0cHM6Ly9tbWJpei5xcGljLmNuL3N6X21tYml6X3BuZy91Y2RZbUdYTGlhOThxM0tXM2pWY0ZMdU9m.jpg
8 F( i7 u' `9 z" d

5 e, Q0 k. f$ u+ C$ q1 w最后开启DMA通道。
$ f' \3 H" Z6 P4 b' T, ]9 l, |) j2 R9 j
aHR0cHM6Ly9tbWJpei5xcGljLmNuL3N6X21tYml6X3BuZy91Y2RZbUdYTGlhOThxM0tXM2pWY0ZMdU9m.jpg

; p' p7 Q1 S2 r) _/ a* t( i8 d6 o9 B  P; {
aHR0cHM6Ly9tbWJpei5xcGljLmNuL3N6X21tYml6X3BuZy91Y2RZbUdYTGlhOThxM0tXM2pWY0ZMdU9m.png

& L: X5 z5 ^+ v& l
aHR0cHM6Ly9tbWJpei5xcGljLmNuL3N6X21tYml6X3BuZy91Y2RZbUdYTGlhOThxM0tXM2pWY0ZMdU9m.jpg
7 d5 R4 e, O% K( u! a
1 h4 C$ t2 x' f3 B0 |: D6 \
  1. void USART2_DMA_Recive(uint8_t *p_data,uint16_t length)
    * d% `- E5 ?; v$ m9 z
  2. {
    ' D; w$ E& P- m' `
  3.   DMA1_Channel5->CPAR = (uint32_t)&(USART2->RDR);          //Peripheral address
    - i$ G, z$ u( r* o* U6 e
  4.   DMA1_Channel5->CMAR = (uint32_t)p_data;                  //memory address6 w$ E* Y" K  G2 t
  5.   DMA1_Channel5->CNDTR = length;                            //Set the length
    . ?3 c8 P- s; w, i4 `# c; Q% [4 P
  6.   DMA1_Channel5->CCR |= DMA_CCR_MINC | DMA_CCR_EN;8 d! L4 @& H- `3 r) y% c9 R; v
  7. }" L9 N; Y! Q4 O$ v# S& Z* J1 T- G
复制代码

- Q1 n& X0 ]2 v+ C- P02  等待串口中断,处理数据
( n1 L  `; Y0 d
) x5 {0 t, \9 n. @
aHR0cHM6Ly9tbWJpei5xcGljLmNuL3N6X21tYml6X3BuZy91Y2RZbUdYTGlhOThxM0tXM2pWY0ZMdU9m.png
! m; l8 [6 u% _4 x( j6 x
6 ?- c+ a6 e0 V9 G- \, {3 u5 m
    此时在debug中,在我们定义好的DMA内存变量,地址在RAM初始完成,后续串口数据接收的时候,DMA会直接将数据置于p_data所对应的内存。
- y  L/ q8 P& g9 ~
' F4 A& v! r2 b6 e3 ]8 d; _    然后在中断服务函数我们可以将我们数据进行处理,Uart_Channel_isr[1](USART2->RDR);,这个函数为我自己写的处理函数,中断里面函数都是数据复制,所以我直接在中断执行,一般大家会在中断写个标志,在主循环进行解析。但是DMA接收配合USART_ISR_IDLE标志在STM32平台下并不友好,如果主循环用来解析数据的时候,空闲中断还没有产生,本帧数据还尚未接收完成,但是由于内存数据已经在实时写入,DMA1_Channel5->CNDTR已经有所变化,并早于空闲中断产生,所以主循环就不能用DMA1_Channel5->CNDTR所接收数据长度进行解析了。一般建议,如果解析数据只是单纯的挪移,此时候直接在中断处理即可,对主程序并没有大的阻塞。
' f  f- B7 |: f8 I
3 b* O8 F" \) i  N. {& t8 M
  1. void USART2_IRQHandler(void)
    % u" J1 F) Y* W; i$ ?
  2. {
    $ k7 W) j$ G4 e0 H( L/ R. R
  3.   if ((USART2->ISR & USART_ISR_ORE) == USART_ISR_ORE) 7 n4 j, |$ C# Z' C  ^$ y  w5 O* q
  4.   {  
    2 i8 R; B3 U! w+ g& U( J
  5.     USART2->ICR |= USART_ICR_ORECF;* M6 `' ^5 {$ N7 V
  6.   }! [4 t, l, q2 M- d2 ]  y% L, O1 e
  7. + P3 c6 B1 U% ]3 j" V) E
  8.   if ((USART2->ISR & USART_ISR_IDLE) == USART_ISR_IDLE)  //The new frame data receive ' T5 o6 S4 P# \9 N
  9.   {& a& |6 Z, ^/ ]& L; F5 ]
  10.     USART2->ICR |= USART_ICR_IDLECF;    ) X! _2 C, |0 j3 Y) X6 n& A# Z  d
  11.     Uart_Channel_isr[1](USART2->RDR);/*读取解析数据*/3 d5 |6 y. q- R0 Y" {  y
  12.    }  
    + Q" ]% V, s" G9 V" Z/ W2 C
  13. }
复制代码

% i" |% v. I0 `- t: t" u6 I$ H' @03 恢复DMA,清空CNDTR,等待下次数据到来8 K, @3 }/ I  T( F7 t
    处理完数据之后,及时将DMA标志以及CNDTR清空,否则CNDTR一直不清空,会导致下次接收数据的时候,造成通道的占用,数据使用过之后,就清理掉CNDTR,这样保证每次接收数据的通道有足够的位置。
& U  t, u) i3 C/ D( X! i7 t9 h
  1. static void Usart2_Dma_Reload(uint16_t length)/*清空数据*/
    - C% }6 k/ H5 R5 L
  2. {- z4 F* _: C& N. D! L: z6 B
  3.   DMA1_Channel5->CCR &= ~(DMA_CCR_EN);             //disable the dma / I- z; @) `4 |: K
  4.   DMA1_Channel5->CNDTR = length;          //Set the length
    & k5 B- L1 q  @7 f
  5.   DMA1_Channel5->CCR |= DMA_CCR_EN;                //enable the dma
    8 B3 V' J: \" J3 c, }' v9 }* y- g4 B
  6. }5 E1 _% z. x4 O, i% Q  T1 k
复制代码

! H4 q9 C/ _9 O3 z- _( @4 x8 o, y8 }+ G' j
收藏 1 评论0 发布时间:2021-11-24 13:30

举报

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