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

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

[复制链接]
STMCU小助手 发布时间:2021-11-24 13:30

( o; E9 r) j4 \. \
aHR0cHM6Ly9tbWJpei5xcGljLmNuL3N6X21tYml6X3BuZy91Y2RZbUdYTGlhOThxM0tXM2pWY0ZMdU9m.jpg

) j  y; L: X8 w" i& w
/ H; j8 d  ^+ X2 ^5 FDMA,全称Direct Memory Access,即直接存储器访问。DMA传输将数据从一个地址空间复制到另一个地址空间,提供在外设和存储器之间或者存储器和存储器之间的高速数据传输。当CPU初始化这个传输动作,传输动作本身是由DMA控制器来实现和完成的。DMA传输方式无需CPU直接控制传输,也没有中断处理方式那样保留现场和恢复现场过程,通过硬件为RAM和IO设备开辟一条直接传输数据的通道,使得CPU的效率大大提高。. f/ I" J* X5 u* x7 _; j8 W+ R% u5 {
1 V, C0 F, \9 v
主要特点:0 p9 A7 C0 B/ n+ m  s( b
•DMA上多达7个可独立配置的通道(请求)
4 Y+ ]! i1 D' s, E
6 q7 B. Y4 |& g; u) S/ a•每个通道都连接到专用的硬件DMA请求,软件触发为
! k$ [+ i/ F9 N$ C! X* Q
; _6 r9 P% `2 @9 L2 F* Y0 q4 g每个频道也都支持。该配置由软件完成。: d) ?  h6 g* K6 c# k

1 t5 w& \1 h5 i0 m•DMA通道的请求之间的优先级是软件可编程的(4
7 |+ t% G( v2 T5 q- [2 h  S3 g0 E7 j, [- y! c
级别由非常高,高,中,低)或硬件组成(在相等的情况下)2 F" o1 v" {+ O& Q
% ^: G1 x" T! H7 I) `
(请求1的优先级高于请求2的优先级,依此类推)
3 t4 r# T' j% [& r9 K  y
5 \  p$ n- O( g  U•独立的源和目标传输大小(字节,半字,字),模拟5 |9 u+ P3 A6 K) h5 F9 H

: X8 C8 v# s; b" W包装和拆箱。源/目标地址必须与数据对齐
) N' ?) Y  c; O( F9 D9 h/ o3 Z& o( E: ]4 G% [- k/ K- D
尺寸。
6 x1 ~, h7 L( [. _% [( ]# E# |$ }
•支持循环缓冲区管理
# a0 H0 r, G) Q& v% o$ o8 t5 m1 |( _" |9 p* q1 P$ p
•3个事件标志(DMA半传输,DMA传输完成和DMA传输错误)
& m+ [; l) V2 |3 O2 S" J
% w! B6 B' ?  l3 L  h: l在每个通道的单个中断请求中进行逻辑或运算3 S- K9 M# ~0 E  a
( Z) B+ x4 h8 g1 Z# P. G
•内存到内存的传输
* {5 u2 |9 ^1 n  D8 z- |5 u% M$ y
  [4 h/ x' W* w; Q•外围到内存和内存到外围,以及外围到外围
% `2 j* m. w) n) V% ?) m8 j% j, |& I" M
转移1 z2 @- H: r! _, P& E2 s

) A6 U3 B: A9 i•访问闪存,SRAM,APB和AHB外设作为源和目标
, }- N% E+ W+ P5 Y  \! p% y  k: y. k
•可编程的数据传输数量:最多65535( a, }' s( y4 `) @( z  s+ k
# H( N6 F( @0 M& f, ?
DMA通道对应的外设情况(F0系列):
- a. J" \9 i1 ]
$ A  {# _/ u- F( j# `
aHR0cHM6Ly9tbWJpei5xcGljLmNuL3N6X21tYml6X3BuZy91Y2RZbUdYTGlhOThxM0tXM2pWY0ZMdU9m.png

0 G9 i" H! H) j4 x* Y
: u0 `1 A# o% S; r" J6 D
aHR0cHM6Ly9tbWJpei5xcGljLmNuL3N6X21tYml6X3BuZy91Y2RZbUdYTGlhOThxM0tXM2pWY0ZMdU9m.jpg

1 [( a. E4 i: @+ M( |# d% N$ R$ ?
+ l6 p) ?# W3 `: @3 W7 {* P5 i1 [" Y$ H2 Q3 X3 U! e5 t

0 h' D9 d$ g/ g6 a9 @6 z7 h    很多博文会讲解里面的详细定义,对于每一处寄存器的详细操作指导,所以我就不会去多写了。
8 o3 x6 r5 y) W8 C5 N# r- j; ]2 y. H8 d( x/ P# @2 k7 ~
    我只想表达,DMA是一种可以快速相关外设数据交互的一种方法,我们学习哪里用它,怎么用它,至于细节学习,大家去网上所搜DMA相信息即可。    7 p: W4 G: ^! _- e* w9 @9 Z: [) t

3 Y3 W* {2 Z$ o/ u7 L* R4 t    之前讲过DMA的数据发送,现在补充上DMA的接收数据部分。
6 V0 `; e) F" n( }3 b) e7 `- Z* j8 @1 i' K/ i$ L3 [
  利用DMA接收串口数据的配置,大致分为:1.初始化串口并开启DMAR接收功能,配置DMA的外设到内存的数据接收功能,2.等待串口中断提示,并进行处理数据,3.清空DMA,重新等待数据8 {8 U7 O4 r% \( \/ Y7 e
; h# _# W4 G+ k2 V# j3 g5 s
01 配置串口与DMA
$ H  O2 ^# j8 C6 k    其中USART2->CR3寄存器的第6 bit用来设置DMA的接收配置,此处比较重要我们设置为USART_CR3_DMAR。 USART2->CR1寄存器中开启帧信息接收完成之后的中断,USART_CR1_IDLEIE,这处可以帮助我们节省CPU的不必要开支,开启此处中断类型,我们只需要在每帧信息接收完成之后,usart才触发中断,我们再解析。其余为正常的usart配置。+ E, o2 M: W6 {! J" E
: X/ h; F0 B9 V0 Z
aHR0cHM6Ly9tbWJpei5xcGljLmNuL3N6X21tYml6X3BuZy91Y2RZbUdYTGlhOThxM0tXM2pWY0ZMdU9m.png
2 _8 n1 d( I2 u- Q& K0 S
, }3 b" y: X( d' B2 g  N
  1. GPIOD->MODER |=   GPIO_MODER_MODER5_1|  //USART2_TX
    3 ?0 i& [9 q/ f
  2.                   GPIO_MODER_MODER6_1;  //USART2_RX  - U4 K# B' f% L. a7 ^! U! w
  3.   /* set baudrate */
    ; j8 e0 d& h& |
  4.   USART2->BRR = USART_Baudrate;//115200
    + m; W! h. E' M
  5.   USART2->CR3 |= USART_CR3_DMAT|USART_CR3_DMAR;//USART_CR3_OVRDIS;// 不需要覆写
      m8 H& D8 T" b  v/ V2 e

  6. 8 M7 m" k( q- Y. K' y6 j% Y; X# k
  7.   /*enable usart2 and enable tx*/
    + \* }7 p( S  X. R7 Z( Y; m
  8.   USART2->CR1 |= USART_CR1_UE | USART_CR1_TE | USART_CR1_RE | USART_CR1_IDLEIE;
复制代码
( ]8 n4 I! ]  U8 ]$ v
配置DMA:
0 |$ \1 l' H# \' x# z6 D: b7 X7 \$ f
    首先根据上方的映射表,我们初始化了usart2,而usart2 RX对应的DMA通道为DMA1 channel5,所以我们进行DMA1 ch5通道配置。
6 a, T3 C2 G' T
1 o0 K' I$ Q1 L9 X/ F6 i第一个为DMA1_Channel5->CPAR寄存器,在下表可知,用来设置对应接收数据的外设的地址,而USART的接收数据的寄存器为RDR,所以寄存器配置为DMA1_Channel5->CPAR = (uint32_t)&(USART2->RDR);
  ^1 s  r* p. A+ L
" W, a9 b; Y: _+ R' r6 M0 J
aHR0cHM6Ly9tbWJpei5xcGljLmNuL3N6X21tYml6X3BuZy91Y2RZbUdYTGlhOThxM0tXM2pWY0ZMdU9m.jpg
, f3 ]" W, j) r% P( D, P6 l

3 R( I8 H) [' g+ g7 g3 T8 k5 l# w5 [' f8 T5 `
/ E1 r6 P# N) R7 b6 l! X
    设置需要放数据的指定内存位置,根据指导手册可知CMAR为DMA通道用来放置数据的内存地址,所以此处设置为我们定义好的变量的地址。
* ~& B' F4 _8 J- ^3 O( }6 q& j" R) p! S
aHR0cHM6Ly9tbWJpei5xcGljLmNuL3N6X21tYml6X3BuZy91Y2RZbUdYTGlhOThxM0tXM2pWY0ZMdU9m.jpg
# m, r6 ^5 M5 Y! s

& C" [6 z- ?" `9 ]# @8 T    设置单次传送数据量的大小,此处最大可设置为65535byte的数据大小。
( v# ^- C( M+ @
% ]/ a( b* I8 X
aHR0cHM6Ly9tbWJpei5xcGljLmNuL3N6X21tYml6X3BuZy91Y2RZbUdYTGlhOThxM0tXM2pWY0ZMdU9m.jpg
0 x9 v/ ]& r4 P2 F
0 Z  m( @1 }* a0 f
最后开启DMA通道。) L' |* Y3 B# V8 t

, c2 h1 u9 }+ f0 w% Y
aHR0cHM6Ly9tbWJpei5xcGljLmNuL3N6X21tYml6X3BuZy91Y2RZbUdYTGlhOThxM0tXM2pWY0ZMdU9m.jpg

" t0 M1 G- f* b& m3 g
' s5 k$ a5 D% B' t4 g
aHR0cHM6Ly9tbWJpei5xcGljLmNuL3N6X21tYml6X3BuZy91Y2RZbUdYTGlhOThxM0tXM2pWY0ZMdU9m.png
' h& k  w+ k1 C$ M
aHR0cHM6Ly9tbWJpei5xcGljLmNuL3N6X21tYml6X3BuZy91Y2RZbUdYTGlhOThxM0tXM2pWY0ZMdU9m.jpg

( t/ x, O+ [8 u9 s
; d2 O- k+ A6 W
  1. void USART2_DMA_Recive(uint8_t *p_data,uint16_t length)
    9 V/ @& Y6 g8 }
  2. {4 c6 w- V' {7 `
  3.   DMA1_Channel5->CPAR = (uint32_t)&(USART2->RDR);          //Peripheral address, U+ q( y% K1 H
  4.   DMA1_Channel5->CMAR = (uint32_t)p_data;                  //memory address
    1 c) I6 T" e7 F
  5.   DMA1_Channel5->CNDTR = length;                            //Set the length# T! z# X/ V' o' I# q
  6.   DMA1_Channel5->CCR |= DMA_CCR_MINC | DMA_CCR_EN;5 O9 g$ q: G/ Q3 q
  7. }9 V0 `# j" Y, t& \2 m" G: m5 p
复制代码

' o* }. V# F# \: h9 X" j6 f" E02  等待串口中断,处理数据
; w6 m+ J5 Y% J. J0 W1 c
% G1 D4 G( ~5 n3 R, k% L/ T) e
aHR0cHM6Ly9tbWJpei5xcGljLmNuL3N6X21tYml6X3BuZy91Y2RZbUdYTGlhOThxM0tXM2pWY0ZMdU9m.png
4 s" T% r" o/ Y

( X; R  b# d  A* ]! O5 ~+ }) m    此时在debug中,在我们定义好的DMA内存变量,地址在RAM初始完成,后续串口数据接收的时候,DMA会直接将数据置于p_data所对应的内存。
8 J2 h+ g8 I; G% D. S8 i* @2 Y
8 v* V7 Q2 g. s5 Q8 i7 E3 N* b4 ]5 C    然后在中断服务函数我们可以将我们数据进行处理,Uart_Channel_isr[1](USART2->RDR);,这个函数为我自己写的处理函数,中断里面函数都是数据复制,所以我直接在中断执行,一般大家会在中断写个标志,在主循环进行解析。但是DMA接收配合USART_ISR_IDLE标志在STM32平台下并不友好,如果主循环用来解析数据的时候,空闲中断还没有产生,本帧数据还尚未接收完成,但是由于内存数据已经在实时写入,DMA1_Channel5->CNDTR已经有所变化,并早于空闲中断产生,所以主循环就不能用DMA1_Channel5->CNDTR所接收数据长度进行解析了。一般建议,如果解析数据只是单纯的挪移,此时候直接在中断处理即可,对主程序并没有大的阻塞。; K9 z/ n. b7 v5 P* N
9 N4 O. U/ [% W4 y$ b, }
  1. void USART2_IRQHandler(void)! d, p: Y5 H# ?  m: n( a+ S$ m
  2. {: f. j+ e9 A4 q3 B  L
  3.   if ((USART2->ISR & USART_ISR_ORE) == USART_ISR_ORE) % B4 k& F! N+ _8 w1 ~6 P& w4 G
  4.   {  ) a1 q0 F! _* o) [4 S1 L' P1 R6 H
  5.     USART2->ICR |= USART_ICR_ORECF;9 P% T+ H) ^. ~8 G) v
  6.   }- D' S0 ?. g" X. ?( W2 K% Y& v7 N

  7. 1 C9 D5 r3 f; K6 K) L' r
  8.   if ((USART2->ISR & USART_ISR_IDLE) == USART_ISR_IDLE)  //The new frame data receive 3 I& V  j+ F+ }& C( K
  9.   {
    , M! m- s! X! j" m2 C  H+ v
  10.     USART2->ICR |= USART_ICR_IDLECF;   
    + Y( ~+ Y1 N9 G8 d% b  f
  11.     Uart_Channel_isr[1](USART2->RDR);/*读取解析数据*/2 Y) x$ H3 f7 _9 H
  12.    }  0 {! I; g5 H5 i1 _. \  ?
  13. }
复制代码

; l, r; K: B5 l) H) K: U$ k03 恢复DMA,清空CNDTR,等待下次数据到来
0 N0 k# [+ e! p/ b7 u    处理完数据之后,及时将DMA标志以及CNDTR清空,否则CNDTR一直不清空,会导致下次接收数据的时候,造成通道的占用,数据使用过之后,就清理掉CNDTR,这样保证每次接收数据的通道有足够的位置。9 G+ F: ^( E: t/ r  E- w" z+ j* E
  1. static void Usart2_Dma_Reload(uint16_t length)/*清空数据*/
    5 d& [2 ?" s' ^% y' {; m$ p9 Y( `9 }
  2. {/ l9 {& X  u/ G  s6 i
  3.   DMA1_Channel5->CCR &= ~(DMA_CCR_EN);             //disable the dma
    8 i0 I: u. L/ m5 G  O
  4.   DMA1_Channel5->CNDTR = length;          //Set the length, j/ f8 n& x) F9 y5 I4 f$ @
  5.   DMA1_Channel5->CCR |= DMA_CCR_EN;                //enable the dma9 ^* W9 g# }5 I* p* C3 A
  6. }
    " [  j, J9 c: W& S2 X! I. q
复制代码
' G: l; O4 J1 e+ U" C) {0 e  C- M
& B  M; k8 a% `2 ^8 S: H
收藏 1 评论0 发布时间:2021-11-24 13:30

举报

0个回答

所属标签

相似技术帖

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