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

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

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

! _) v% E! ?8 f5 g; y
aHR0cHM6Ly9tbWJpei5xcGljLmNuL3N6X21tYml6X3BuZy91Y2RZbUdYTGlhOThxM0tXM2pWY0ZMdU9m.jpg
/ f$ s1 d$ {  @$ {5 P+ k
/ V' P0 n0 h4 j+ \& W6 k; N
DMA,全称Direct Memory Access,即直接存储器访问。DMA传输将数据从一个地址空间复制到另一个地址空间,提供在外设和存储器之间或者存储器和存储器之间的高速数据传输。当CPU初始化这个传输动作,传输动作本身是由DMA控制器来实现和完成的。DMA传输方式无需CPU直接控制传输,也没有中断处理方式那样保留现场和恢复现场过程,通过硬件为RAM和IO设备开辟一条直接传输数据的通道,使得CPU的效率大大提高。3 m3 {- p+ R6 X/ O' Z1 a

7 _) P  m+ F, [/ \  K' E7 [主要特点:
' e- n8 s$ y& P# E( W% F, u" m•DMA上多达7个可独立配置的通道(请求)% Q2 t* X- D' y5 A8 C- Q- X

( ?3 u( j2 ~  l3 P/ b•每个通道都连接到专用的硬件DMA请求,软件触发为7 ]$ m2 l8 W; J% I+ d5 U
0 i9 v  R( p4 R/ |# ?
每个频道也都支持。该配置由软件完成。5 M0 n% ~7 Q9 J% n
; T0 D  B; g3 W& d
•DMA通道的请求之间的优先级是软件可编程的(4
! `. X" q+ f' S# B+ m
4 F1 |! p7 J; b5 [( _+ E. p" k级别由非常高,高,中,低)或硬件组成(在相等的情况下)
2 n: D6 N6 g3 m1 i+ \2 i: N0 q# m$ Q: Y% w. |1 k
(请求1的优先级高于请求2的优先级,依此类推)
& w' X# W& L- O# ^2 R; Q3 _+ W7 b" m5 e" b) t6 I! O
•独立的源和目标传输大小(字节,半字,字),模拟, e; D4 \! R; f0 }4 Q8 k9 e, S& U
7 S8 W5 Z! J5 L$ q( o  _% D& s1 s
包装和拆箱。源/目标地址必须与数据对齐" _4 l8 @$ h/ k- G1 K+ j+ [

5 h2 i: K5 h5 |3 U$ W  f$ p尺寸。
2 w' T# e- f* _- |  h
6 P7 J* E2 m6 @( g3 Y- D7 B•支持循环缓冲区管理5 n1 L! U4 j, B8 \& @) i
* F) `7 W' h2 B( d
•3个事件标志(DMA半传输,DMA传输完成和DMA传输错误)
3 m4 S) Y1 d$ W; f2 [- U+ I+ \1 D2 @" y
在每个通道的单个中断请求中进行逻辑或运算
: K- j4 Q( B7 a4 B- d6 g
- J# k4 n$ ^4 [& j$ k" m& X•内存到内存的传输
( j/ I7 c' j1 {3 ^& ]) F" d
% ]' P! c% t8 Y# }1 \•外围到内存和内存到外围,以及外围到外围! D/ ]2 w( s* y# [# m- s

( H! W+ K9 O1 V9 P* V转移
% ?1 q) X- z4 f3 i* X; k0 \! N
8 Y7 R5 \$ `; v2 d3 N, F2 b' o•访问闪存,SRAM,APB和AHB外设作为源和目标
6 ?+ C' L: ?% N+ i$ W3 Z8 Z  \  w. x  d
•可编程的数据传输数量:最多655357 b: u1 D- {+ C, c' F
+ g& K% n) Y( t+ ^9 q: g# ?2 z* j
DMA通道对应的外设情况(F0系列):( A: w9 d" q- n2 P! Q# f
6 w& v7 c' t" h4 u
aHR0cHM6Ly9tbWJpei5xcGljLmNuL3N6X21tYml6X3BuZy91Y2RZbUdYTGlhOThxM0tXM2pWY0ZMdU9m.png

2 |5 j  J- I- o' Y' N/ V% j
) t! l( T! x9 |- o+ A5 h
aHR0cHM6Ly9tbWJpei5xcGljLmNuL3N6X21tYml6X3BuZy91Y2RZbUdYTGlhOThxM0tXM2pWY0ZMdU9m.jpg
1 g. J) [! {( u8 H) g/ O- q

2 K' f- q; ?- d
! y, l5 u2 q  ?6 d. u; w8 O) }6 C1 l  _2 j4 k
    很多博文会讲解里面的详细定义,对于每一处寄存器的详细操作指导,所以我就不会去多写了。
+ M- [  s2 {- j1 `8 t
, L7 i* Q. S- R: _    我只想表达,DMA是一种可以快速相关外设数据交互的一种方法,我们学习哪里用它,怎么用它,至于细节学习,大家去网上所搜DMA相信息即可。    5 Q/ w1 I. r5 C$ n% M

- }4 Y" s& X. Z1 m5 b: g    之前讲过DMA的数据发送,现在补充上DMA的接收数据部分。% s) U4 }$ v& y8 U5 e9 v
. @) X: L- K- v
  利用DMA接收串口数据的配置,大致分为:1.初始化串口并开启DMAR接收功能,配置DMA的外设到内存的数据接收功能,2.等待串口中断提示,并进行处理数据,3.清空DMA,重新等待数据- q! J8 \3 l$ t" s

! b+ e; h4 {" X+ _; _7 D7 K5 |01 配置串口与DMA
3 q0 {; T* _) ^1 c  b8 `    其中USART2->CR3寄存器的第6 bit用来设置DMA的接收配置,此处比较重要我们设置为USART_CR3_DMAR。 USART2->CR1寄存器中开启帧信息接收完成之后的中断,USART_CR1_IDLEIE,这处可以帮助我们节省CPU的不必要开支,开启此处中断类型,我们只需要在每帧信息接收完成之后,usart才触发中断,我们再解析。其余为正常的usart配置。
' T" }1 v& l9 K
7 C5 O2 S7 `& D$ e  a: z
aHR0cHM6Ly9tbWJpei5xcGljLmNuL3N6X21tYml6X3BuZy91Y2RZbUdYTGlhOThxM0tXM2pWY0ZMdU9m.png
) t' Z/ V/ Q8 w0 e

& @/ b( }' k3 S5 R, c, b1 a0 u
  1. GPIOD->MODER |=   GPIO_MODER_MODER5_1|  //USART2_TX0 c8 C' s  T- c2 `4 k( D
  2.                   GPIO_MODER_MODER6_1;  //USART2_RX  
    " u+ I8 F! y( b
  3.   /* set baudrate */: A/ h' P! G7 `
  4.   USART2->BRR = USART_Baudrate;//115200
    / y3 n  {/ I$ E7 `4 ]
  5.   USART2->CR3 |= USART_CR3_DMAT|USART_CR3_DMAR;//USART_CR3_OVRDIS;// 不需要覆写
    ' g8 i0 N# M9 i4 t" S  R
  6. 6 H0 e0 R8 t7 ^/ q) d9 N
  7.   /*enable usart2 and enable tx*/
    ; M' x2 {2 D4 M
  8.   USART2->CR1 |= USART_CR1_UE | USART_CR1_TE | USART_CR1_RE | USART_CR1_IDLEIE;
复制代码
0 f6 {) I6 N/ i, K' k
配置DMA:) B% V4 h$ L  L5 T# m6 f
% q3 o( }9 Z' U; z6 h+ x' L
    首先根据上方的映射表,我们初始化了usart2,而usart2 RX对应的DMA通道为DMA1 channel5,所以我们进行DMA1 ch5通道配置。, w; X% _9 ]8 l  @8 c

0 q' s; ?2 V! \* Y# U, s1 N第一个为DMA1_Channel5->CPAR寄存器,在下表可知,用来设置对应接收数据的外设的地址,而USART的接收数据的寄存器为RDR,所以寄存器配置为DMA1_Channel5->CPAR = (uint32_t)&(USART2->RDR);
$ a- o$ D. j8 W$ c' W1 L6 f' z4 H" W
aHR0cHM6Ly9tbWJpei5xcGljLmNuL3N6X21tYml6X3BuZy91Y2RZbUdYTGlhOThxM0tXM2pWY0ZMdU9m.jpg

8 L& }$ l1 v# o  ^" r% I1 X5 o6 E
9 b* I. A* P# b: h2 Z' _* J
" N* N9 ~- P, V
    设置需要放数据的指定内存位置,根据指导手册可知CMAR为DMA通道用来放置数据的内存地址,所以此处设置为我们定义好的变量的地址。
" ?3 ?: l( l7 O( b: Q# K4 ~8 S+ K( r, q
aHR0cHM6Ly9tbWJpei5xcGljLmNuL3N6X21tYml6X3BuZy91Y2RZbUdYTGlhOThxM0tXM2pWY0ZMdU9m.jpg

6 V2 A* \$ _% m- k9 l( O) ^* _" A) c
    设置单次传送数据量的大小,此处最大可设置为65535byte的数据大小。4 D* s$ n. \; X! r* ^. C7 R8 G5 N

3 D9 D# m' ]1 M( B% o; j/ d2 k6 E! e
aHR0cHM6Ly9tbWJpei5xcGljLmNuL3N6X21tYml6X3BuZy91Y2RZbUdYTGlhOThxM0tXM2pWY0ZMdU9m.jpg
6 N; @: `# F9 z# r% r
" U& H, R# g% t
最后开启DMA通道。7 E( M' o) |' ?( T/ @

% Z. @" L6 }, X5 q" J9 n0 n
aHR0cHM6Ly9tbWJpei5xcGljLmNuL3N6X21tYml6X3BuZy91Y2RZbUdYTGlhOThxM0tXM2pWY0ZMdU9m.jpg
! n# V+ c5 d* Z2 c& Z
2 [# k- Z" d5 T7 X
aHR0cHM6Ly9tbWJpei5xcGljLmNuL3N6X21tYml6X3BuZy91Y2RZbUdYTGlhOThxM0tXM2pWY0ZMdU9m.png

+ _) e* u2 r  D& Y) ~
aHR0cHM6Ly9tbWJpei5xcGljLmNuL3N6X21tYml6X3BuZy91Y2RZbUdYTGlhOThxM0tXM2pWY0ZMdU9m.jpg

5 p* }+ a. |1 }, `7 B$ N3 {, _! l9 D; M1 m5 D9 |
  1. void USART2_DMA_Recive(uint8_t *p_data,uint16_t length)
    ; q. g& G" L) O8 C6 E5 }5 c
  2. {7 \/ V" X# a  H
  3.   DMA1_Channel5->CPAR = (uint32_t)&(USART2->RDR);          //Peripheral address# F7 f8 u/ ~. A# U: ?' B3 c% L
  4.   DMA1_Channel5->CMAR = (uint32_t)p_data;                  //memory address" H7 a! i* r9 y$ n4 h* {
  5.   DMA1_Channel5->CNDTR = length;                            //Set the length
    4 |! s  L# F7 ]' t5 J! ]0 @" k
  6.   DMA1_Channel5->CCR |= DMA_CCR_MINC | DMA_CCR_EN;, _9 K- L  i  U& I
  7. }
    9 q  T1 J* u* r4 O2 Q$ }
复制代码
% n  d$ b) v0 @1 ^' q: {2 m
02  等待串口中断,处理数据
3 u5 C2 A" N, q# O0 N7 `9 W7 k2 [, @
aHR0cHM6Ly9tbWJpei5xcGljLmNuL3N6X21tYml6X3BuZy91Y2RZbUdYTGlhOThxM0tXM2pWY0ZMdU9m.png

6 h# a- Y& ?- k7 H- w7 _/ J6 e$ f: D- q% c9 Y9 j9 W
    此时在debug中,在我们定义好的DMA内存变量,地址在RAM初始完成,后续串口数据接收的时候,DMA会直接将数据置于p_data所对应的内存。
& S/ }* `* J6 d/ M/ v2 n9 |4 q! M1 E! U
    然后在中断服务函数我们可以将我们数据进行处理,Uart_Channel_isr[1](USART2->RDR);,这个函数为我自己写的处理函数,中断里面函数都是数据复制,所以我直接在中断执行,一般大家会在中断写个标志,在主循环进行解析。但是DMA接收配合USART_ISR_IDLE标志在STM32平台下并不友好,如果主循环用来解析数据的时候,空闲中断还没有产生,本帧数据还尚未接收完成,但是由于内存数据已经在实时写入,DMA1_Channel5->CNDTR已经有所变化,并早于空闲中断产生,所以主循环就不能用DMA1_Channel5->CNDTR所接收数据长度进行解析了。一般建议,如果解析数据只是单纯的挪移,此时候直接在中断处理即可,对主程序并没有大的阻塞。
: t3 X" X9 P3 u: z/ t# @9 a, [. v; _
5 m  F; M' Y0 E: F
  1. void USART2_IRQHandler(void)
    - j$ [" k  j7 D  R  K  {
  2. {  h% q" y- d9 g8 b$ b
  3.   if ((USART2->ISR & USART_ISR_ORE) == USART_ISR_ORE)
    9 F$ q0 g  ]4 n' k5 w" U1 A
  4.   {  6 G) d0 @0 t0 v9 y5 a0 U
  5.     USART2->ICR |= USART_ICR_ORECF;
    5 _8 _1 y; \& \$ F& i  o. X
  6.   }+ U* Y) W+ B. S6 B; Q
  7. / Z6 M3 B- u6 Z
  8.   if ((USART2->ISR & USART_ISR_IDLE) == USART_ISR_IDLE)  //The new frame data receive
    2 p% a  A* Q2 C2 k
  9.   {4 ~) `. _' M5 L8 z, j0 Y$ s4 t
  10.     USART2->ICR |= USART_ICR_IDLECF;   
    ; q5 X5 ]; ?" d5 Y; _2 e6 T
  11.     Uart_Channel_isr[1](USART2->RDR);/*读取解析数据*/
    ; A: `7 u2 t$ h3 ^
  12.    }  
    7 p% Z# |6 o* @3 L
  13. }
复制代码

# I3 J7 V, K1 p4 ~" {& Y, b. A03 恢复DMA,清空CNDTR,等待下次数据到来* J1 t1 x$ J; E. A
    处理完数据之后,及时将DMA标志以及CNDTR清空,否则CNDTR一直不清空,会导致下次接收数据的时候,造成通道的占用,数据使用过之后,就清理掉CNDTR,这样保证每次接收数据的通道有足够的位置。
- x0 e2 A8 l* N# [& m
  1. static void Usart2_Dma_Reload(uint16_t length)/*清空数据*/
    ' u+ n3 m2 c% v1 c
  2. {
    % j0 r( t. I; i! @* u# t4 W
  3.   DMA1_Channel5->CCR &= ~(DMA_CCR_EN);             //disable the dma + V: u7 v- G$ b$ e9 U' A7 F. F
  4.   DMA1_Channel5->CNDTR = length;          //Set the length
    " s; X+ O' {. H, Q
  5.   DMA1_Channel5->CCR |= DMA_CCR_EN;                //enable the dma
    9 R  l% [& j3 ?" B4 E
  6. }- M$ G1 J1 ]" \+ ^
复制代码
- X$ ~  }! ^, y# s: [$ M

- z; ~! m- ]+ Z3 l( p$ j
收藏 1 评论0 发布时间:2021-11-24 13:30

举报

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