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

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

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

' `$ |) E0 {, E0 y3 ?
aHR0cHM6Ly9tbWJpei5xcGljLmNuL3N6X21tYml6X3BuZy91Y2RZbUdYTGlhOThxM0tXM2pWY0ZMdU9m.jpg
2 S. ^* W; o3 r; Z& L& P

6 i3 x4 S6 ]7 LDMA,全称Direct Memory Access,即直接存储器访问。DMA传输将数据从一个地址空间复制到另一个地址空间,提供在外设和存储器之间或者存储器和存储器之间的高速数据传输。当CPU初始化这个传输动作,传输动作本身是由DMA控制器来实现和完成的。DMA传输方式无需CPU直接控制传输,也没有中断处理方式那样保留现场和恢复现场过程,通过硬件为RAM和IO设备开辟一条直接传输数据的通道,使得CPU的效率大大提高。
/ `- x6 c/ b0 _$ z8 ?# h) e& g$ s% R" Q+ B) y; }% e# l
主要特点:0 L5 Y+ m8 X% F+ O0 M( |8 ~
•DMA上多达7个可独立配置的通道(请求)* T, W) G9 [% F; f4 V. d5 z: |
! M) h  T; ~7 @1 d
•每个通道都连接到专用的硬件DMA请求,软件触发为
* [) j* Q. O+ f' }5 l0 C1 c( Q, |2 O1 T/ b+ c1 D) G0 `7 S, P# y
每个频道也都支持。该配置由软件完成。/ ^% g. J0 Z5 ?. M% D

/ s8 T8 @* [" c- {; \•DMA通道的请求之间的优先级是软件可编程的(4
: ~# [. ?5 ?3 F, X& P! d
4 J! w( k4 c7 O3 Q$ x级别由非常高,高,中,低)或硬件组成(在相等的情况下)
& A9 U* x" X$ C: R; V3 v! S2 @; @7 R- A) k% `
(请求1的优先级高于请求2的优先级,依此类推)
' r7 \# f+ a* V' B" h3 I3 Z1 Z% f# r9 f# _
•独立的源和目标传输大小(字节,半字,字),模拟9 m& N! R1 c- d9 {  u
  o$ {0 u0 \) T& }$ |+ V- f) Y
包装和拆箱。源/目标地址必须与数据对齐) v2 Y4 W( |: F' u* j# {# C
6 x- D! ]4 E3 ]6 X4 |
尺寸。# e- ]4 u  w& o5 Q6 w! Z3 [
" z$ q8 ~: ]9 g! V3 _& o
•支持循环缓冲区管理" N- W1 m+ z: _+ S+ S' c- a. ?( G

* o, C$ R3 ~# ^! d, i: w  H•3个事件标志(DMA半传输,DMA传输完成和DMA传输错误)& _: ^$ E3 s7 b( D" k  H# {; w

