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

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

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

$ s" C# r5 ?# K) N/ k
aHR0cHM6Ly9tbWJpei5xcGljLmNuL3N6X21tYml6X3BuZy91Y2RZbUdYTGlhOThxM0tXM2pWY0ZMdU9m.jpg

# \1 c1 K. @) ^- j  Z9 _2 `8 i0 B. @
DMA,全称Direct Memory Access,即直接存储器访问。DMA传输将数据从一个地址空间复制到另一个地址空间,提供在外设和存储器之间或者存储器和存储器之间的高速数据传输。当CPU初始化这个传输动作,传输动作本身是由DMA控制器来实现和完成的。DMA传输方式无需CPU直接控制传输,也没有中断处理方式那样保留现场和恢复现场过程,通过硬件为RAM和IO设备开辟一条直接传输数据的通道,使得CPU的效率大大提高。/ S9 l( W- A6 F0 l" x1 S
( L) e  j* S! _- J2 Q5 l
主要特点:
. Q( \" x( g7 C- _9 C) U•DMA上多达7个可独立配置的通道(请求)) _9 v" P; G: A/ E4 r

3 z) U% N1 T5 \' U3 P! c•每个通道都连接到专用的硬件DMA请求,软件触发为
5 Q6 {! Z& x6 T/ u" h4 s& g, l+ s: C3 U1 i
每个频道也都支持。该配置由软件完成。' D1 h6 c2 s8 i$ ]

. c& u8 C' A( a4 j4 ?( L•DMA通道的请求之间的优先级是软件可编程的(4- L; p! r. r# K  j, b1 h1 s
6 O2 l! m3 G; A" }
级别由非常高,高,中,低)或硬件组成(在相等的情况下)0 e9 y3 r# C, L4 d! I; `) z) U
# B8 M# P. g1 y8 _1 P
(请求1的优先级高于请求2的优先级,依此类推)
  \5 Y1 ^9 k3 K* Q0 c! Y& }' G; t9 j8 y3 j" I
•独立的源和目标传输大小(字节,半字,字),模拟
) k  [* [6 ?0 z1 Y+ I8 U, b
: }0 Z% o, f3 O1 @# Q$ T: G包装和拆箱。源/目标地址必须与数据对齐
5 }* c3 y) ^9 m9 s7 M# O4 d7 `: [, k/ |
尺寸。& W+ X5 P4 E, A% ^6 S  |& m6 F' C8 g/ _
( m/ m$ v7 G3 ]" t
•支持循环缓冲区管理1 S/ ?* _* ^* w

