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

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

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

( ~8 s& K. m: r+ M% r
aHR0cHM6Ly9tbWJpei5xcGljLmNuL3N6X21tYml6X3BuZy91Y2RZbUdYTGlhOThxM0tXM2pWY0ZMdU9m.jpg
% k, g/ p* W& I% ^' g- H

2 g5 x, r/ f7 G# @1 `* VDMA,全称Direct Memory Access,即直接存储器访问。DMA传输将数据从一个地址空间复制到另一个地址空间,提供在外设和存储器之间或者存储器和存储器之间的高速数据传输。当CPU初始化这个传输动作,传输动作本身是由DMA控制器来实现和完成的。DMA传输方式无需CPU直接控制传输,也没有中断处理方式那样保留现场和恢复现场过程,通过硬件为RAM和IO设备开辟一条直接传输数据的通道,使得CPU的效率大大提高。  ]; f' x% B4 ^8 X: U3 p

0 X' J& U/ b0 r& U主要特点:
' Z8 U. H* a% b, x0 }•DMA上多达7个可独立配置的通道(请求)7 Z$ |, e: m* m
# t! i1 x5 n) }5 t* k
•每个通道都连接到专用的硬件DMA请求,软件触发为
2 H  E6 Q9 q& y5 C: _% e' O% c; j4 A6 N: @+ I
每个频道也都支持。该配置由软件完成。  Z* Y4 [! V' M/ l" w4 s# P& x. W: t
1 p$ n6 z, \& \3 \/ ~! S) ?5 k
•DMA通道的请求之间的优先级是软件可编程的(4
* L+ d% w# ?% n9 R) Y5 {- L, A
! M5 w0 V8 X. z( [' c# g级别由非常高,高,中,低)或硬件组成(在相等的情况下)
- V0 D) q5 [& n- E6 t9 g' s; F2 v) O# U4 [
(请求1的优先级高于请求2的优先级,依此类推)- g; h" u! @4 P1 B9 Z8 l
5 g6 R) j# u" G7 a3 h
•独立的源和目标传输大小(字节,半字,字),模拟
% g) Y$ @% c. c& Q! h9 P+ G/ u) k( c; M1 e. c$ l% s7 b% X
包装和拆箱。源/目标地址必须与数据对齐1 F$ Y8 O% F& D2 _
4 f, @& C( R& r4 L
尺寸。7 Z7 _- ^0 N6 y  D0 s2 {

5 d" E) A9 D! m; ]: [. c' B•支持循环缓冲区管理
" b* Q9 G9 u/ P) _4 p: o2 I8 K+ C; V% K
•3个事件标志(DMA半传输,DMA传输完成和DMA传输错误)
( T7 f3 K; i# x( d" D
: f' s  }/ _5 f在每个通道的单个中断请求中进行逻辑或运算
& `  m- p% M6 K. b7 C6 f; E8 E3 P
5 ~. x  z( Y# Z$ n0 w•内存到内存的传输
" x  c8 P) K8 c, U: B' w
* ~0 n* }. E. p•外围到内存和内存到外围,以及外围到外围
  G* ?4 w* h9 k* X
' P/ A# Z7 u1 r3 g) J1 [转移
. @% h$ q1 x6 z5 c8 T( L0 p* H( _$ Y% Q
•访问闪存,SRAM,APB和AHB外设作为源和目标
! k; j) I8 Q  z
- D$ D8 z+ ]! \; M) ~9 o9 J•可编程的数据传输数量:最多65535
( Y& n% o. a9 ?1 p7 z7 o/ ^% Q3 N( X8 c! j; C4 K+ ^( H7 T
DMA通道对应的外设情况(F0系列):* ~- u9 F7 J0 D% e

$ d! @: U% F7 }& m/ a1 H! j9 p
aHR0cHM6Ly9tbWJpei5xcGljLmNuL3N6X21tYml6X3BuZy91Y2RZbUdYTGlhOThxM0tXM2pWY0ZMdU9m.png

7 ~& P  F' l$ A' Z
4 `" _9 d/ _5 U* {1 I$ Y% p8 A" g
aHR0cHM6Ly9tbWJpei5xcGljLmNuL3N6X21tYml6X3BuZy91Y2RZbUdYTGlhOThxM0tXM2pWY0ZMdU9m.jpg
8 m) ^- e% b  z0 L1 b& l* Z; M3 @
7 y1 {0 ]' H" s9 n( z/ `

# P) ^' m% K" G! K4 S# p6 |
; m; q9 D4 D" R    很多博文会讲解里面的详细定义,对于每一处寄存器的详细操作指导,所以我就不会去多写了。
  o) \0 S1 Q" l7 X* n, Q; Z! d) X) r: n  ?+ d. ~+ |$ T
    我只想表达,DMA是一种可以快速相关外设数据交互的一种方法,我们学习哪里用它,怎么用它,至于细节学习,大家去网上所搜DMA相信息即可。    6 q' p: Q5 n) W0 R& D
* n* O! F8 \/ w' O; ~
    之前讲过DMA的数据发送,现在补充上DMA的接收数据部分。
$ U; y1 P1 P' \" I" W+ {+ o& q
( r7 z2 V# g' |4 L  利用DMA接收串口数据的配置,大致分为:1.初始化串口并开启DMAR接收功能,配置DMA的外设到内存的数据接收功能,2.等待串口中断提示,并进行处理数据,3.清空DMA,重新等待数据3 w* Q8 }$ A: s+ w* f4 d
5 g3 \% q) `. R. n9 \6 J5 t1 U
01 配置串口与DMA
7 I) ~+ [! j+ T* f) E4 j5 g% u    其中USART2->CR3寄存器的第6 bit用来设置DMA的接收配置,此处比较重要我们设置为USART_CR3_DMAR。 USART2->CR1寄存器中开启帧信息接收完成之后的中断,USART_CR1_IDLEIE,这处可以帮助我们节省CPU的不必要开支,开启此处中断类型,我们只需要在每帧信息接收完成之后,usart才触发中断,我们再解析。其余为正常的usart配置。. D0 N8 n! f: Y4 I0 y
' N4 E' f2 N- y" N5 d( [6 B
aHR0cHM6Ly9tbWJpei5xcGljLmNuL3N6X21tYml6X3BuZy91Y2RZbUdYTGlhOThxM0tXM2pWY0ZMdU9m.png

. u+ V" s% }& Y) L3 e, T8 n9 k
; g1 i6 @* j3 P8 s- F. a" R
  1. GPIOD->MODER |=   GPIO_MODER_MODER5_1|  //USART2_TX3 y( ]" Y5 L+ n  h1 }
  2.                   GPIO_MODER_MODER6_1;  //USART2_RX  
    , g" j/ r! v. o+ N( t
  3.   /* set baudrate */
    & {7 y$ r1 g( b; t
  4.   USART2->BRR = USART_Baudrate;//115200
    ; M& {( G1 {% E8 X1 b3 ]
  5.   USART2->CR3 |= USART_CR3_DMAT|USART_CR3_DMAR;//USART_CR3_OVRDIS;// 不需要覆写
    % _/ h! u9 N6 J6 q
  6. 7 m% d) f9 C5 X2 Q& x$ q
  7.   /*enable usart2 and enable tx*/
    : N1 R! I! ?, a# ?- F% G
  8.   USART2->CR1 |= USART_CR1_UE | USART_CR1_TE | USART_CR1_RE | USART_CR1_IDLEIE;
复制代码

5 b6 l+ R% t: ^, p0 u配置DMA:
  Z$ b& i% `$ o! t% d
% R! D/ x! m) B    首先根据上方的映射表,我们初始化了usart2,而usart2 RX对应的DMA通道为DMA1 channel5,所以我们进行DMA1 ch5通道配置。6 a9 R( u/ B3 u& k

' `, a. L0 {' D- g第一个为DMA1_Channel5->CPAR寄存器,在下表可知,用来设置对应接收数据的外设的地址,而USART的接收数据的寄存器为RDR,所以寄存器配置为DMA1_Channel5->CPAR = (uint32_t)&(USART2->RDR);: ]- q, [$ q; p6 Q6 ?+ L
" ~0 Z: M9 q/ r! ^  Q
aHR0cHM6Ly9tbWJpei5xcGljLmNuL3N6X21tYml6X3BuZy91Y2RZbUdYTGlhOThxM0tXM2pWY0ZMdU9m.jpg

$ m. s/ p+ g- r% {( H" l- D
- j  q5 R, k9 j5 T
, r1 Z' M" v# s( B" ?8 n( a4 k( K8 |: S
    设置需要放数据的指定内存位置,根据指导手册可知CMAR为DMA通道用来放置数据的内存地址,所以此处设置为我们定义好的变量的地址。& H, T5 B( J' @3 x! B% r

- Q# W& l) G( N  S. g$ p% A
aHR0cHM6Ly9tbWJpei5xcGljLmNuL3N6X21tYml6X3BuZy91Y2RZbUdYTGlhOThxM0tXM2pWY0ZMdU9m.jpg

# z" y+ z5 b; j
5 q6 p7 T3 W$ {5 ~( T    设置单次传送数据量的大小,此处最大可设置为65535byte的数据大小。9 r5 `) C# p$ j$ C; k

8 N. p8 C/ k' F: k
aHR0cHM6Ly9tbWJpei5xcGljLmNuL3N6X21tYml6X3BuZy91Y2RZbUdYTGlhOThxM0tXM2pWY0ZMdU9m.jpg

( @" U3 w  h* e+ @* A/ G' J6 _! w
+ v1 h7 u; I# A最后开启DMA通道。) \# T" r  z+ D6 T8 h+ v+ }

; L: Q  E! f2 B, A5 P
aHR0cHM6Ly9tbWJpei5xcGljLmNuL3N6X21tYml6X3BuZy91Y2RZbUdYTGlhOThxM0tXM2pWY0ZMdU9m.jpg

/ t5 }0 S7 y5 |4 H5 k' r3 H& {$ U/ L. S( z" d
aHR0cHM6Ly9tbWJpei5xcGljLmNuL3N6X21tYml6X3BuZy91Y2RZbUdYTGlhOThxM0tXM2pWY0ZMdU9m.png
' j7 a; N1 F+ \: E3 n6 e8 \& @) b
aHR0cHM6Ly9tbWJpei5xcGljLmNuL3N6X21tYml6X3BuZy91Y2RZbUdYTGlhOThxM0tXM2pWY0ZMdU9m.jpg
; |/ V% v/ `4 i; D. ?$ R
1 r- O; r) @$ N( H+ M
  1. void USART2_DMA_Recive(uint8_t *p_data,uint16_t length)
    ( [+ b# e- Z( V- v
  2. {, q6 o. B/ |2 S; w0 D
  3.   DMA1_Channel5->CPAR = (uint32_t)&(USART2->RDR);          //Peripheral address& b1 c% L7 d" R+ U. i
  4.   DMA1_Channel5->CMAR = (uint32_t)p_data;                  //memory address0 o0 Y5 x  R( e
  5.   DMA1_Channel5->CNDTR = length;                            //Set the length
    1 d( }+ c# [, ~9 R
  6.   DMA1_Channel5->CCR |= DMA_CCR_MINC | DMA_CCR_EN;
    ' ^) c. d1 I) k
  7. }
    ' f- y' n1 z3 ]8 Q6 T( K
复制代码

4 H+ k& \$ ~4 O3 T9 X02  等待串口中断,处理数据# e; v8 G# n% }5 ^4 {- d
5 e: [9 _/ [3 Q% p
aHR0cHM6Ly9tbWJpei5xcGljLmNuL3N6X21tYml6X3BuZy91Y2RZbUdYTGlhOThxM0tXM2pWY0ZMdU9m.png

5 w6 o+ M% B: e9 ]2 O
6 t' c2 Z8 E  M; g5 ^; f* y    此时在debug中,在我们定义好的DMA内存变量,地址在RAM初始完成,后续串口数据接收的时候,DMA会直接将数据置于p_data所对应的内存。
/ S- b) n/ D6 q5 |% V- x
2 i! n0 V1 r. [5 B% L    然后在中断服务函数我们可以将我们数据进行处理,Uart_Channel_isr[1](USART2->RDR);,这个函数为我自己写的处理函数,中断里面函数都是数据复制,所以我直接在中断执行,一般大家会在中断写个标志,在主循环进行解析。但是DMA接收配合USART_ISR_IDLE标志在STM32平台下并不友好,如果主循环用来解析数据的时候,空闲中断还没有产生,本帧数据还尚未接收完成,但是由于内存数据已经在实时写入,DMA1_Channel5->CNDTR已经有所变化,并早于空闲中断产生,所以主循环就不能用DMA1_Channel5->CNDTR所接收数据长度进行解析了。一般建议,如果解析数据只是单纯的挪移,此时候直接在中断处理即可,对主程序并没有大的阻塞。# B' D. a- |* q2 E7 c4 @

' m6 ]5 e% B- C& P" s6 |0 ^
  1. void USART2_IRQHandler(void)
    % t! p' H# J! q5 B7 m
  2. {; K6 r* w# G+ ]7 Q! T$ f# F
  3.   if ((USART2->ISR & USART_ISR_ORE) == USART_ISR_ORE)
    9 s8 V1 B- p' b, E. m, G
  4.   {  
    9 \1 {7 z# k" f+ {' [
  5.     USART2->ICR |= USART_ICR_ORECF;
    6 Q; e1 o0 ]9 c' m5 K9 v5 ^) f
  6.   }
      L$ v1 V& t1 ^& X( Z  |
  7. # O1 Q+ S. p$ x6 l0 k6 `
  8.   if ((USART2->ISR & USART_ISR_IDLE) == USART_ISR_IDLE)  //The new frame data receive
    9 q5 O# |7 o) X  J8 q
  9.   {) h3 I9 U) l" ^
  10.     USART2->ICR |= USART_ICR_IDLECF;    ) A" K+ z: q$ {1 e% X) Y7 s; \1 c
  11.     Uart_Channel_isr[1](USART2->RDR);/*读取解析数据*/+ ^$ \, D7 j- ?7 T% U
  12.    }  
    & {$ ^/ G+ C2 S& I+ Q1 |
  13. }
复制代码

5 n. e! W: Z+ z) Q03 恢复DMA,清空CNDTR,等待下次数据到来8 b1 P) L+ j. r& @) h
    处理完数据之后,及时将DMA标志以及CNDTR清空,否则CNDTR一直不清空,会导致下次接收数据的时候,造成通道的占用,数据使用过之后,就清理掉CNDTR,这样保证每次接收数据的通道有足够的位置。8 F9 x" l- M' D9 j8 l( s! V2 R. d4 m
  1. static void Usart2_Dma_Reload(uint16_t length)/*清空数据*/8 m. N; j* v. \
  2. {
    , S$ c" ?$ e! l4 E
  3.   DMA1_Channel5->CCR &= ~(DMA_CCR_EN);             //disable the dma   X- c  q- M) u3 u: d$ h/ d
  4.   DMA1_Channel5->CNDTR = length;          //Set the length
    6 ?3 T$ x* O, R1 S8 A/ [
  5.   DMA1_Channel5->CCR |= DMA_CCR_EN;                //enable the dma
    " X+ D6 C2 a& Z3 @
  6. }
    , ]2 X# c. e1 H5 k8 |6 T5 e$ y
复制代码

1 n# F& m; R3 N3 @% v4 w% K: |: h1 F" P% L4 R$ X" C
收藏 1 评论0 发布时间:2021-11-24 13:30

举报

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