# p. \3 K. W7 m9 ^在每个通道的单个中断请求中进行逻辑或运算
1 l! K! J0 p9 I7 E4 z) f7 C. C( J( r6 Z( S/ V$ O% g; Q
•内存到内存的传输5 L2 u' w9 i! `# l* R

( C+ Y) ?$ |: F* q•外围到内存和内存到外围,以及外围到外围
7 |2 X' y+ c* h, `1 Z
7 E9 [' U' Z  ~6 w# ?" @转移7 K" @  K1 g+ j" o; R- z- O
6 {$ @- t; m* n! X) P9 ?
•访问闪存,SRAM,APB和AHB外设作为源和目标
; Z: Y' r: |& M) D, J/ d$ T/ `# o6 ~  `; G8 b: E1 h* Y4 c3 j, ?
•可编程的数据传输数量:最多655356 ^- a3 b3 S* [& `

9 F0 b: E# w- C' U4 Z% f& _DMA通道对应的外设情况(F0系列):
* {0 O- H; a9 S" `; T! q$ {" H3 E: O6 ~
aHR0cHM6Ly9tbWJpei5xcGljLmNuL3N6X21tYml6X3BuZy91Y2RZbUdYTGlhOThxM0tXM2pWY0ZMdU9m.png

. J, ]% I8 O# v% U0 f* U/ I' M8 l8 a. x4 v7 b) u
aHR0cHM6Ly9tbWJpei5xcGljLmNuL3N6X21tYml6X3BuZy91Y2RZbUdYTGlhOThxM0tXM2pWY0ZMdU9m.jpg

* |- b- C2 ~1 C6 c2 e- ?
0 f- t1 M: U! s% L2 M& R% H$ P" L% X) s7 p
* v  t* Z6 b  {0 {6 a% v
    很多博文会讲解里面的详细定义,对于每一处寄存器的详细操作指导,所以我就不会去多写了。
# v3 t8 K3 N: y8 d: H3 ~0 D) X# O+ x; b3 i) t' e
    我只想表达,DMA是一种可以快速相关外设数据交互的一种方法,我们学习哪里用它,怎么用它,至于细节学习,大家去网上所搜DMA相信息即可。    ( n  b9 Q+ O6 f1 O
/ k) S1 Q, E- Q2 u  ]& v0 Y
    之前讲过DMA的数据发送,现在补充上DMA的接收数据部分。' V0 K& a2 {) a

- x2 t% o3 U1 V6 G( P2 ~8 ]  利用DMA接收串口数据的配置,大致分为:1.初始化串口并开启DMAR接收功能,配置DMA的外设到内存的数据接收功能,2.等待串口中断提示,并进行处理数据,3.清空DMA,重新等待数据
& H0 l. q$ B, ^+ B
$ U2 \9 Q4 b! {) f5 }: o1 `01 配置串口与DMA, a6 W' A: m1 l. n2 g) A. C+ a* l; G
    其中USART2->CR3寄存器的第6 bit用来设置DMA的接收配置,此处比较重要我们设置为USART_CR3_DMAR。 USART2->CR1寄存器中开启帧信息接收完成之后的中断,USART_CR1_IDLEIE,这处可以帮助我们节省CPU的不必要开支,开启此处中断类型,我们只需要在每帧信息接收完成之后,usart才触发中断,我们再解析。其余为正常的usart配置。
  ^+ `3 u# ~, h
& O' y; z$ V1 h: x
aHR0cHM6Ly9tbWJpei5xcGljLmNuL3N6X21tYml6X3BuZy91Y2RZbUdYTGlhOThxM0tXM2pWY0ZMdU9m.png
- w) A" i- j, r: G1 v# Q& q( k
0 U7 ~; J1 @1 j5 G3 X4 J
  1. GPIOD->MODER |=   GPIO_MODER_MODER5_1|  //USART2_TX, U$ _9 e+ V7 N. y
  2.                   GPIO_MODER_MODER6_1;  //USART2_RX  
    - [3 m& x7 w3 {% b2 S; d: T
  3.   /* set baudrate */
    8 @* U8 c* f, D; t  m
  4.   USART2->BRR = USART_Baudrate;//115200+ M; I2 N* N0 T, z( I. J- S
  5.   USART2->CR3 |= USART_CR3_DMAT|USART_CR3_DMAR;//USART_CR3_OVRDIS;// 不需要覆写2 o5 _0 P7 D5 w
  6. & `" L4 X7 v- O& D$ A; w( z
  7.   /*enable usart2 and enable tx*/
    # f$ Z+ D# w& D% ^
  8.   USART2->CR1 |= USART_CR1_UE | USART_CR1_TE | USART_CR1_RE | USART_CR1_IDLEIE;
复制代码

; \; v1 v4 R( q配置DMA:
' x4 K4 d5 O$ A! `9 v# y: x  [: E+ w; k+ p
    首先根据上方的映射表,我们初始化了usart2,而usart2 RX对应的DMA通道为DMA1 channel5,所以我们进行DMA1 ch5通道配置。+ K) x* J: h$ m  v" h/ i4 y
  |5 l2 A# n- _
第一个为DMA1_Channel5->CPAR寄存器,在下表可知,用来设置对应接收数据的外设的地址,而USART的接收数据的寄存器为RDR,所以寄存器配置为DMA1_Channel5->CPAR = (uint32_t)&(USART2->RDR);
5 F3 J" f& X( A; q+ J3 X" h9 u
' I2 Q. ^) S1 @0 x( }; N, r* Z
aHR0cHM6Ly9tbWJpei5xcGljLmNuL3N6X21tYml6X3BuZy91Y2RZbUdYTGlhOThxM0tXM2pWY0ZMdU9m.jpg

1 f% W( x% v. o. P6 t7 X
% X$ U* o) I4 y$ q& W5 a
- A  U2 W; T" f6 b5 a  L! f1 M; \$ Z. O: K+ C# @  v/ H7 W" X% Q
    设置需要放数据的指定内存位置,根据指导手册可知CMAR为DMA通道用来放置数据的内存地址,所以此处设置为我们定义好的变量的地址。
% W: ]9 M2 b  u0 S8 W: q
  P; ^& ?. A* a' U& Y
aHR0cHM6Ly9tbWJpei5xcGljLmNuL3N6X21tYml6X3BuZy91Y2RZbUdYTGlhOThxM0tXM2pWY0ZMdU9m.jpg
' E! H6 p8 e/ g: a
1 R1 |0 {4 K# w3 [  e4 @4 R
    设置单次传送数据量的大小,此处最大可设置为65535byte的数据大小。; p: `* J* C$ H4 C/ W/ H

: {. J7 h, b2 {1 ?8 x7 d& E  x; k& b  q" z
aHR0cHM6Ly9tbWJpei5xcGljLmNuL3N6X21tYml6X3BuZy91Y2RZbUdYTGlhOThxM0tXM2pWY0ZMdU9m.jpg
/ e$ p& F, n: P3 i

2 A  _! ?! V6 ]最后开启DMA通道。
' \! G0 x! a8 j1 [% x3 D: d* @9 ^( c# D5 r& u, u9 \. i; y* z# n
aHR0cHM6Ly9tbWJpei5xcGljLmNuL3N6X21tYml6X3BuZy91Y2RZbUdYTGlhOThxM0tXM2pWY0ZMdU9m.jpg
6 z* P; m& \. c# x/ i

0 I0 w. b5 M% D  s; f
aHR0cHM6Ly9tbWJpei5xcGljLmNuL3N6X21tYml6X3BuZy91Y2RZbUdYTGlhOThxM0tXM2pWY0ZMdU9m.png

& P1 ]* A* F- t8 F* ?' m
aHR0cHM6Ly9tbWJpei5xcGljLmNuL3N6X21tYml6X3BuZy91Y2RZbUdYTGlhOThxM0tXM2pWY0ZMdU9m.jpg

. C' O3 I, f/ Z' k" V& q' \% U! Y) A  [
  1. void USART2_DMA_Recive(uint8_t *p_data,uint16_t length)4 s( |# ~8 U+ g2 f1 Z4 j" G' d
  2. {( A) y& \9 N+ Z+ |
  3.   DMA1_Channel5->CPAR = (uint32_t)&(USART2->RDR);          //Peripheral address
    9 p) E5 H4 V" S0 b9 r! g
  4.   DMA1_Channel5->CMAR = (uint32_t)p_data;                  //memory address! j/ a- G6 R! S  b2 x
  5.   DMA1_Channel5->CNDTR = length;                            //Set the length9 _  f9 ]6 ^- }% H0 d
  6.   DMA1_Channel5->CCR |= DMA_CCR_MINC | DMA_CCR_EN;" S: E& a* F1 J2 |1 I9 [2 V4 \" ?
  7. }+ I8 t$ ~% ^0 K4 X- x0 h3 Q' o; y
复制代码

$ j* T- }' }2 T6 P. h! S02  等待串口中断,处理数据. s7 C1 u2 b/ i1 Y

4 D9 a: b! j5 ]- U- O" l- Y
aHR0cHM6Ly9tbWJpei5xcGljLmNuL3N6X21tYml6X3BuZy91Y2RZbUdYTGlhOThxM0tXM2pWY0ZMdU9m.png
' M" x. p: K8 G. o  y0 P
$ @' B; W3 ~  t# ]2 x
    此时在debug中,在我们定义好的DMA内存变量,地址在RAM初始完成,后续串口数据接收的时候,DMA会直接将数据置于p_data所对应的内存。/ ~8 Y: r# Y; T# O0 h5 F

) P: W* [$ @/ Q# ~4 L4 v9 z* B    然后在中断服务函数我们可以将我们数据进行处理,Uart_Channel_isr[1](USART2->RDR);,这个函数为我自己写的处理函数,中断里面函数都是数据复制,所以我直接在中断执行,一般大家会在中断写个标志,在主循环进行解析。但是DMA接收配合USART_ISR_IDLE标志在STM32平台下并不友好,如果主循环用来解析数据的时候,空闲中断还没有产生,本帧数据还尚未接收完成,但是由于内存数据已经在实时写入,DMA1_Channel5->CNDTR已经有所变化,并早于空闲中断产生,所以主循环就不能用DMA1_Channel5->CNDTR所接收数据长度进行解析了。一般建议,如果解析数据只是单纯的挪移,此时候直接在中断处理即可,对主程序并没有大的阻塞。/ _5 d9 {/ w- Z
+ {4 H# i+ R2 T
  1. void USART2_IRQHandler(void)
    . P- T; Z: E0 Z, S5 C3 D4 C; t6 K* L' O
  2. {# P) p' x: M; t( t/ i
  3.   if ((USART2->ISR & USART_ISR_ORE) == USART_ISR_ORE) 3 i0 @) a9 J5 K* E& c0 y
  4.   {  
    " ?- ?3 h+ \8 X5 N9 X, ^8 n1 R0 S
  5.     USART2->ICR |= USART_ICR_ORECF;
    * ~7 Y4 L& @. u5 q  G- D9 I, v
  6.   }: }. N8 u+ Z9 w
  7. ' `; @( D! m- E9 y  x
  8.   if ((USART2->ISR & USART_ISR_IDLE) == USART_ISR_IDLE)  //The new frame data receive
    " |& z5 u% R* \6 _( B; W
  9.   {
    8 S0 C0 e" Q* ~. ?- M  q
  10.     USART2->ICR |= USART_ICR_IDLECF;   
    ( H% G8 i7 V% V4 C
  11.     Uart_Channel_isr[1](USART2->RDR);/*读取解析数据*// M7 ?$ u6 s5 q
  12.    }  4 T; e- i' K8 r; D
  13. }
复制代码

. E; H3 M& Z9 m* ~' f03 恢复DMA,清空CNDTR,等待下次数据到来
% ^: _! F+ z8 a    处理完数据之后,及时将DMA标志以及CNDTR清空,否则CNDTR一直不清空,会导致下次接收数据的时候,造成通道的占用,数据使用过之后,就清理掉CNDTR,这样保证每次接收数据的通道有足够的位置。1 h9 H: ~* ~- ?2 S( I+ ^
  1. static void Usart2_Dma_Reload(uint16_t length)/*清空数据*/- B7 M1 O8 `8 w0 k( i6 c7 H
  2. {) L7 G+ ^/ L6 @* ^  S
  3.   DMA1_Channel5->CCR &= ~(DMA_CCR_EN);             //disable the dma
    ( U4 G6 m+ K6 @9 a
  4.   DMA1_Channel5->CNDTR = length;          //Set the length7 ?6 [& a# ?* U7 U) A0 ^% }4 ]
  5.   DMA1_Channel5->CCR |= DMA_CCR_EN;                //enable the dma; E$ o2 }/ O+ t. S+ a$ e
  6. }. z5 |, C5 X+ Y7 D2 |# e% ~! p: k
复制代码

& R4 j; w4 p( S' X, F
; k8 C. F5 S- |4 \8 P8 e
收藏 1 评论0 发布时间:2021-11-24 13:30

举报

0个回答
关于
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
意法半导体官网
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
官方最新发布
STM32Cube扩展软件包
意法半导体边缘AI套件
ST - 理想汽车豪华SUV案例
ST意法半导体智能家居案例
STM32 ARM Cortex 32位微控制器
关注我们
st-img 微信公众号
st-img 手机版