6 \. B/ _1 j" u# p9 U6 o. K•3个事件标志(DMA半传输,DMA传输完成和DMA传输错误)
, B4 p3 s4 |4 ?- M2 C4 P! Q
: _0 m- S1 f6 T% E2 U" |$ f  c在每个通道的单个中断请求中进行逻辑或运算
0 W7 Y. C8 B  d1 p
2 z3 E, R; K  Y+ z3 S  U1 ~•内存到内存的传输5 h+ d7 ]0 Z. x% u! I$ ]5 M
4 M4 Q. |* y5 m( i+ z7 c3 R
•外围到内存和内存到外围,以及外围到外围- u: k" {6 T  h: k6 w

+ U; G0 P- `0 X# @转移
" X: Q  P( v, p1 j. o4 p8 e+ T3 a! j& U* ^: D' F; p9 U. k4 d% J
•访问闪存,SRAM,APB和AHB外设作为源和目标
$ M' U% j- ~0 f  d' r4 V& w
2 Q3 |$ [0 x) |' e•可编程的数据传输数量:最多65535
( [" [3 O& k" C( Z* b7 y" G& B7 _$ A  C$ @/ P
DMA通道对应的外设情况(F0系列):( Y4 A# C# R3 K! A* S0 b" v6 _
- A7 V( H" t% [7 y
aHR0cHM6Ly9tbWJpei5xcGljLmNuL3N6X21tYml6X3BuZy91Y2RZbUdYTGlhOThxM0tXM2pWY0ZMdU9m.png
) h) N8 w* H9 f' G" ]/ f( y5 n
- V3 Z7 ?! b0 {+ X6 D
aHR0cHM6Ly9tbWJpei5xcGljLmNuL3N6X21tYml6X3BuZy91Y2RZbUdYTGlhOThxM0tXM2pWY0ZMdU9m.jpg

. f- j8 L0 I& l7 Y5 `& }4 W2 `3 C4 t
. f! p; p7 K. X  E; e* V
8 i* D* b9 h! A. w3 L: P) f" o1 d( L* p9 \& m
    很多博文会讲解里面的详细定义,对于每一处寄存器的详细操作指导,所以我就不会去多写了。  x. R& C7 s- {* \8 F- Y, U

' q2 f$ B! N8 v+ M    我只想表达,DMA是一种可以快速相关外设数据交互的一种方法,我们学习哪里用它,怎么用它,至于细节学习,大家去网上所搜DMA相信息即可。   
" w9 \; X0 R9 R* {# f8 u; ]0 V' F& O% w2 _5 j! {" K3 r& a
    之前讲过DMA的数据发送,现在补充上DMA的接收数据部分。
, Q1 f% y1 c/ S$ F/ E( N  d# @- H  P' F
  利用DMA接收串口数据的配置,大致分为:1.初始化串口并开启DMAR接收功能,配置DMA的外设到内存的数据接收功能,2.等待串口中断提示,并进行处理数据,3.清空DMA,重新等待数据5 Y2 n! i7 `2 o9 T6 P' l) j) F
