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

(干货)串口DMA很难?那就一步步带你学会!

[复制链接]
li1229574727 发布时间:2020-5-15 00:15
串口DMA

& q9 m2 g% L$ h0 b

DMA利用好无疑会让串口使用起来更加高效,同时CPU还能处理自己的事情,但是DMA的使用却让代码变得更加的复杂,也使串口配置变得更加麻烦!麻烦???也许只是学习的方式不对,代码是变长了,但是思路清晰的话,也就是在原来串口加了点东西而已。本文让你轻轻松松学会串口DMA!定长数据传输与不定长数据传输都教会你!通过此文,希望能让更多人了解和会使用串口DMA,希望大家多多支持!

一、串口DMA的配置

本文针对串口2(USART2)如何进行DMA传输进行讲解,如果采用其他串口,注意要修改相应端口配置以及DMA通道。

1、串口的DMA请求映像

通过我之前的博文《 STM32 | DMA配置和使用如此简单(超详细)》可以知道,串口2(USART2)的接收(USART2_RX)和发送(USART2_TX)分别在DMA1控制器的通道6和通道7。下图为DMA1控制器的请求映像图。

8 }  Q4 m' x$ S; J0 _
. a5 y( M" g4 Z6 Y

5 F. P: I; V- s' f/ _

2、资源说明


0 X& ^+ _( |) A9 j6 q  l6 x  E8 ~( b( \

STM32F1中,USART2使用的是PA2(USART2_TX)和PA3(USART2_RX),为了方便阅读,使用到的资源列在下表。

外设        GPIO口        DMA请求映像通道        备注

USART2_TX        PA2        DMA1通道7        RAM->USART2的数据传输

USART2_RX        PA3        DMA1通道6        USART2->RAM的数据传输

3、DMA初始化配置

USART2的DMA配置在《 STM32 | DMA配置和使用如此简单(超详细)》中分别讲了库函数版和寄存器版两种配置,我这里直接搬用,不理解的可以看看《 STM32 | DMA配置和使用如此简单(超详细)》这篇文章。要注意的是库函数最大优势就是便于阅读,所以在DMA初始化配置中我们使用库函数。

首先,我们要先定义三个缓冲区(作全局定义),一个发送缓冲区,两个接收缓冲区,两个接收缓冲区是为了做双缓冲区,目的是为了防止后一次传输的数据覆盖前一次传输的数据,并且留出足够的时间让CPU处理缓冲区数据。双缓冲在串口DMA中有着很重要的意义并起着很大的作用!

  1. //USART2_MAX_TX_LEN和USART2_MAX_RX_LEN在头文件进行了宏定义,分别指USART2最大发送长度和最大接收长度: O: V; K8 g/ V( H; ]0 A' `$ C
  2. u8 USART2_TX_BUF[USART2_MAX_TX_LEN];         //发送缓冲,最大USART2_MAX_TX_LEN字节/ `2 b; l; _5 g
  3. u8 u1rxbuf[USART2_MAX_RX_LEN];                      //发送数据缓冲区17 z( \2 g0 K1 M% ]7 ?. a1 Z
  4. u8 u2rxbuf[USART2_MAX_RX_LEN];                      //发送数据缓冲区26 x& w: v7 c4 F$ `# V4 ?' J. J
  5. u8 witchbuf=0;                                                    //标记当前使用的是哪个缓冲区,0:使用u1rxbuf;1:使用u2rxbuf
    % m  A" }7 Z8 c, ^
  6. u8 USART2_TX_FLAG=0;                                      //USART2发送标志,启动发送时置1+ t  _3 F, X( Y4 c
  7. u8 USART2_RX_FLAG=0;                                      //USART2接收标志,启动接收时置1/ H+ _' d  t0 B( l( T  V
复制代码

要说明的是,实际上发送缓冲区可能用不上,因为我们要发送的数据内容和大小都不一定每次都是固定的,可以是变化的,我们只需重新指派新的发送缓冲区地址即可,但是在初始化中我们还是要指定一个发送缓冲区地址。
2 A, Z: @& y2 R- f( A& N下面是DMA1_USART2的初始化函数。

  1. void DMA1_USART2_Init(void)
    ) [- q$ @' h& W/ Z8 a
  2. {; d, `# n) W! I( s4 Y+ F3 Z2 b7 r
  3.         DMA_InitTypeDef DMA1_Init;
    8 P4 L" z4 S- F
  4.         NVIC_InitTypeDef NVIC_InitStructure;  z8 e7 e! |% B2 T2 ?5 _4 r! Z' r
  5.         % x& [' a# D* w+ @" E4 s8 ?
  6.         RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1,ENABLE);                                                //使能DMA1时钟
    ! b% j) g# l% L& j/ w3 r% e5 b

  7. : R/ \+ D2 `4 @8 q- E& N4 e) D/ p- z
  8.         //DMA_USART2_RX  USART2->RAM的数据传输5 \4 J; S7 r0 E" T; q- [
  9.         DMA_DeInit(DMA1_Channel6);                                                                                                //将DMA的通道6寄存器重设为缺省值 ( m# Q) }8 k, r; R. m9 m
  10.         DMA1_Init.DMA_PeripheralBaseAddr = (u32)(&USART2->DR);                                        //启动传输前装入实际RAM地址$ X$ C, ]5 f9 O2 }, _6 R# g
  11.         DMA1_Init.DMA_MemoryBaseAddr = (u32)u1rxbuf;                                            //设置接收缓冲区首地址
    4 q  L$ s0 I# ^& [0 t. e
  12.         DMA1_Init.DMA_DIR = DMA_DIR_PeripheralSRC;                                                                //数据传输方向,从外设读取到内存
    8 [$ e5 c, K! j/ t! C
  13.         DMA1_Init.DMA_BufferSize = USART2_MAX_RX_LEN;                                                        //DMA通道的DMA缓存的大小
    1 B; v+ F6 j4 M" I9 K" B
  14.         DMA1_Init.DMA_PeripheralInc = DMA_PeripheralInc_Disable;                                //外设地址寄存器不变
    1 w) R/ r( E8 m  N% \: c" Q4 p
  15.         DMA1_Init.DMA_MemoryInc = DMA_MemoryInc_Enable;                                                        //内存地址寄存器递增4 M4 ^* R; T8 w, d% u, Y/ w! F
  16.         DMA1_Init.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;                        //数据宽度为8位
    " ^) Y0 Z0 l) u; d0 y; X
  17.         DMA1_Init.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;                                        //数据宽度为8位
    9 N5 Y3 W; K0 R$ L! A+ Z
  18.         DMA1_Init.DMA_Mode = DMA_Mode_Normal;                                                                        //工作在正常模式
    ! Z! K$ I! h4 h8 q( ]
  19.         DMA1_Init.DMA_Priority = DMA_Priority_High;                                                         //DMA通道 x拥有高优先级 / i( j6 a2 s1 X: a5 q9 \
  20.         DMA1_Init.DMA_M2M = DMA_M2M_Disable;                                                                        //DMA通道x没有设置为内存到内存传输
    ! t" ~  |: f& M
  21.          
    " C7 P, @: m+ E* {4 t7 U
  22.         DMA_Init(DMA1_Channel6,&DMA1_Init);                                                                         //对DMA通道6进行初始化
    8 i( q: Q& h& U" h- @7 O8 t
  23.         
    ( f" j6 R! ~( r; m1 b4 C
  24.         //DMA_USART2_TX  RAM->USART2的数据传输1 s  a# w) Y6 C: L1 P% A, `( p
  25.         DMA_DeInit(DMA1_Channel7);                                                                                                //将DMA的通道7寄存器重设为缺省值 0 f' N2 [2 r$ a! |. V+ C0 n9 C
  26.         DMA1_Init.DMA_PeripheralBaseAddr = (u32)(&USART2->DR);                                        //启动传输前装入实际RAM地址
    " w$ S/ T* e: h/ X5 O
  27.         DMA1_Init.DMA_MemoryBaseAddr = (u32)USART2_TX_BUF;                              //设置发送缓冲区首地址3 V4 p$ Z; a# X& m/ \$ h) o
  28.         DMA1_Init.DMA_DIR = DMA_DIR_PeripheralDST;                                                                 //数据传输方向,从内存发送到外设
    8 i) U" Y' [* E' l
  29.         DMA1_Init.DMA_BufferSize = USART2_MAX_TX_LEN;                                                        //DMA通道的DMA缓存的大小
    ; W/ O$ N$ j) b: f" [
  30.         DMA1_Init.DMA_PeripheralInc = DMA_PeripheralInc_Disable;                                //外设地址寄存器不变
    1 R( G1 r, C9 k! o
  31.         DMA1_Init.DMA_MemoryInc = DMA_MemoryInc_Enable;                                                        //内存地址寄存器递增; D9 W+ e1 n: x
  32.         DMA1_Init.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;                        //数据宽度为8位1 U, E; u8 X  X' P% l
  33.         DMA1_Init.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;                                        //数据宽度为8位
    " T4 y8 u0 [' C" c
  34.         DMA1_Init.DMA_Mode = DMA_Mode_Normal;                                                                        //工作在正常模式
    , q" S1 E4 z! g4 Z+ ?) P
  35.         DMA1_Init.DMA_Priority = DMA_Priority_High;                                                         //DMA通道 x拥有高优先级 6 @! E4 X7 m& Z9 h+ w- a4 a
  36.         DMA1_Init.DMA_M2M = DMA_M2M_Disable;                                                                        //DMA通道x没有设置为内存到内存传输
    * E3 ~. V3 L6 v! C: I

  37. - F9 `9 w$ p4 J- Y: B0 `" t
  38.         DMA_Init(DMA1_Channel7,&DMA1_Init);                                                                         //对DMA通道7进行初始化
    ! C0 w9 `1 p- Y: M0 h
  39.         
    6 f2 F1 ^" o0 ]3 ]( i8 A1 W. d
  40.         //DMA1通道6 NVIC 配置; C  e8 M* |3 w# f4 m
  41.         NVIC_InitStructure.NVIC_IRQChannel = DMA1_Channel6_IRQn;                                //NVIC通道设置: i( y# V. p! C% F" ?
  42.         NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 3 ;                                //抢占优先级7 C" |4 N  J: R! U: k' I* d
  43.         NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;                                                //子优先级
    ! U7 {4 }7 m6 }- t. i4 _2 R5 w: R# ?4 K
  44.         NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;                                                        //IRQ通道使能
    # L* e4 c) C6 E9 ?4 b) A( g( D6 Q( g. @
  45.         NVIC_Init(&NVIC_InitStructure);                                                                                        //根据指定的参数初始化NVIC寄存器/ E+ C' }) F1 z

  46. 8 i& C6 N5 T$ M
  47.         //DMA1通道7 NVIC 配置; M6 l: v# T$ Q7 f9 _* y2 F
  48.         NVIC_InitStructure.NVIC_IRQChannel = DMA1_Channel7_IRQn;                                //NVIC通道设置/ b: M. @: F2 g2 F8 ~+ D2 Q& }$ O' w  ~
  49.         NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 3 ;                                //抢占优先级2 @/ `1 v) C: O
  50.         NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;                                                //子优先级
    5 M9 H: }- o. r1 X. x! v
  51.         NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;                                                       //IRQ通道使能
    ) d6 ^, L# Y& \# _) M$ Y
  52.         NVIC_Init(&NVIC_InitStructure);                                                                                        //根据指定的参数初始化NVIC寄存器
    . {6 V" }3 i) C
  53. ( E6 S4 T3 e( [) M- f) v7 ?' q
  54.         DMA_ITConfig(DMA1_Channel6,DMA_IT_TC,ENABLE);                                                        //开USART2 Rx DMA中断' c: ^' u. M# ~
  55.         DMA_ITConfig(DMA1_Channel7,DMA_IT_TC,ENABLE);                                                        //开USART2 Tx DMA中断
    3 R" B- A: F0 J

  56. ! {* |5 N% j7 H( s
  57.         DMA_Cmd(DMA1_Channel6,ENABLE);                                                                           //使DMA通道6停止工作
    7 M3 S' T6 \% [
  58.         DMA_Cmd(DMA1_Channel7,DISABLE);                                                                           //使DMA通道7停止工作
    1 G/ R: N0 C# _7 H# j- ?; E
  59.          
    7 r  W/ P# _- p) F" r# ^
  60.         USART_DMACmd(USART2, USART_DMAReq_Tx, ENABLE);                                                //开启串口DMA发送
    - `0 V3 E* R9 u* }
  61.         USART_DMACmd(USART2, USART_DMAReq_Rx, ENABLE);                                                //开启串口DMA接收, G& q% h) F/ W) D, w/ z2 X) T/ [
  62. }
    7 |/ l5 D1 Q+ _/ b" t, ?
复制代码

相应配置的讲解在《 STM32 | DMA配置和使用如此简单(超详细)》中讲解过,这里不再叙述,要注意的是,我们采用中断方式进行DMA传输。正常情况下,我们传输完数据要进行相应的处理,那我们怎么知道什么时候数据传输完成了呢?这时候就借助DMA传输完成中断。既然打开了中断,那一定要注意中断优先级,这里特别指出串口的中断优先级应低于串口DMA通道的中断优先级。

4、串口配置

前面已经完成了DMA的配置,而DMA是不能单独使用的,所以不要忘了配置要用到的串口USART2。

  1. void Initial_UART2(unsigned long baudrate)
    & i! \* ~- ?( I; B7 m8 p$ a$ H
  2. {
    7 x9 c: S- C$ s5 e6 [* F
  3.         //GPIO端口设置9 Y  R5 P- c6 E: _; \
  4.         GPIO_InitTypeDef GPIO_InitStructure;
    ; e+ v* R+ f3 l6 O) R- R2 d# `# U0 A
  5.         USART_InitTypeDef USART_InitStructure;7 q- @% d' H/ ]6 h/ h
  6.         NVIC_InitTypeDef NVIC_InitStructure; * l2 D- N/ `, ~/ j% N1 v3 ~
  7.        
    % x/ Q% }. n2 s0 e
  8.         RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2 | RCC_APB2Periph_GPIOA, ENABLE);        //使能USART2,GPIOA时钟
    6 v; p1 }$ e1 o( u5 N  q6 k5 f! B
  9.         + u- q  j2 s  l6 f
  10.         //USART2_TX   GPIOA.2初始化
    3 X: d* Y+ d: _7 T4 E
  11.         GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2;                                                                                //PA.2
    8 z; b0 T$ K( J/ w. E: l4 G, Y
  12.         GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;                                                                        //复用推挽输出: K& ?/ Y8 k: r" e
  13.         GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;                                                                //GPIO速率50MHz- o  r. Y- |9 C! a3 Y) [: g" ]. D& v
  14.         GPIO_Init(GPIOA, &GPIO_InitStructure);                                                                                        //初始化GPIOA.2, j9 H6 G, G& c( F! d. l2 _# B
  15.        
    5 }* B: k* l( q4 i/ Y8 O& h
  16.         //USART2_RX          GPIOA.3初始化( `+ ?" J4 d: F+ [
  17.         GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3;                                                                                //PA.3
    0 K2 k  @7 c6 s1 e% Q
  18.         GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;                                                        //浮空输入! Y* o4 G/ q. ^; A  E# Y
  19.         GPIO_Init(GPIOA, &GPIO_InitStructure);                                                                                        //初始化GPIOA.3
    % _! A# v" w4 U4 a  `! P! n  @  b
  20.          
    + i9 u- J# o- p7 f
  21.         //USART 初始化设置$ ?' w& h+ Y2 \( E' Y
  22.         USART_InitStructure.USART_BaudRate = baudrate;                                                                        //串口波特率
    1 ^% k7 k. Z9 _( k# q* Y( t( J
  23.         USART_InitStructure.USART_WordLength = USART_WordLength_8b;                                                //字长为8位数据格式
    3 f# _5 J- ]- X+ J0 _- y+ q
  24.         USART_InitStructure.USART_StopBits = USART_StopBits_1;                                                        //一个停止位
    2 j. t% n9 G2 W
  25.         USART_InitStructure.USART_Parity = USART_Parity_No ;                                                        //无奇偶校验位* @( G) O3 w& ^& @; p" z
  26.         USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;        //无硬件数据流控制
    ; A3 \) E. Z. q" i2 }. `2 j
  27.         USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;                                        //收发模式4 i1 K3 |8 W4 f1 M9 l/ @# M5 O
  28.         USART_Init(USART2, &USART_InitStructure);                                                                                 //初始化串口2
    5 S- u/ j3 Y) [. e' e9 u) b
  29.        
    * u% ?4 t( f7 g3 r1 U! U' j# q4 |
  30.         //中断开启设置
    7 e+ C$ B/ q4 w# k2 w
  31.         USART_ITConfig(USART2, USART_IT_IDLE, ENABLE);                                                                        //开启检测串口空闲状态中断( F; }# O; }3 r) w: p0 x. M) R9 }7 S
  32.         USART_ClearFlag(USART2,USART_FLAG_TC);                                                                                        //清除USART2标志位
    9 ~; T' v& F% q' Z9 ~
  33.        
    & j8 p' S; v/ P3 v9 M7 v/ ?% p
  34.         USART_Cmd(USART2, ENABLE);                                                                                                                //使能串口2  K0 N9 k* e) ^6 `) x3 e. o
  35.         ; T. Z  |" b6 P  ]8 |% A
  36.         NVIC_InitStructure.NVIC_IRQChannel = USART2_IRQn;                                                                //NVIC通道设置
    % [' ~7 g' @$ p4 @2 C* z3 P
  37.         NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 8;                                                //抢占优先级
    0 u7 k- |) H$ O% j' E& s
  38.         NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;                                                                //响应优先级
    4 @& X7 i( h! o# U- j
  39.         NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;                                                                        //IRQ通道使能& F8 ?; y; m) a( w4 l+ l+ }8 Q5 O
  40.         NVIC_Init(&NVIC_InitStructure);                                                                                                        //根据指定的参数初始化NVIC寄存器
    4 `" E- U  c4 ]( e' a
  41.         / {& s4 [  p  ^3 r
  42.         DMA1_USART2_Init();                                                                                                                                //DMA1_USART2初始化2 @) h  Y# n& P9 [0 b
  43. }
    , V$ X0 v* l; `+ J0 Q

  44. & E  X# A2 e. Y  g! O
复制代码

可以注意到,我这里定义的串口中断抢占优先级低于DMA中断的抢占优先级。细心点可以看到,我开启了串口空闲中断。为什么呢?因为通常情况下我们是不知道接收数据的长度的,这样我们是没有办法利用DMA传输完成标志位来判断是否完成接收,所以我们这里采用串口空闲中断来判断数据是否接收完成,接收完成了会进入串口空闲中断。串口配置的最后进行了串口2(USART2)DMA的初始化,这样直接初始化串口也直接包括了串口DMA的初始化,主函数初始化只需一步即可。

二、串口DMA的使用

。。。。。。论坛看起来太不舒服了,有的格式没办法设置,如果喜欢还是去CSDN博客看吧!
; j3 ]+ y, q  R& Q

原文链接:http://blog.csdn.net/weixin_44524484/article/details/106029682

  g1 G& ~8 J7 `  b; c9 Y. e7 i0 s

以下为原文目录,一步一步教会你串口DMA!


. W" {6 b; _, v6 Q/ \
- h' j: M# v3 i* A
收藏 3 评论2 发布时间:2020-5-15 00:15

举报

2个回答
li1229574727 回答时间:2020-5-15 00:21:46
受不了,代码怎么整都是不整齐的!
李康1202 回答时间:2020-5-15 08:37:39
都是干货

所属标签

相似分享

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