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

一个严谨的STM32串口DMA发送&接收(1.5Mbps波特率)机制

[复制链接]
STMCU-管管 发布时间:2020-9-15 15:49
1 前言" U  `" l6 Z, H
% d) C7 m. i# }. C

  直接存储器访问(Direct Memory Access),简称DMA。DMA是CPU一个用于数据从一个地址空间到另一地址空间“搬运”(拷贝)的组件,数据拷贝过程不需CPU干预,数据拷贝结束则通知CPU处理。因此,大量数据拷贝时,使用DMA可以释放CPU资源。DMA数据拷贝过程,典型的有:

  • 内存—>内存,内存间拷贝
  • 外设—>内存,如uart、spi、i2c等总线接收数据过程
  • 内存—>外设,如uart、spi、i2c等总线发送数据过程* @- ]& X, b' C- P6 V* P5 S7 z

    " Z; w) P3 v0 a* i
5 }, @9 o# v' d
2 串口有必要使用DMA吗
: ~: k3 a7 G( w6 ]( {0 n

8 q4 p! p6 B4 i
0 L5 r  @2 m- H6 x- Q  O5 L  R' @

  串口(uart)是一种低速的串行异步通信,适用于低速通信场景,通常使用的波特率小于或等于115200bps。对于小于或者等于115200bps波特率的,而且数据量不大的通信场景,一般没必要使用DMA,或者说使用DMA并未能充分发挥出DMA的作用。


; q$ H# l2 n9 w0 D9 u/ |' l: r

  对于数量大,或者波特率提高时,必须使用DMA以释放CPU资源,因为高波特率可能带来这样的问题:

  • 对于发送,使用循环发送,可能阻塞线程,需要消耗大量CPU资源“搬运”数据,浪费CPU
  • 对于发送,使用中断发送,不会阻塞线程,但需浪费大量中断资源,CPU频繁响应中断;以115200bps波特率,1s传输11520字节,大约69us需响应一次中断,如波特率再提高,将消耗更多CPU资源
  • 对于接收,如仍采用传统的中断模式接收,同样会因为频繁中断导致消耗大量CPU资源
    3 e( _0 K( I1 @( Q6 _8 K! @+ r. U
    / Z8 ?  A8 g- g9 E! n; B! \8 O1 n

( A0 g4 f0 |1 t- z0 x

  因此,高波特率场景下,串口非常有必要使用DMA。

3 q: b1 i: w: X
3 实现方式
% O& M5 W, l$ K
8 M$ Q0 v$ W7 O9 i5 G% E7 @$ u

1 (2).png


4 H$ s. j: }1 a2 i" p3 H0 }" v% @1 G! b! f& b! U6 {7 p3 [0 s& v# m5 W/ }
4 STM32串口使用DMA
. ^3 _- R' L3 p' _  \8 O

8 T1 \% O) Z8 z5 ^* u( `+ e9 Y( x8 `+ N5 [: S* q  g4 p

  关于STM32串口使用DMA,不乏一些开发板例程及网络上一些博主的使用教程。使用步骤、流程、配置基本大同小异,正确性也没什么毛病,但都是一些基本的Demo例子,作为学习过程没问题;实际项目使用缺乏严谨性,数据量大时可能导致数据异常。


- N  \, M+ N9 z1 o

测试平台:

  • STM32F030C8T6
  • UART1/UART2
  • DMA1 Channel2—Channel5
  • ST标准库
  • 主频48MHz(外部12MHz晶振)3 J8 U$ Y% T) U9 \* s

; W- c8 g2 A( M$ ?- B/ ], g) l6 s% X

9 e$ v0 W6 M8 G3 E  F  y1 ]2 Z
2.png
. j2 m/ E' d. b' S" |0 f


* ^, a2 [4 k, [; y2 {

5 串口DMA接收# F9 e* J3 |9 j+ ~
3 s& u1 A( v' p9 M2 H
5.1 基本流程( J4 Q/ L# a* A: ~' V

3.png


5 ?# \: ~- v6 |% h. S; j4 @3 ]( Y
串口接收流程图

$ O3 k# {) K- [& C; c2 Z+ g
5 d3 |0 y9 e8 l2 V$ j) I5.2 相关配置
# G9 b* A+ i: G( q6 a0 \

/ \4 G6 ~. G: P: ?

关键步骤

【1】初始化串口

【2】使能串口DMA接收模式,使能串口空闲中断

【3】配置DMA参数,使能DMA通道buf半满(传输一半数据)中断、buf溢满(传输数据完成)中断


) h. C: Z( _# t, v( I

为什么需要使用DMA 通道buf半满中断?

# T$ L  s) z; u5 |

  很多串口DMA模式接收的教程、例子,基本是使用了“空间中断”+“DMA传输完成中断”来接收数据。实质上这是存在风险的,当DMA传输数据完成,CPU介入开始拷贝DMA通道buf数据,如果此时串口继续有数据进来,DMA继续搬运数据到buf,就有可能将数据覆盖,因为DMA数据搬运是不受CPU控制的,即使你关闭了CPU中断。

& I: G  y! B: t- t' q! M

  严谨的做法需要做双buf,CPU和DMA各自一块内存交替访问,即是"乒乓缓存” ,处理流程步骤应该是这样:

+ f& \" e2 s, F5 F: W: b5 s4 i

【1】第一步,DMA先将数据搬运到buf1,搬运完成通知CPU来拷贝buf1数据
. D+ t* ?/ y+ J5 [# W【2】第二步,DMA将数据搬运到buf2,与CPU拷贝buf1数据不会冲突
* i" t& Y3 P/ L2 u; {4 Y【3】第三步,buf2数据搬运完成,通知CPU来拷贝buf2数据
  k7 v- C' ^5 d$ b# |" \3 b【4】执行完第三步,DMA返回执行第一步,一直循环


0 m! y$ t% j) n2 r6 ^6 Z


; k  G+ O$ e0 N, c. h8 _

4.png


/ w' ~; K: g0 d
# E" Y9 X' ^3 q% Q; j' c5 y双缓存DMA数据搬运过程
6 w5 ~3 @( J5 n# ^6 y# A& O) Y, g/ J
# h. d& [% Z9 l. k0 K8 \* }

  STM32F0系列DMA不支持双缓存(以具体型号为准)机制,但提供了一个buf"半满中断",即是数据搬运到buf大小的一半时,可以产生一个中断信号。基于这个机制,我们可以实现双缓存功能,只需将buf空间开辟大一点即可。

9 I3 Y: C% q( i8 J9 R6 l) W

【1】第一步,DMA将数据搬运完成buf的前一半时,产生“半满中断”,CPU来拷贝buf前半部分数据
! }1 i4 c! W: s) K! f0 L4 k5 n【2】第二步,DMA继续将数据搬运到buf的后半部分,与CPU拷贝buf前半部数据不会冲突
, \  X9 O, l5 n; b7 k7 }& @9 s; Y# a【3】第三步,buf后半部分数据搬运完成,触发“溢满中断”,CPU来拷贝buf后半部分数据
1 e* G2 w4 G" `; i$ s【4】执行完第三步,DMA返回执行第一步,一直循环


" n4 O/ E9 y: T6 x# T
6 I) q6 L7 ]. ~0 C- D  B+ v5 P& B) [

9 T8 Y& c% @. ^, I! a/ v7 b" w

5.png

* {" Z/ F5 o$ E0 ]5 q1 I

* A0 t- M& \9 f% `6 G! \
使用半满中断DMA数据搬运过程
# _8 W4 q; q4 r, \1 Z
; _: B, ^' K8 Z

  UART2 DMA模式接收配置代码如下,与其他外设使用DMA的配置基本一致,留意关键配置:

  • 串口接收,DMA通道工作模式设为连续模式
  • 使能DMA通道接收buf半满中断、溢满(传输完成)中断
  • 启动DMA通道前清空相关状态标识,防止首次传输错乱数据
    1 P0 m3 D4 n1 N6 {* _

; H9 z8 h! \2 l& |' z: B

9 ?/ V# e, t& p0 Q2 `
5 G3 ?' o& X9 M; Y6 m
! ~9 K* T7 [7 M6 t8 Y: t- o( P
  1. void bsp_uart2_dmarx_config(uint8_t *mem_addr, uint32_t mem_size)
    2 U0 Z+ O4 f: u( n; }9 U9 L2 T
  2. {  h) x$ Q8 P3 x' T0 i, k
  3.           DMA_InitTypeDef DMA_InitStructure;5 g9 q( n+ [' f5 @2 p* [5 J, [
  4.         
    & Z! R/ |0 q3 `- {2 F6 _0 Q
  5.         DMA_DeInit(DMA1_Channel5); ) q' C/ W8 K; W% |! f; s3 K" l
  6.         DMA_Cmd(DMA1_Channel5, DISABLE);
    ( q5 b" o$ v5 R- j2 o: }
  7.         DMA_InitStructure.DMA_PeripheralBaseAddr         = (uint32_t)&(USART2->RDR);/* UART2接收数据地址 */% f% ]& F5 P$ G" O
  8.         DMA_InitStructure.DMA_MemoryBaseAddr                 = (uint32_t)mem_addr; /* 接收buf */
    4 P2 y+ _* T2 T) J6 A
  9.         DMA_InitStructure.DMA_DIR                                         = DMA_DIR_PeripheralSRC;         /* 传输方向:外设->内存 */  h0 X& p" T1 R) C# i, j
  10.         DMA_InitStructure.DMA_BufferSize                         = mem_size; /* 接收buf大小 */2 [7 J* A+ Z% w1 L' e
  11.         DMA_InitStructure.DMA_PeripheralInc                 = DMA_PeripheralInc_Disable;
    : a5 S. |8 w% O5 c5 c
  12.         DMA_InitStructure.DMA_MemoryInc                         = DMA_MemoryInc_Enable; % x& L4 l/ ^4 |8 C+ S
  13.         DMA_InitStructure.DMA_PeripheralDataSize         = DMA_PeripheralDataSize_Byte; 4 T) h7 w/ a  L, i
  14.         DMA_InitStructure.DMA_MemoryDataSize                 = DMA_MemoryDataSize_Byte;# r% m* ?# V4 i, ?. M0 y. ^
  15.         DMA_InitStructure.DMA_Mode                                         = DMA_Mode_Circular; /* 连续模式 */
    2 X0 T* F1 B9 m% X: ~- P4 I) W; j
  16.         DMA_InitStructure.DMA_Priority                                 = DMA_Priority_VeryHigh; ( D" ^  o5 A1 V4 k- U" b& `
  17.         DMA_InitStructure.DMA_M2M                                         = DMA_M2M_Disable; / a' J; B+ p. A/ F- G0 I' ^6 `( j
  18.         DMA_Init(DMA1_Channel5, &DMA_InitStructure); " m4 u4 Z* \# k8 |
  19.         DMA_ITConfig(DMA1_Channel5, DMA_IT_TC|DMA_IT_HT|DMA_IT_TE, ENABLE);/* 使能DMA半满、溢满、错误中断 */
    ( ?' q  h5 ?% B% w" P' m7 h
  20.         DMA_ClearFlag(DMA1_IT_TC5);        /* 清除相关状态标识 */# a! [' s/ }" A9 C5 l
  21.         DMA_ClearFlag(DMA1_IT_HT5);
    * `+ s/ @3 F+ ]  }- P2 x# u; B
  22.         DMA_Cmd(DMA1_Channel5, ENABLE);
    , z6 [2 T. Q; ~' @  ~
  23. }
复制代码
# H! K+ T2 h/ Q# R0 v* `
+ |7 @/ h, V; j, e

DMA 错误中断“DMA_IT_TE”,一般用于前期调试使用,用于检查DMA出现错误的次数,发布软件可以不使能该中断。


& Z, P4 I# C0 L5.3 接收处理

2 Q0 |! J8 T4 D" s8 b

  基于上述描述机制,DMA方式接收串口数据,有三种中断场景需要CPU去将buf数据拷贝到fifo中,分别是:

  • DMA通道buf溢满(传输完成)场景
  • DMA通道buf半满场景
  • 串口空闲中断场景
    4 ]5 e. E4 i' R* R; U
    - U( ]* H- g, D* v1 n

  前两者场景,前面文章已经描述。串口空闲中断指的是,数据传输完成后,串口监测到一段时间内没有数据进来,则触发产生的中断信号。


1 i- G  _0 C1 c$ |2 U4 R 5.3 .1 接收数据大小

( M. |4 V5 B: I

  数据传输过程是随机的,数据大小也是不定的,存在几类情况:

  • 数据刚好是DMA接收buf的整数倍,这是理想的状态
  • 数据量小于DMA接收buf或者小于接收buf的一半,此时会触发串口空闲中断
    0 S: m0 s; V( `1 l: m" a6 j
    $ A- {, t8 U6 S& u( I, H- Y  D/ z

   因此,我们需根据“DMA通道buf大小”、“DMA通道buf剩余空间大小”、“上一次接收的总数据大小”来计算当前接收的数据大小。

  1. /* 获取DMA通道接收buf剩余空间大小 */
    * O! V+ @6 B$ t: }
  2. uint16_t DMA_GetCurrDataCounter(DMA_Channel_TypeDef* DMAy_Channelx);
复制代码

1 {0 o* H) g9 t3 m7 Y

DMA通道buf溢满场景计算

! b: \' }" s9 y5 u

  1. 接收数据大小 = DMA通道buf大小 - 上一次接收的总数据大小
复制代码

, ~% J/ x3 c3 h( N/ o) j/ r' v9 m" F, g5 l$ R( Q

DMA通道buf溢满中断处理函数:

  1. void uart_dmarx_done_isr(uint8_t uart_id)3 \5 U( Q" ~0 m: b: x  D9 Y
  2. {
    / q0 J& I5 `% I9 l
  3.           uint16_t recv_size;
    4 _9 i9 ~# n6 U  `/ Z: v, v6 c, T
  4.         & n1 j9 t$ r! F8 {5 M% f4 B
  5.         recv_size = s_uart_dev[uart_id].dmarx_buf_size - s_uart_dev[uart_id].last_dmarx_size;
    4 N# `  }' x4 D* w$ O. f( Q, a
  6. + V& {4 I: w; W, d
  7.         fifo_write(&s_uart_dev[uart_id].rx_fifo,
    $ R$ Q/ K6 t  j& Z
  8.                                    (const uint8_t *)&(s_uart_dev[uart_id].dmarx_buf[s_uart_dev[uart_id].last_dmarx_size]), recv_size);8 j! o, ]+ n, @( a7 v

  9. - W( j' R5 ^# I$ E  G$ R# ~
  10.         s_uart_dev[uart_id].last_dmarx_size = 0;
    - f! F0 L0 Q* z, e9 R6 r  \7 y! }7 m
  11. }
复制代码

2 u' D$ u1 O" O( O7 q7 s) G# y

DMA通道buf半满场景计算

9 G7 v* [% L) R! _0 S: g: D

  1. 接收数据大小 = DMA通道接收总数据大小 - 上一次接收的总数据大小- m5 Q5 |6 q4 S. m+ i
  2. DMA通道接收总数据大小 = DMA通道buf大小 - DMA通道buf剩余空间大小
复制代码

: {0 d$ v3 P- p. B% q* ?7 N6 e) @

DMA通道buf半满中断处理函数:


( h5 ~8 v% |" X: m9 N/ u2 e

  1. void uart_dmarx_half_done_isr(uint8_t uart_id)
    2 V( H" _1 K. M: i
  2. {; O5 ]1 m. y  g0 x; B( a
  3.           uint16_t recv_total_size;  L  U$ W  |; I5 U
  4.           uint16_t recv_size;
      D7 {' E5 a1 `
  5.         4 d! P4 Z! {1 \; x& N. Q' V
  6.         if(uart_id == 0)' \0 }0 q8 S6 V
  7.         {9 t3 I1 M7 c6 ?
  8.                   recv_total_size = s_uart_dev[uart_id].dmarx_buf_size - bsp_uart1_get_dmarx_buf_remain_size();4 a: i0 \5 v$ X- u
  9.         }
    - g  D! e( U/ `
  10.         else if (uart_id == 1)" L$ M9 g. U3 R' i8 M3 ?
  11.         {
    6 ^( I# s* w2 }
  12.                 recv_total_size = s_uart_dev[uart_id].dmarx_buf_size - bsp_uart2_get_dmarx_buf_remain_size();$ p3 V, o, o! L% Z: y2 e
  13.         }7 z& B# R7 @: Z7 ?- I  Z
  14.         recv_size = recv_total_size - s_uart_dev[uart_id].last_dmarx_size;
    , U' ^$ K4 k7 ]- ]/ Y4 Z
  15.        
    * Q; G) p/ g# l
  16.         fifo_write(&s_uart_dev[uart_id].rx_fifo, + `1 o  m$ I! V3 y$ n, E
  17.                                    (const uint8_t *)&(s_uart_dev[uart_id].dmarx_buf[s_uart_dev[uart_id].last_dmarx_size]), recv_size);
    , X9 [0 f& b1 B" }
  18.         s_uart_dev[uart_id].last_dmarx_size = recv_total_size;/* 记录接收总数据大小 */
    2 W( N# C! k& h7 S' h% I8 X
  19. }
复制代码
& |% T' A" ~; f! h& q
4 I  _/ N6 b' B, R: t7 f

串口空闲中断场景计算


1 t; {7 E+ r1 L7 W

  串口空闲中断场景的接收数据计算与“DMA通道buf半满场景”计算方式是一样的。

串口空闲中断处理函数:

注:
4 d" |" Z7 S( E& O2 T" b/ ^串口空闲中断处理函数,除了将数据拷贝到串口接收fifo中,还可以增加特殊处理,如作为串口数据传输完成标识、不定长度数据处理等等。

9 \8 [; s1 c4 G
5.3.2 接收数据偏移地址

4 W0 }3 k3 E2 \! @: O" @# q. E  v

  将有效数据拷贝到fifo中,除了需知道有效数据大小外,还需知道数据存储于DMA 接收buf的偏移地址。有效数据偏移地址只需记录上一次接收的总大小即,可,在DMA通道buf全满中断处理函数将该值清零,因为下一次数据将从buf的开头存储。

' \% [( l4 z! b! y; E

在DMA通道buf溢满中断处理函数中将数据偏移地址清零:

. N, ?5 `& T3 g! z& g8 W

  1. void uart_dmarx_done_isr(uint8_t uart_id)6 u2 A# s$ N/ o1 s  E
  2. {
    ( u4 j" z) S' g: G) m
  3.         /* todo */
    1 Z' x0 V) V$ x3 `5 g
  4.         s_uart_dev[uart_id].last_dmarx_size = 0;
    2 o8 f6 [4 @9 c% H. n7 b) n& j* v
  5. }
复制代码

: G! U4 V* @  S; [8 d* y% Q- K4 T
: _/ o* f# z4 L2 N( `5.4 应用读取串口数据方法
) c" z( R6 ~# _8 T

  经过前面的处理步骤,已将串口数据拷贝至接收fifo,应用程序任务只需从fifo获取数据进行处理。前提是,处理效率必须大于DAM接收搬运数据的效率,否则导致数据丢失或者被覆盖处理。


* h; J0 r9 S, R* w6 串口DMA发送

3 c% Z, M0 Q& |* o& \
3 _  ~9 s; [; j/ Q0 {* L, T. C: g0 E5.1 基本流程
. v( L1 A# X( C! p, B+ n2 W

6.png


8 j. U$ F3 R& g2 q% P) G/ M0 h% y4 j2 r2 I" y7 `/ P; D
串口发送流程图
' e2 Z  ?, E9 x1 g( L! _6 }) l

6 M$ S5 Z' x' k0 k( N: j' C' ~9 G4 k3 Z2 c/ s
5.2 相关配置

$ @1 R% i# p/ D4 K- E2 {- {$ I: R5 K

关键步骤


1 j! ^* G. Y4 q$ c

【1】初始化串口

% e; d, i. m8 z% N

【2】使能串口DMA发送模式

- m- E) |: D0 V* |6 j

【3】配置DMA发送通道,这一步无需在初始化设置,有数据需要发送时才配置使能DMA发送通道


% f' s5 X( h3 d" P. {

  UART2 DMA模式发送配置代码如下,与其他外设使用DMA的配置基本一致,留意关键配置:


* |$ U/ X7 p; t' K# p

  • 串口发送是,DMA通道工作模式设为单次模式(正常模式),每次需要发送数据时重新配置DMA
  • 使能DMA通道传输完成中断,利用该中断信息处理一些必要的任务,如清空发送状态、启动下一次传输
  • 启动DMA通道前清空相关状态标识,防止首次传输错乱数据* f" l" i- J0 T/ T+ `

    8 b, ~1 c* w0 E! W; B) V4 _- {) S' a
  • . s! m( j: K+ ^9 n, ]
    4 N1 {7 R/ R$ u5 D1 F

: r5 P9 y  T4 L2 F5.3 发送处理

  串口待发送数据存于发送fifo中,发送处理函数需要做的的任务就是循环查询发送fifo是否存在数据,如存在则将该数据拷贝到DMA发送buf中,然后启动DMA传输。前提是需要等待上一次DMA传输完毕,即是DMA接收到DMA传输完成中断信号"DMA_IT_TC"。

串口发送处理函数:

9 b! V) D: N6 j- y1 r# h) S

  1. void uart_poll_dma_tx(uint8_t uart_id)
    ! u9 w# _* G0 V! m
  2. {
    7 o0 ~! ?: K6 a9 r$ c. h
  3.           uint16_t size = 0;5 W) w5 m( s7 D7 [9 F0 e  U; T( P! s
  4.        
    7 X- V& ^8 {3 T' [( e( {
  5.         if (0x01 == s_uart_dev[uart_id].status)2 y$ `/ P6 P* r+ }2 ?% O
  6.     {
    , r% l' h# t1 t9 Z' `3 \9 i
  7.         return;
    $ F9 o4 u9 n2 c; Y/ h  _; m* Y- v
  8.     }9 |" e9 H% L% \
  9.         size = fifo_read(&s_uart_dev[uart_id].tx_fifo, s_uart_dev[uart_id].dmatx_buf,
    2 ]0 e% Y% f: E) {. Y* A: h  C8 d
  10.                                          s_uart_dev[uart_id].dmatx_buf_size);; ^# Q* H. w; Z" Q
  11.         if (size != 0)
    ( t! Y& G3 o- A2 ^6 D) m
  12.         {2 M7 H% d  {& U* P* w! n
  13.         s_UartTxRxCount[uart_id*2+0] += size;9 i/ |1 u& ]# [- }* j
  14.                   if (uart_id == 0)8 H" _1 u2 l& P# ?; g& p
  15.                 {5 J) A) x- U8 C+ D1 n8 D: w5 o
  16.             s_uart_dev[uart_id].status = 0x01;        /* DMA发送状态 */
    . R, W' u, A$ n4 u
  17.                           bsp_uart1_dmatx_config(s_uart_dev[uart_id].dmatx_buf, size);% V; g" H6 [+ H  ?- X6 h! z
  18.                 }* i/ l4 U4 P( ?1 ]8 U
  19.                 else if (uart_id == 1)( F) J, g8 q$ q* z! W5 y
  20.                 {
    " O/ a& s2 R6 k/ c
  21.             s_uart_dev[uart_id].status = 0x01;        /* DMA发送状态,必须在使能DMA传输前置位,否则有可能DMA已经传输并进入中断 */
    8 x8 U. F3 U5 ]; h0 Y5 W
  22.                         bsp_uart2_dmatx_config(s_uart_dev[uart_id].dmatx_buf, size);
    # q8 S. d( [+ Z- _
  23.                 }
    1 r6 H1 M% I. f5 d- X, G
  24.         }/ F1 B2 d4 b3 o. v% k& y* p; M
  25. }
复制代码

9 N( W8 b/ m! ~. Z  [

注意发送状态标识,必须先置为“发送状态”,然后启动DMA 传输。如果步骤反过来,在传输数据量少时,DMA传输时间短,“DMA_IT_TC”中断可能比“发送状态标识置位”先执行,导致程序误判DMA一直处理发送状态(发送标识无法被清除)


: L* N3 K8 E7 C! ~3 o8 \


6 v5 X1 s9 v. j5 R9 F; {3 l

注:- d% R* W' N+ I3 a) c
关于DMA发送数据启动函数,有些博客文章描述只需改变DMA发送buf的大小即可;经过测试发现,该方法在发送数据量较小时可行,数据量大后,导致发送失败,而且不会触发DMA发送完成中断。因此,可靠办法是:每次启动DMA发送,重新配置DMA通道所有参数。该步骤只是配置寄存器过程,实质上不会占用很多CPU执行时间。

& V2 `; m+ h  S6 G# w8 C2 Z$ P

DMA传输完成中断处理函数:

  1. void uart_dmatx_done_isr(uint8_t uart_id)* c( l! N' k3 U/ |
  2. {6 L) {$ ~. V$ @: t& ^; t
  3.         s_uart_dev[uart_id].status = 0;        /* 清空DMA发送状态标识 */
    1 u" w7 y! ]2 H  f: z) `: j; D% ~
  4. }
复制代码
. ?; k7 P/ \' F( ?


5 m1 Z1 q7 _9 m( f" {5 ?5 O/ c


4 M' s9 R; |( ]$ {# Y

  上述串口发送处理函数可以在几种情况调用:


7 W: J9 G! W+ D$ A# R" N

4 Q  f" w! ]' h

  • 主线程任务调用,前提是线程不能被其他任务阻塞,否则导致fifo溢出2 Z) l7 A$ v0 b$ L. b( I

    8 V/ D" d3 `. w0 n/ E
  1. void thread(void)
    # j1 }* ^0 @% |! _6 f
  2. {/ O. G5 \# G+ P. A  b# |9 W' U* Q
  3.     uart_dmatx_done_isr(DEV_UART1);! B# L+ P' e0 F
  4.     uart_dmatx_done_isr(DEV_UART2);$ c; q. _* K: `2 }% F7 S; e2 A
  5. }
复制代码

  c6 K( ?, x/ V" H) y
/ C- x) U! ]% \/ G' \" `; d
  • 定时器中断中调用
    & e* |% B7 q) G( W2 E# r
  1. void TIMx_IRQHandler(void)/ D( f( p8 \3 c. R/ c' r( m; N
  2. {
    # A) p+ L, g2 b9 S
  3.     uart_dmatx_done_isr(DEV_UART1);& g& S- i2 n) S( N
  4.     uart_dmatx_done_isr(DEV_UART2);* w! o5 u8 ~; t# p# _
  5. }
复制代码
# l1 @! {- P4 j( X  U7 q

6 c6 m+ f3 H: s' `2 Y! t; J+ k% a
  • DMA通道传输完成中断中调用/ D+ Z' w& q& O) h% O
    6 W; n7 b* e; l6 r6 S2 t5 n
  1. void DMA1_Channel4_5_IRQHandler(void)  I6 J( g! i3 Q. Z; Y/ P; f* j# T$ F
  2. {* m# v4 [3 S% f. a
  3.         if(DMA_GetITStatus(DMA1_IT_TC4))! E: ]- }9 K" w4 Y
  4.         {5 K7 j4 b; S7 ?, l$ b. {
  5.                 UartDmaSendDoneIsr(UART_2);: b1 \" [, F% f  d+ t7 I+ n
  6.                 DMA_ClearFlag(DMA1_FLAG_TC4);
    ) K+ |" p0 n+ M: m7 R3 c! L4 F! W
  7.                 uart_dmatx_done_isr(DEV_UART2);, }$ m$ F! r( R2 X0 d
  8.         }
    # K6 h, B7 x  L& Q6 X6 t+ c) w- j
  9. }
复制代码

每次拷贝多少数据量到DMA发送buf:

  关于这个问题,与具体应用场景有关,遵循的原则就是:只要发送fifo的数据量大于等于DMA发送buf的大小,就应该填满DMA发送buf,然后启动DMA传输,这样才能充分发挥会DMA性能。因此,需兼顾每次DMA传输的效率和串口数据流实时性,参考着几类实现:

  • 周期查询发送fifo数据,启动DMA传输,充分利用DMA发送效率,但可能降低串口数据流实时性
  • 实时查询发送fifo数据,加上超时处理,理想的方法
  • 在DMA传输完成中断中处理,保证实时连续数据流
    / F/ b% s4 C5 |" y" c4 D) @8 a: K
    3 S* G8 o6 i& W' \& v
2 ?) A. c& @8 M
6 串口设备

4 k) u' {* G$ B, R6 T
9 N0 S8 t- |/ F1 U, j7 r' {+ L1 A6.1 数据结构
# {- m3 m" m* Z' ?5 K
  1. /* 串口设备数据结构 */( x% h7 C5 i; L& _" F4 U5 o$ \2 Q
  2. typedef struct" ~1 q- b2 b  T: I+ |& q6 D+ M
  3. {& H4 H$ O% G# s3 S- J
  4.         uint8_t status;                        /* 发送状态 */1 o4 \8 l7 m# O
  5.         _fifo_t tx_fifo;                /* 发送fifo */+ o" ]. J# T8 e( D7 V8 q& q
  6.         _fifo_t rx_fifo;                /* 接收fifo */7 M$ r, I. L- j7 X
  7.         uint8_t *dmarx_buf;                /* dma接收缓存 */
    % B5 T) ~% I( f: Y
  8.         uint16_t dmarx_buf_size;/* dma接收缓存大小*/
    % o" P: s% G5 Q  E
  9.         uint8_t *dmatx_buf;                /* dma发送缓存 */
    , W4 G5 _0 h! `9 Z
  10.         uint16_t dmatx_buf_size;/* dma发送缓存大小 */! t! g9 v% ]$ i/ {3 z
  11.         uint16_t last_dmarx_size;/* dma上一次接收数据大小 */
    0 K9 c. c8 {/ k7 M
  12. }uart_device_t;
复制代码

( _8 `% k( R5 y7 c! {" o0 ~% H7 e. h8 E& s
6.2 对外接口
2 r. c3 p6 q' o1 W3 N
7 Z& I, b0 A9 H
  1. /* 串口注册初始化函数 */' n. N- j( d7 w6 @; D
  2. void uart_device_init(uint8_t uart_id)
    6 ^8 k5 u4 a, {' P6 ~0 _
  3. {, b; i4 \7 {# N9 O* k; }
  4.           if (uart_id == 1)0 E$ H  Z2 X4 W0 `& [) b7 ~
  5.         {  @0 y" q" g& ^8 j
  6.                 /* 配置串口2收发fifo */
    ' ^: g$ ~! t+ `  F1 t% c  D, Q1 K
  7.                 fifo_register(&s_uart_dev[uart_id].tx_fifo, &s_uart2_tx_buf[0], ) u3 p& C* c$ M  Y! A
  8.                       sizeof(s_uart2_tx_buf), fifo_lock, fifo_unlock);) o) q9 n2 o) \! U/ A6 v
  9.                 fifo_register(&s_uart_dev[uart_id].rx_fifo, &s_uart2_rx_buf[0],
    ! ], ^: @- e2 H% U/ @8 M
  10.                       sizeof(s_uart2_rx_buf), fifo_lock, fifo_unlock);6 u' f# _# K$ g
  11.                 " m$ z# y/ e2 n: @7 t# P( e! p9 Q
  12.                 /* 配置串口2 DMA收发buf */
    : v6 v) p+ V% m4 c& N# X
  13.                 s_uart_dev[uart_id].dmarx_buf = &s_uart2_dmarx_buf[0];
    1 k1 Z( ~. ?2 O% E4 l
  14.                 s_uart_dev[uart_id].dmarx_buf_size = sizeof(s_uart2_dmarx_buf);. O4 j$ q" ^+ m: {8 ?; N' U5 v
  15.                 s_uart_dev[uart_id].dmatx_buf = &s_uart2_dmatx_buf[0];
    ) _/ f5 r. D; q! F
  16.                 s_uart_dev[uart_id].dmatx_buf_size = sizeof(s_uart2_dmatx_buf);2 [: k- q7 n" m0 c2 A4 [( V
  17.                 bsp_uart2_dmarx_config(s_uart_dev[uart_id].dmarx_buf,
    3 I- i' @8 I2 o5 V6 e3 _% j
  18.                                                            sizeof(s_uart2_dmarx_buf));3 R' F$ _3 f) S( F# S& A
  19.                 s_uart_dev[uart_id].status  = 0;% I5 ~4 m, n. j7 y7 c' t4 B
  20.         }
    ; D& d. Y. j- V( @* ~
  21. }# g  C2 j% E7 v# k( w) U6 O
  22. 0 |, |+ V0 V$ L# Y2 D5 D
  23. /* 串口发送函数 */
    - h6 O' R- `$ d
  24. uint16_t uart_write(uint8_t uart_id, const uint8_t *buf, uint16_t size)7 x; q' O/ w7 M
  25. {- g8 r2 }, N) H1 |  ?
  26.         return fifo_write(&s_uart_dev[uart_id].tx_fifo, buf, size);/ b4 r' @( J3 s: |
  27. }" h( o# O' a' _$ w7 Y8 l

  28. 7 c* O7 S* h' l
  29. /* 串口读取函数 */; D9 T/ x5 u5 B7 N$ f/ w
  30. uint16_t uart_read(uint8_t uart_id, uint8_t *buf, uint16_t size)
    0 e7 l$ @9 B8 |. B  j2 ~
  31. {
    % R' _- [2 z+ _
  32.         return fifo_read(&s_uart_dev[uart_id].rx_fifo, buf, size);5 _7 B; K' G$ s
  33. }
复制代码

/ N8 u( Y5 h1 [( }$ h8 q8 X5 w3 Q8 完整源码
' p3 C7 w) b  ?% }
% g- _8 |: Q6 w* S2 a& v9 N5 d( C
% L9 G9 J* S0 D

串口&DMA底层配置:

, h( y6 X0 J' z5 _

  1. #include <stddef.h>( b# f1 B' m( g2 C
  2. #include <stdint.h>
    1 H0 u1 M" \, S6 t+ K% V) u! w
  3. #include <stdbool.h>
    4 W( P" b. N* B
  4. #include "stm32f0xx.h"
    & V% i$ n+ C/ l
  5. #include "bsp_uart.h"; ^! |" @4 K& {- H
  6. ( J; O1 A( f2 J4 j, {7 N4 S
  7. /**
    6 i: ]* L2 M; B' ~3 M
  8. * @brief  ( n' E/ S  v1 w1 a" b- N# T" ~  D
  9. * @param  2 ]( d# p" c/ @  a
  10. * @retval , `6 |4 e% ]5 O* h# Q
  11. */
    + P3 ~) F' Y9 v
  12. static void bsp_uart1_gpio_init(void)' C( }. |1 s5 r! p0 `
  13. {
    ) Y  D! \8 [" U0 J6 {( E# X
  14.     GPIO_InitTypeDef    GPIO_InitStructure;
    9 D' O- Q! n5 u& q6 x$ ?
  15. #if 0
    9 {% \- D1 Z, g8 `  T* M/ r
  16.         RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOB, ENABLE);
    6 d( J8 s% S8 t, W
  17.        
    7 `; P( I2 b$ d+ }: j$ c
  18.     GPIO_PinAFConfig(GPIOB, GPIO_PinSource6, GPIO_AF_0);
    8 [2 c2 W1 J& B+ Z
  19.     GPIO_PinAFConfig(GPIOB, GPIO_PinSource7, GPIO_AF_0);
    ! g1 Y5 _( k% j- L( n
  20.         1 e) }9 n  Q) S* _8 u, V
  21.         GPIO_InitStructure.GPIO_Pin         = GPIO_Pin_6 | GPIO_Pin_7;- l3 a: M: \( x, Z% M. m8 u8 X- S
  22.     GPIO_InitStructure.GPIO_Mode         = GPIO_Mode_AF;
    2 Z! s. m/ J. a
  23.         GPIO_InitStructure.GPIO_OType         = GPIO_OType_PP;. G# r, o, A& y. M6 D
  24.     GPIO_InitStructure.GPIO_Speed          = GPIO_Speed_Level_3;8 u1 N) _* ^: @7 ~, M
  25.     GPIO_InitStructure.GPIO_PuPd         = GPIO_PuPd_UP;
    $ L- X% x8 x+ `5 X
  26.     GPIO_Init(GPIOB, &GPIO_InitStructure);
    ! w/ ]- c$ p% {
  27. #else5 R# l3 S7 o& F9 ^: N
  28.         RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOA, ENABLE);
      _' q- P, p2 g( S% l6 S) \
  29.         / h: r5 s) X, l8 l
  30.     GPIO_PinAFConfig(GPIOB, GPIO_PinSource9, GPIO_AF_1);
    + o4 r; b: |) u% P# d+ N, h+ E
  31.     GPIO_PinAFConfig(GPIOB, GPIO_PinSource10, GPIO_AF_1); / d9 e; J0 F: P& B$ M; ]3 z
  32.        
    0 z: b+ s5 K4 Z/ `' j/ M& t
  33.         GPIO_InitStructure.GPIO_Pin         = GPIO_Pin_9 | GPIO_Pin_10;, D2 Y. L, D2 R% Q8 v! H
  34.     GPIO_InitStructure.GPIO_Mode         = GPIO_Mode_AF;* W. o( ]' q. \5 M( Q3 ]
  35.         GPIO_InitStructure.GPIO_OType         = GPIO_OType_PP;
    ( O) G' B1 G" {) `7 \
  36.     GPIO_InitStructure.GPIO_Speed          = GPIO_Speed_Level_3;/ r) o; ?% S. G! |2 x: M
  37.     GPIO_InitStructure.GPIO_PuPd         = GPIO_PuPd_UP;% _& y/ [/ k" [2 p' s4 v( P+ T
  38.     GPIO_Init(GPIOA, &GPIO_InitStructure);
    # v) e; O, k; o% p
  39. #endif
    & O5 s* \% J( q0 ]. K5 ?; Y  I
  40. }- |& i4 e) J& [6 ]* ~9 S* Z7 W  U7 x

  41. 6 G" V6 q# _7 W* H, l7 I; P
  42. /**
    6 v" _9 ~2 G: g7 O
  43. * @brief  
    & G, l3 N& B/ M1 [
  44. * @param  1 f7 f2 Z2 ~* \' A7 x: B
  45. * @retval 8 k7 _! S' x: h3 M. _
  46. */
    , A5 Q" `2 k! T
  47. static void bsp_uart2_gpio_init(void). P2 F! p! j6 n; D' v! {
  48. {
    5 O- D; q$ R" N" F% V
  49.         GPIO_InitTypeDef GPIO_InitStructure;
    + W0 E, G* `" W) T: G3 N: f8 w
  50.         ( F9 M) T. C. I. p
  51.         RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOB, ENABLE);/ K' ?$ S# o# p) ]. u/ s
  52.         8 A+ n0 d& e* e+ @6 I
  53.         GPIO_PinAFConfig(GPIOA, GPIO_PinSource2, GPIO_AF_1);( L) W' t& H; C0 [, G
  54.         GPIO_PinAFConfig(GPIOA, GPIO_PinSource3, GPIO_AF_1);
    + V7 K& ]. \7 Z; {% f0 m
  55.         , s1 W( y- p0 v8 g
  56.         GPIO_InitStructure.GPIO_Pin   = GPIO_Pin_2 | GPIO_Pin_3;
    & H% W# k$ A1 Q" s5 `) b
  57.         GPIO_InitStructure.GPIO_Mode  = GPIO_Mode_AF;3 R9 L( _/ ]  U5 H3 f3 y- u: \: t( _
  58.         GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;) J. a- P: ^6 r4 ^
  59.         GPIO_InitStructure.GPIO_Speed = GPIO_Speed_10MHz;
    0 @4 H$ |, S# B& Q  u) D
  60.         GPIO_InitStructure.GPIO_PuPd  = GPIO_PuPd_UP;
    , l5 D8 e# L  `2 H9 q
  61.         GPIO_Init(GPIOA, &GPIO_InitStructure);( w0 R$ [! Z# J9 }
  62. }0 y9 I) z+ }3 O

  63. # y5 H- d' O6 w6 N' f
  64. /**
    - \- C' t' l. r6 ~* @
  65. * @brief    ?4 U4 s. G" ~# x0 R7 _: X
  66. * @param  5 z  T1 u( L$ j9 j
  67. * @retval
    " \  Y+ B1 a3 b0 C. }2 n0 B
  68. */
    # n4 [- U7 g- V/ w; l7 M2 a! M0 h1 B
  69. void bsp_uart1_init(void)4 v5 ?/ \. \( C+ V
  70. {
    ' |2 H, z5 T! g8 D% F  g
  71.         USART_InitTypeDef USART_InitStructure;' K5 g# i- R1 R3 S" N
  72.         NVIC_InitTypeDef NVIC_InitStructure;
    $ @8 [7 T# j* `( D% ?
  73.         # S* d+ S, x: c
  74.         bsp_uart1_gpio_init();6 y& v* ]7 D7 y& [4 C
  75.         # C1 L& X/ N2 m  ]4 n+ x. n8 h* g% g
  76.         /* 使能串口和DMA时钟 */( U( O( g$ d: r
  77.         RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);) u3 y" [8 p$ d3 _
  78.         RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);' c2 T9 N/ I& k0 }  C
  79.         + j) q) P9 A9 J, S5 i2 g
  80.         USART_InitStructure.USART_BaudRate            = 57600;
    / T+ `+ ^& h  [" L9 P- Q: s6 a% K
  81.         USART_InitStructure.USART_WordLength          = USART_WordLength_8b;: K7 F$ c+ \# U! L7 D" [
  82.         USART_InitStructure.USART_StopBits            = USART_StopBits_1;. Z) Q. O- w; {6 `) c: {; ]# Z# u+ O
  83.         USART_InitStructure.USART_Parity              = USART_Parity_No;
      o/ ]' Q3 ~- e9 B1 E: ~0 d
  84.         USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;; R$ p: m5 m. L4 c/ C$ {/ M
  85.         USART_InitStructure.USART_Mode                = USART_Mode_Rx | USART_Mode_Tx;/ g& W) R- j5 A; Y& J5 {. r( k2 F( a& }
  86.         USART_Init(USART1, &USART_InitStructure);. `; B. z" W7 s- Z% m
  87.        
    $ |1 O6 w7 h6 s# n4 u
  88.         USART_ITConfig(USART1, USART_IT_IDLE, ENABLE);        /* 使能空闲中断 */
    $ T- ^+ P5 w" v
  89.         USART_OverrunDetectionConfig(USART1, USART_OVRDetection_Disable);" r; A+ O# p# H% [  i& F
  90.         % z0 {, Z  a- M
  91.         USART_Cmd(USART1, ENABLE);
    6 y" ]& P0 y' _; s- r$ O7 w4 q
  92.         USART_DMACmd(USART1, USART_DMAReq_Rx|USART_DMAReq_Tx, ENABLE); /* 使能DMA收发 */
    1 c$ K! ~8 G, b; J  w

  93. ! X' e6 U7 E. O1 z9 U" `
  94.         /* 串口中断 */0 T9 t, ~1 o* b
  95.         NVIC_InitStructure.NVIC_IRQChannel         = USART1_IRQn;
    ( V& C/ n; E: y+ ]  A# Z8 v
  96.         NVIC_InitStructure.NVIC_IRQChannelPriority = 2;
    - T: u4 N& Y4 J! Z7 r6 z5 K
  97.         NVIC_InitStructure.NVIC_IRQChannelCmd      = ENABLE;& A0 \7 g; N' y7 Q/ l$ O# V
  98.         NVIC_Init(&NVIC_InitStructure);& y+ l2 W  h' @2 J# @8 ^
  99. " B& ^+ K! s, M& b
  100.         /* DMA中断 */
    ' `* U) v) D! `2 }* `' U! H; Y
  101.           NVIC_InitStructure.NVIC_IRQChannel                    = DMA1_Channel2_3_IRQn;       * j2 w/ V2 W2 L$ y8 {7 T
  102.           NVIC_InitStructure.NVIC_IRQChannelPriority = 0;
    ' _8 r9 p! l6 h! q6 j& [" C& B* v
  103.         NVIC_InitStructure.NVIC_IRQChannelCmd      = ENABLE;
    , R7 V4 `/ x$ o0 x
  104.           NVIC_Init(&NVIC_InitStructure);& S* P; s/ ]1 P; a
  105. }3 {8 P7 r  T$ y, p

  106. % e1 x3 i9 `( u9 p& P# G
  107. /**
    : ^4 V$ v1 Q" i1 G" L
  108. * @brief  
    7 m0 p3 E% K0 r3 x! b
  109. * @param  
    0 N: T5 y5 }. P$ M4 o! i
  110. * @retval 4 S' L% y6 q5 L7 Y7 i
  111. */4 z; p( e5 H) N2 l  Y- u/ H
  112. void bsp_uart2_init(void)
    , d" Q- ?* s4 s* e* C! H
  113. {! H3 P: b4 ]; U: u7 ]
  114.         USART_InitTypeDef USART_InitStructure;" k  ~: U$ D: T
  115.         NVIC_InitTypeDef NVIC_InitStructure;
      R) k, k" J. B- [0 i- _2 h
  116.        
    8 j4 n: C* P) h: `+ w1 M+ T
  117.         bsp_uart2_gpio_init();
    4 J( o# b) b9 d' R
  118.        
    ; k6 M; B! _  A" e, v
  119.         /* 使能串口和DMA时钟 */0 g3 }* v/ F& s
  120.         RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);
    " X9 A8 n" r/ X' @# \
  121.         RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2, ENABLE);' ^( `4 I! }9 K: E1 `5 a

  122. : _' D0 g% d& t: ]1 z
  123.         USART_InitStructure.USART_BaudRate            = 57600;& D. f5 X) i+ x, N% u
  124.         USART_InitStructure.USART_WordLength          = USART_WordLength_8b;
    8 ]: K5 [# o7 x8 ~* s1 ~- n
  125.         USART_InitStructure.USART_StopBits            = USART_StopBits_1;8 B2 Z0 ^8 J# ^
  126.         USART_InitStructure.USART_Parity              = USART_Parity_No;8 q( X9 \8 E! H9 t7 }
  127.         USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;) m7 E& f: R1 M+ T9 V- g+ O
  128.         USART_InitStructure.USART_Mode                = USART_Mode_Rx | USART_Mode_Tx;
    % _& S4 A# O6 x9 j1 I
  129.         USART_Init(USART2, &USART_InitStructure);
    . |: H3 k4 F% |+ x& O
  130.         + U: h  V8 n' B
  131.         USART_ITConfig(USART2, USART_IT_IDLE, ENABLE);        /* 使能空闲中断 */
    ) p% g1 V2 o  P9 K
  132.         USART_OverrunDetectionConfig(USART2, USART_OVRDetection_Disable);( |" o, S3 x  u4 M- p6 }+ v% I
  133.         . Q! W& r% M: |5 n* A5 Y
  134.         USART_Cmd(USART2, ENABLE);2 l1 f3 ]! q, y' z* Y
  135.         USART_DMACmd(USART2, USART_DMAReq_Rx|USART_DMAReq_Tx, ENABLE);         /* 使能DMA收发 */
    # V: l% t9 V" h6 ]7 y
  136. 6 U. R, R$ t2 \; x: c( i8 `9 x, I
  137.         /* 串口中断 */
    5 F1 S* o1 f4 m1 X
  138.         NVIC_InitStructure.NVIC_IRQChannel         = USART2_IRQn;0 e0 r# I- r. }6 a: f
  139.         NVIC_InitStructure.NVIC_IRQChannelPriority = 2;/ m0 t8 Y0 _' s  _7 U9 m+ j) n
  140.         NVIC_InitStructure.NVIC_IRQChannelCmd      = ENABLE;6 l, h4 l: ^8 E  L5 y4 r
  141.         NVIC_Init(&NVIC_InitStructure);  A7 [( [$ U. M, M4 I

  142. # n+ ]) k; L( ?* a6 B$ G5 e
  143.         /* DMA中断 */
    2 Z% |0 d, O2 ^! s( n; k
  144.         NVIC_InitStructure.NVIC_IRQChannel         = DMA1_Channel4_5_IRQn;         [( |& e, L9 l$ ?& p/ T
  145.           NVIC_InitStructure.NVIC_IRQChannelPriority = 0;
    , {' H9 f/ c  [9 T0 l2 g" e
  146.         NVIC_InitStructure.NVIC_IRQChannelCmd      = ENABLE;
    * x0 R' q  i; L- ~+ q4 d: b- q
  147.           NVIC_Init(&NVIC_InitStructure);
    5 k; B% x& o( @" l
  148. }9 e. m2 v0 y7 L1 b

  149. : d) P6 t% _7 H' L; a! k3 v% ^
  150. void bsp_uart1_dmatx_config(uint8_t *mem_addr, uint32_t mem_size), Y5 v: W1 i0 W2 G0 b
  151. {% d8 \5 F+ p+ j, s5 w& G
  152.           DMA_InitTypeDef DMA_InitStructure;# y- Y1 o2 O/ ]3 D
  153.        
    : l+ U( K% m" v" Q! W- c5 @. Z
  154.         DMA_DeInit(DMA1_Channel2);+ ^" x0 S- @4 s, Y" S
  155.         DMA_Cmd(DMA1_Channel2, DISABLE);
    8 |! \' X1 J0 m
  156.         DMA_InitStructure.DMA_PeripheralBaseAddr         = (uint32_t)&(USART1->TDR);( [) r' `! B* E: m  A( [% t; h
  157.         DMA_InitStructure.DMA_MemoryBaseAddr                 = (uint32_t)mem_addr; : i% G$ n' B# N
  158.         DMA_InitStructure.DMA_DIR                                         = DMA_DIR_PeripheralDST;         /* 传输方向:内存->外设 */% q2 M# o1 M, N3 ?2 c
  159.         DMA_InitStructure.DMA_BufferSize                         = mem_size; ' A. I9 H! l( r7 V* I5 D8 x- M& m" _& r
  160.         DMA_InitStructure.DMA_PeripheralInc                 = DMA_PeripheralInc_Disable;
    ! k$ n; _/ ^3 k' b! d
  161.         DMA_InitStructure.DMA_MemoryInc                         = DMA_MemoryInc_Enable;
    / {. i( h8 ?& S+ ^: A! ]$ Q
  162.         DMA_InitStructure.DMA_PeripheralDataSize         = DMA_PeripheralDataSize_Byte;
    9 ~! E, t0 l+ A* b
  163.         DMA_InitStructure.DMA_MemoryDataSize                 = DMA_MemoryDataSize_Byte;0 ^: p( ~  P) }5 @, ~
  164.         DMA_InitStructure.DMA_Mode                                         = DMA_Mode_Normal;
    + {0 M" V, G, M. L. R3 D
  165.         DMA_InitStructure.DMA_Priority                                 = DMA_Priority_High;
    % m) B0 M& p) O/ }: @4 p4 Q/ h+ l
  166.         DMA_InitStructure.DMA_M2M                                         = DMA_M2M_Disable; 1 \( f" ]: M; Q$ G; T5 r# f3 k
  167.         DMA_Init(DMA1_Channel2, &DMA_InitStructure);  + t, Z! H1 L' l! t- T) ~: ?  o
  168.         DMA_ITConfig(DMA1_Channel2, DMA_IT_TC|DMA_IT_TE, ENABLE); 4 l  M# W* J; @
  169.         DMA_ClearFlag(DMA1_IT_TC2);        /* 清除发送完成标识 */
    2 Q, t* B8 R8 s9 e4 T
  170.         DMA_Cmd(DMA1_Channel2, ENABLE); * ~7 \; {! I2 i: A* t
  171. }9 ?9 o1 E7 b" _0 n7 _

  172. , z2 ?$ q  I( [$ h, _5 a
  173. void bsp_uart1_dmarx_config(uint8_t *mem_addr, uint32_t mem_size)
    ! J8 e0 o$ {  `' D" Y) d% T
  174. {
    3 W$ k; Q- F4 W* M
  175.           DMA_InitTypeDef DMA_InitStructure;5 H/ ~/ y, z: `  D: L6 `: k. V
  176.         + I& a/ E, m: Y) q' W' X
  177.         DMA_DeInit(DMA1_Channel3);
    0 ~8 ?; R$ Y9 f6 ^5 z: ^0 `
  178.         DMA_Cmd(DMA1_Channel3, DISABLE);' j/ j  M# j. w6 u2 R$ Y
  179.         DMA_InitStructure.DMA_PeripheralBaseAddr         = (uint32_t)&(USART1->RDR);
    4 C$ ~6 ]1 ?# L5 K6 c- F
  180.         DMA_InitStructure.DMA_MemoryBaseAddr                 = (uint32_t)mem_addr;
    5 Y; ~& P: T  Y, A: m  J7 N- P
  181.         DMA_InitStructure.DMA_DIR                                         = DMA_DIR_PeripheralSRC;         /* 传输方向:外设->内存 */
    4 |% W( F+ p8 v1 Y; H
  182.         DMA_InitStructure.DMA_BufferSize                         = mem_size; , U+ E3 o- D, O5 c% v. k7 Y' H
  183.         DMA_InitStructure.DMA_PeripheralInc                 = DMA_PeripheralInc_Disable; 6 t0 Z6 w# R5 k0 ^2 r7 L$ }; T, S
  184.         DMA_InitStructure.DMA_MemoryInc                         = DMA_MemoryInc_Enable;
    2 i. l7 ^; G( k3 p/ H% T
  185.         DMA_InitStructure.DMA_PeripheralDataSize         = DMA_PeripheralDataSize_Byte; $ C) ?7 @0 Z7 z0 W" [6 ]
  186.         DMA_InitStructure.DMA_MemoryDataSize                 = DMA_MemoryDataSize_Byte;0 h* T" G6 q6 h6 l% F' P9 |' `6 V
  187.         DMA_InitStructure.DMA_Mode                                         = DMA_Mode_Circular; 1 @4 q( H2 Y, [1 H. u6 i. g! j# H( d- V) k
  188.         DMA_InitStructure.DMA_Priority                                 = DMA_Priority_VeryHigh; - m2 ~+ ~8 a$ z
  189.         DMA_InitStructure.DMA_M2M                                         = DMA_M2M_Disable;
    7 {5 ^- e7 [& E1 t
  190.         DMA_Init(DMA1_Channel3, &DMA_InitStructure);
    / Q3 W3 e/ D2 d5 l) }! l" V& T0 \
  191.         DMA_ITConfig(DMA1_Channel3, DMA_IT_TC|DMA_IT_HT|DMA_IT_TE, ENABLE);/* 使能DMA半满、全满、错误中断 */! Q) Q( o# V  d; p
  192.         DMA_ClearFlag(DMA1_IT_TC3);
    # S% J+ K  X/ d# i# [
  193.         DMA_ClearFlag(DMA1_IT_HT3);
    2 M) j; A" |  _/ X- z4 T4 j" a
  194.         DMA_Cmd(DMA1_Channel3, ENABLE);
    : }, ~4 l" [" N% o- K! {2 W2 e
  195. }2 W% E6 m5 ?0 j9 h
  196. ( _+ q/ l8 {2 _' n9 r: Z* @; ^  H
  197. uint16_t bsp_uart1_get_dmarx_buf_remain_size(void)% \  E0 Z" Y; y7 F
  198. {3 y$ ]! j" p+ g! _# L( p, N% ~
  199.         return DMA_GetCurrDataCounter(DMA1_Channel3);        /* 获取DMA接收buf剩余空间 */
    0 x7 D, w/ [2 X- G
  200. }
    9 v2 p  v' N$ Z8 Y( I: \8 r

  201. : o; C# _% @* R6 J( t
  202. void bsp_uart2_dmatx_config(uint8_t *mem_addr, uint32_t mem_size)$ O$ @& T7 I  T/ k
  203. {% J6 |( ^& a! o6 C2 v' O9 ?
  204.           DMA_InitTypeDef DMA_InitStructure;$ r- b; Q, j$ h5 p" L
  205.         " n: C* K" f: ?
  206.         DMA_DeInit(DMA1_Channel4);
    ! F$ q* P, x) f6 Q; n- V4 J  ?% B
  207.         DMA_Cmd(DMA1_Channel4, DISABLE);: `* ^) _4 d5 b9 e
  208.         DMA_InitStructure.DMA_PeripheralBaseAddr         = (uint32_t)&(USART2->TDR);
    8 O, H1 j# b' M1 n/ P# Q
  209.         DMA_InitStructure.DMA_MemoryBaseAddr                 = (uint32_t)mem_addr;   F1 J8 I& w4 ~; Z' H* B1 R* x! B
  210.         DMA_InitStructure.DMA_DIR                                         = DMA_DIR_PeripheralDST;         /* 传输方向:内存->外设 */5 ?. k9 w8 Z0 d  U
  211.         DMA_InitStructure.DMA_BufferSize                         = mem_size;
    5 }6 B9 S7 N* M; n
  212.         DMA_InitStructure.DMA_PeripheralInc                 = DMA_PeripheralInc_Disable; ( c" s. x" ~& z/ u  d& p0 E. R
  213.         DMA_InitStructure.DMA_MemoryInc                         = DMA_MemoryInc_Enable;
    ' K5 y" |" L0 m! J! ]* n* t
  214.         DMA_InitStructure.DMA_PeripheralDataSize         = DMA_PeripheralDataSize_Byte;
    0 m+ S7 O; Y& N" @  D" a
  215.         DMA_InitStructure.DMA_MemoryDataSize                 = DMA_MemoryDataSize_Byte;0 I; |- V& {- [
  216.         DMA_InitStructure.DMA_Mode                                         = DMA_Mode_Normal; 0 u' A, ]2 {+ @' u+ H
  217.         DMA_InitStructure.DMA_Priority                                 = DMA_Priority_High;
      i( y% n, |! _( V
  218.         DMA_InitStructure.DMA_M2M                                         = DMA_M2M_Disable;
    . U$ \9 t5 ?3 {* V
  219.         DMA_Init(DMA1_Channel4, &DMA_InitStructure);  / P- }; f& J$ v' C- k. M: s
  220.         DMA_ITConfig(DMA1_Channel4, DMA_IT_TC|DMA_IT_TE, ENABLE); 5 W, u6 q0 |3 r9 y7 M# U' Y$ a
  221.         DMA_ClearFlag(DMA1_IT_TC4);        /* 清除发送完成标识 */
    9 {  y, p8 O. U$ `/ ^0 Z$ g
  222.         DMA_Cmd(DMA1_Channel4, ENABLE);
      F  \( t* t' R6 T+ O" l( v
  223. }  |* \$ @8 R' z( C7 z: D# H

  224. / Z- T8 ^4 O) Z( j0 f
  225. void bsp_uart2_dmarx_config(uint8_t *mem_addr, uint32_t mem_size)) `$ n2 f3 l6 e& h
  226. {
    8 j; n; M3 w2 Z: d
  227.           DMA_InitTypeDef DMA_InitStructure;
      [' N( F% M" v
  228.         ' a- l: I% T6 u5 }0 n% L7 @, T
  229.         DMA_DeInit(DMA1_Channel5);
    4 d- c4 H+ O5 D- G- o
  230.         DMA_Cmd(DMA1_Channel5, DISABLE);5 `  {, Y4 J4 V% j  \" S
  231.         DMA_InitStructure.DMA_PeripheralBaseAddr         = (uint32_t)&(USART2->RDR);! g. o4 `/ L! W6 P8 Y% R5 z* x
  232.         DMA_InitStructure.DMA_MemoryBaseAddr                 = (uint32_t)mem_addr;
    8 Y1 h. {7 K5 o+ w5 l1 j
  233.         DMA_InitStructure.DMA_DIR                                         = DMA_DIR_PeripheralSRC;         /* 传输方向:外设->内存 */
    . ~) q. v, C) ], [; L% Q; `  O
  234.         DMA_InitStructure.DMA_BufferSize                         = mem_size; * \* v2 J& m" b) A+ X, `6 u" h
  235.         DMA_InitStructure.DMA_PeripheralInc                 = DMA_PeripheralInc_Disable;
    3 H% @4 h( {8 i
  236.         DMA_InitStructure.DMA_MemoryInc                         = DMA_MemoryInc_Enable; 8 Q3 _7 E1 c3 W2 ^. u$ A
  237.         DMA_InitStructure.DMA_PeripheralDataSize         = DMA_PeripheralDataSize_Byte;
    ' R8 d: x2 F9 {5 {5 n6 y5 x) V2 `0 d# Z
  238.         DMA_InitStructure.DMA_MemoryDataSize                 = DMA_MemoryDataSize_Byte;! }( T+ v0 a0 z0 G6 B
  239.         DMA_InitStructure.DMA_Mode                                         = DMA_Mode_Circular;
    % Z' [  s3 C9 u0 [8 l3 X
  240.         DMA_InitStructure.DMA_Priority                                 = DMA_Priority_VeryHigh; - |. Q2 L7 e% \+ \. u5 D5 b2 s
  241.         DMA_InitStructure.DMA_M2M                                         = DMA_M2M_Disable; 3 A1 W$ d  V( g& _, q
  242.         DMA_Init(DMA1_Channel5, &DMA_InitStructure);
    - r3 i+ r/ ]5 n' ]
  243.         DMA_ITConfig(DMA1_Channel5, DMA_IT_TC|DMA_IT_HT|DMA_IT_TE, ENABLE);/* 使能DMA半满、全满、错误中断 */
    7 w, |0 C: Y6 u7 t
  244.         DMA_ClearFlag(DMA1_IT_TC5);5 v% Y% w# q3 Y2 w
  245.         DMA_ClearFlag(DMA1_IT_HT5);3 J% q- E+ G' ^* y
  246.         DMA_Cmd(DMA1_Channel5, ENABLE); 9 s7 L: D) S5 D, r
  247. }
    , T( h3 n1 m/ Q. c

  248. & }% S* g  ?) A2 B6 K5 _
  249. uint16_t bsp_uart2_get_dmarx_buf_remain_size(void)) s/ V9 W% Y& O1 d; u" R5 Q8 m
  250. {
    7 P1 R& _' y* V5 h; B
  251.         return DMA_GetCurrDataCounter(DMA1_Channel5);        /* 获取DMA接收buf剩余空间 */
    6 R7 i: Z0 a7 l( |$ L( s- d- u( [/ ~! e
  252. }
复制代码
! M9 {/ r+ ~9 C" f


& N. z( |+ u! Z$ `/ F

压力测试:

  • 1.5Mbps波特率,串口助手每毫秒发送1k字节数据,stm32f0 DMA接收数据,再通过DMA发送回串口助手,毫无压力。
  • 1.5Mbps波特率,可传输大文件测试,将接受数据保存为文件,与源文件比较。
  • 串口高波特率测试需要USB转TLL工具及串口助手都支持才可行,推荐CP2102、FT232芯片的USB转TTL工具。
    ) p% M3 p) h9 F8 g2 p

    # Y& R$ R$ N2 `3 d' d! k9 f
. o& a/ K3 l; I( y7 g* a% q" q

7.png


- N  C% h  l) t7 b0 G
1.5Mbps串口回环压力测试
0 y1 `9 Y4 p; \2 ^- Q0 r& F
; s: I* b; k5 A* W, L2 G- w8 m4 Y

0 Y" t, v4 w8 }# y( n9 S
收藏 3 评论0 发布时间:2020-9-15 15:49

举报

0个回答

所属标签

相似分享

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