4 Z* n0 O$ g  a5 p
01 配置串口与DMA1 S- _8 {! R" ~$ c  Z: ^
    其中USART2->CR3寄存器的第6 bit用来设置DMA的接收配置,此处比较重要我们设置为USART_CR3_DMAR。 USART2->CR1寄存器中开启帧信息接收完成之后的中断,USART_CR1_IDLEIE,这处可以帮助我们节省CPU的不必要开支,开启此处中断类型,我们只需要在每帧信息接收完成之后,usart才触发中断,我们再解析。其余为正常的usart配置。; Z3 G; o% \) p0 I% {! Q& C* ^. k5 D
, O5 {( Z8 ^6 U' B! G% @
aHR0cHM6Ly9tbWJpei5xcGljLmNuL3N6X21tYml6X3BuZy91Y2RZbUdYTGlhOThxM0tXM2pWY0ZMdU9m.png

) e9 Q1 r  y$ E- M+ i; t5 o
5 ~! u) {' j% u1 D- w
  1. GPIOD->MODER |=   GPIO_MODER_MODER5_1|  //USART2_TX! X' W. I" w. g& P+ {; n
  2.                   GPIO_MODER_MODER6_1;  //USART2_RX  
    2 |2 [) _5 l- f
  3.   /* set baudrate */
    2 N9 ^! A) W5 i3 W. c
  4.   USART2->BRR = USART_Baudrate;//115200' G$ q- j" s0 G( I
  5.   USART2->CR3 |= USART_CR3_DMAT|USART_CR3_DMAR;//USART_CR3_OVRDIS;// 不需要覆写& E, o0 U0 y; o! ]
  6. ​
    ! s/ J6 B1 Q$ J# p; Y# ?: n) f
  7.   /*enable usart2 and enable tx*/
    . y3 q2 u7 x" Y! |
  8.   USART2->CR1 |= USART_CR1_UE | USART_CR1_TE | USART_CR1_RE | USART_CR1_IDLEIE;
复制代码

& N6 @- n, w/ T6 U  E9 m配置DMA:' ~0 m5 E* n: I2 a% Q' c" D/ S
5 ^( Z; q* w( M' E% F
    首先根据上方的映射表,我们初始化了usart2,而usart2 RX对应的DMA通道为DMA1 channel5,所以我们进行DMA1 ch5通道配置。* T- |# j  F& v  p4 C8 ]
2 w2 _4 P% q9 S: ^5 |
第一个为DMA1_Channel5->CPAR寄存器,在下表可知,用来设置对应接收数据的外设的地址,而USART的接收数据的寄存器为RDR,所以寄存器配置为DMA1_Channel5->CPAR = (uint32_t)&(USART2->RDR);
  ]5 {# G( O+ l8 q' W' G8 i! X0 n+ x; z
aHR0cHM6Ly9tbWJpei5xcGljLmNuL3N6X21tYml6X3BuZy91Y2RZbUdYTGlhOThxM0tXM2pWY0ZMdU9m.jpg
# t0 A4 h$ j9 z8 K% g5 ~7 @

3 V/ q8 D: A/ U( t! h: A
' ^7 n' s" J* y) r: u: x' `; }, \: z# X! M/ h% K
    设置需要放数据的指定内存位置,根据指导手册可知CMAR为DMA通道用来放置数据的内存地址,所以此处设置为我们定义好的变量的地址。( b7 n) V# E: `% j8 P; [
9 ~8 R/ X9 [$ i% a& }+ I8 W
aHR0cHM6Ly9tbWJpei5xcGljLmNuL3N6X21tYml6X3BuZy91Y2RZbUdYTGlhOThxM0tXM2pWY0ZMdU9m.jpg

6 ^7 w8 m. @/ v+ Y8 {: d" C* Z' g' A! f5 _- s
    设置单次传送数据量的大小,此处最大可设置为65535byte的数据大小。& N) r5 R- U5 O4 {+ r
. K* E# o0 A9 }0 Q
aHR0cHM6Ly9tbWJpei5xcGljLmNuL3N6X21tYml6X3BuZy91Y2RZbUdYTGlhOThxM0tXM2pWY0ZMdU9m.jpg
+ B' Z& L) p- f& q8 d7 E- L! O& c

- W' x% d0 r- k! T' l最后开启DMA通道。
1 R3 G: O, S8 l  T! Q' ]; O! s
1 b0 F, s2 A+ k/ o6 o6 p% l, x3 ?4 a
aHR0cHM6Ly9tbWJpei5xcGljLmNuL3N6X21tYml6X3BuZy91Y2RZbUdYTGlhOThxM0tXM2pWY0ZMdU9m.jpg
# }" \. {2 g3 h9 }
* G+ s2 ?( c. d1 q* z
aHR0cHM6Ly9tbWJpei5xcGljLmNuL3N6X21tYml6X3BuZy91Y2RZbUdYTGlhOThxM0tXM2pWY0ZMdU9m.png
! A# J3 t7 L  a. [: ^) Y
aHR0cHM6Ly9tbWJpei5xcGljLmNuL3N6X21tYml6X3BuZy91Y2RZbUdYTGlhOThxM0tXM2pWY0ZMdU9m.jpg

3 ^+ x( X. J' O+ `2 _9 D' l8 P' k
% }6 _+ x: H6 W
  1. void USART2_DMA_Recive(uint8_t *p_data,uint16_t length)
    + s4 F( {- X4 ^6 H9 x4 a
  2. {
    * R4 p7 a. T! Z0 `3 q
  3.   DMA1_Channel5->CPAR = (uint32_t)&(USART2->RDR);          //Peripheral address
    ; b' O" Z! v/ ~" e' E! a
  4.   DMA1_Channel5->CMAR = (uint32_t)p_data;                  //memory address
    # H4 N# r9 i  h: u
  5.   DMA1_Channel5->CNDTR = length;                            //Set the length
    7 @$ d0 z# F: Z; x$ d; O3 b
  6.   DMA1_Channel5->CCR |= DMA_CCR_MINC | DMA_CCR_EN;
    & j6 Q9 d4 X! |0 D' X" J$ A; J
  7. }
    + t: N. p( r7 _6 L
复制代码
4 d  W# P5 w# L! h$ X2 I5 Y
02  等待串口中断,处理数据
3 S( G( G. F8 j% ]8 \) Y, J+ {1 u
aHR0cHM6Ly9tbWJpei5xcGljLmNuL3N6X21tYml6X3BuZy91Y2RZbUdYTGlhOThxM0tXM2pWY0ZMdU9m.png
: h% H, ~' ^4 }; s5 T4 o! p

% R9 o2 q2 ^* t6 v* }9 W; B" Y    此时在debug中,在我们定义好的DMA内存变量,地址在RAM初始完成,后续串口数据接收的时候,DMA会直接将数据置于p_data所对应的内存。% k9 D, H4 l, K: a& w& K1 v
' f! y' t. i- B6 A$ _* U$ G
    然后在中断服务函数我们可以将我们数据进行处理,Uart_Channel_isr[1](USART2->RDR);,这个函数为我自己写的处理函数,中断里面函数都是数据复制,所以我直接在中断执行,一般大家会在中断写个标志,在主循环进行解析。但是DMA接收配合USART_ISR_IDLE标志在STM32平台下并不友好,如果主循环用来解析数据的时候,空闲中断还没有产生,本帧数据还尚未接收完成,但是由于内存数据已经在实时写入,DMA1_Channel5->CNDTR已经有所变化,并早于空闲中断产生,所以主循环就不能用DMA1_Channel5->CNDTR所接收数据长度进行解析了。一般建议,如果解析数据只是单纯的挪移,此时候直接在中断处理即可,对主程序并没有大的阻塞。
: p) `" V! P( `9 _2 O5 |8 f* B! y
  1. void USART2_IRQHandler(void)
    " ~# J8 S! F! G# P* q" D
  2. {7 @* L  l2 o0 o* [
  3.   if ((USART2->ISR & USART_ISR_ORE) == USART_ISR_ORE)
    8 S( S$ w; M7 V9 N5 G
  4.   {  
      c! {6 [( d, ^' A; z( h* E
  5.     USART2->ICR |= USART_ICR_ORECF;) H- J) P6 G/ X: \; `" H6 o: W
  6.   }1 G( l" D1 q$ o4 l2 w1 Q9 O
  7. 8 s+ Z" s5 Z+ M; j; z5 l+ [
  8.   if ((USART2->ISR & USART_ISR_IDLE) == USART_ISR_IDLE)  //The new frame data receive 7 P" I+ n0 p4 g; a
  9.   {
    5 C( `% u0 A3 M$ t$ i+ ?
  10.     USART2->ICR |= USART_ICR_IDLECF;    - T- J. k2 n. D" X& a' O" Y
  11.     Uart_Channel_isr[1](USART2->RDR);/*读取解析数据*/' o2 p9 F. j( ]+ \8 U0 B/ A
  12.    }  
    9 a5 ]5 C- I1 S: u3 Y8 M
  13. }
复制代码

( F7 \( g8 J/ A$ e! |% w03 恢复DMA,清空CNDTR,等待下次数据到来
+ i6 H- [  j$ N) P0 f  h- g, d    处理完数据之后,及时将DMA标志以及CNDTR清空,否则CNDTR一直不清空,会导致下次接收数据的时候,造成通道的占用,数据使用过之后,就清理掉CNDTR,这样保证每次接收数据的通道有足够的位置。# h0 _) {2 l6 m3 t! f& e( _
  1. static void Usart2_Dma_Reload(uint16_t length)/*清空数据*/
      w$ z; ?6 T3 o9 s
  2. {& h1 @1 L5 P9 [
  3.   DMA1_Channel5->CCR &= ~(DMA_CCR_EN);             //disable the dma
    , k3 w3 U8 {8 j9 m- j" @: |
  4.   DMA1_Channel5->CNDTR = length;          //Set the length
    * r1 L: ]3 e/ `. [2 f
  5.   DMA1_Channel5->CCR |= DMA_CCR_EN;                //enable the dma
    ; P' \9 [) C3 a
  6. }
    $ P: v1 s" J9 N! w1 A
复制代码

) X0 Q7 ?: Y6 y, }* U2 R! I/ @5 }
5 O5 W+ F& d! }
赞 收藏 1 评论0 发布时间:2021-11-24 13:30

举报

0个回答

所属标签

相似技术帖

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