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

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

[复制链接]
STMCU-管管 发布时间:2020-9-15 15:49
1 前言. o9 N  ?# {1 b& D8 M

  M+ _- T- n3 q5 H$ v

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

  • 内存—>内存,内存间拷贝
  • 外设—>内存,如uart、spi、i2c等总线接收数据过程
  • 内存—>外设,如uart、spi、i2c等总线发送数据过程. h# w+ [) s. J; U

    5 b7 g$ B/ [' m2 |/ r8 P. z' E

1 C- {- \( Q6 Q+ l. A( ]- j2 串口有必要使用DMA吗
* o, ?2 j. N! b, w

8 ~) K+ V' G! _. D
  {2 E  y( O3 d0 I9 F% v  X( Q8 r

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

0 A  B/ p6 V+ X

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

  • 对于发送,使用循环发送,可能阻塞线程,需要消耗大量CPU资源“搬运”数据,浪费CPU
  • 对于发送,使用中断发送,不会阻塞线程,但需浪费大量中断资源,CPU频繁响应中断;以115200bps波特率,1s传输11520字节,大约69us需响应一次中断,如波特率再提高,将消耗更多CPU资源
  • 对于接收,如仍采用传统的中断模式接收,同样会因为频繁中断导致消耗大量CPU资源
    9 w  n4 q; y) G& x, f# k

    & p( o+ _% g: r

( q1 g; B/ \6 p; V  g

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

7 u* B; f* S9 p! U$ J1 ^& K. G7 U# o. F
3 实现方式
) p* \8 _; g0 I* G1 C  a

/ R1 \) T) m2 C8 @4 |

1 (2).png


  R% R/ t1 [5 Q. e$ {4 F6 R" n4 [" k" A
/ [* V: R3 q2 B  p" C4 STM32串口使用DMA
! e0 F$ K5 e( z* ^; e$ K( Z, H

- ~! m. B% {/ n' w$ v3 N8 ~
( m7 C; Z+ C: g; S/ d

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

* I2 c5 U! B  h0 _; N/ [& u& A

测试平台:

  • STM32F030C8T6
  • UART1/UART2
  • DMA1 Channel2—Channel5
  • ST标准库
  • 主频48MHz(外部12MHz晶振)
    1 v3 D/ t' o5 [9 h6 _4 U
" M2 ^$ e7 b( J4 ~! h3 b
+ v. F$ r% ^7 C
2.png

  B( j+ u0 [/ q8 q3 L* t

' t" p8 z- N9 M8 S9 e4 }, k3 B

5 串口DMA接收
* f3 Z; u% V' g( U
; b2 B: f# ?" ^, ]! j5.1 基本流程% G# l/ R& ^' r$ W) \; T

3.png

, i/ ^; N; s. u5 F* ~( l  l
串口接收流程图
5 W- n- @% ~* d* o
: h# l8 q) O" C3 k# s" J. {; n6 g+ t
5.2 相关配置

3 y' F0 B4 s% Z$ ?! M& g* a1 Z
8 X$ k* y- k5 A2 i7 F

关键步骤

【1】初始化串口

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

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


! Y7 X' v. V; x; V/ X

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

, D" D* q* \* {9 D

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

% E" I3 Q, T* L* B' M

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


& r% W7 ?4 T! b; b

【1】第一步,DMA先将数据搬运到buf1,搬运完成通知CPU来拷贝buf1数据# Z) s+ z" u% B+ z+ K' w/ U
【2】第二步,DMA将数据搬运到buf2,与CPU拷贝buf1数据不会冲突5 _5 S! G. D2 \/ `
【3】第三步,buf2数据搬运完成,通知CPU来拷贝buf2数据
' t6 E5 }8 a4 m/ N9 p" x) X" d. l【4】执行完第三步,DMA返回执行第一步,一直循环


8 ]9 m# b' ?$ _; u2 [/ X


. x- Y! U- h1 }# p

4.png


( D0 S2 C& J) I* C! H# j/ o1 b* K
双缓存DMA数据搬运过程
, S& W( f6 t% E/ y0 g3 r
/ M. m0 M4 @) G6 _7 S# b3 G1 ?

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


: n$ i3 J8 C4 z* S" ^' h0 {

【1】第一步,DMA将数据搬运完成buf的前一半时,产生“半满中断”,CPU来拷贝buf前半部分数据" q; G  c1 Y& Y+ z
【2】第二步,DMA继续将数据搬运到buf的后半部分,与CPU拷贝buf前半部数据不会冲突# \# i% @9 {6 n0 c1 o$ G) z9 o/ J
【3】第三步,buf后半部分数据搬运完成,触发“溢满中断”,CPU来拷贝buf后半部分数据9 y8 l7 V6 s5 ^1 h2 P0 `
【4】执行完第三步,DMA返回执行第一步,一直循环

* y( U# L( \/ Q: @
9 B$ _+ I. u9 j+ w9 l* q: l7 j

0 G- G1 d- a& t- k4 q

5.png


; @# s6 y& n/ C$ n( ?5 v0 J, q( T$ x9 A4 z7 V6 [! Q" j
使用半满中断DMA数据搬运过程
+ L5 f2 o- s5 ^
1 R* P/ f+ S! g4 E

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

  • 串口接收,DMA通道工作模式设为连续模式
  • 使能DMA通道接收buf半满中断、溢满(传输完成)中断
  • 启动DMA通道前清空相关状态标识,防止首次传输错乱数据+ L/ W0 N# R8 j! M- u1 s* _
- [- G! {2 e3 s. r7 `( D# }
6 ~) g+ u7 m% `) a

, ^( Y" M; R1 p. J) ^" Q% }/ J' k' p

6 p; @2 A* K  w4 Y- Q7 W6 Q# o
  1. void bsp_uart2_dmarx_config(uint8_t *mem_addr, uint32_t mem_size)7 `+ s6 T+ K& Z5 i+ y) `4 O, b3 g* O; z
  2. {
    : W, }# R, V8 I" A& G2 c
  3.           DMA_InitTypeDef DMA_InitStructure;. B. P4 O( u% ~8 U6 R
  4.         ; Y3 Y. r& z: w4 z: E7 F
  5.         DMA_DeInit(DMA1_Channel5);
    ; V( }  ]& R1 E8 b
  6.         DMA_Cmd(DMA1_Channel5, DISABLE);$ I: D* _+ P: Q2 l0 J* `: y
  7.         DMA_InitStructure.DMA_PeripheralBaseAddr         = (uint32_t)&(USART2->RDR);/* UART2接收数据地址 */4 |3 I6 I  a8 G5 {
  8.         DMA_InitStructure.DMA_MemoryBaseAddr                 = (uint32_t)mem_addr; /* 接收buf */8 p* f3 e; e; p7 ?8 w! z
  9.         DMA_InitStructure.DMA_DIR                                         = DMA_DIR_PeripheralSRC;         /* 传输方向:外设->内存 */; y9 p7 v( [: E9 s2 J) x
  10.         DMA_InitStructure.DMA_BufferSize                         = mem_size; /* 接收buf大小 */7 g- @3 h" e) e% D7 v0 l
  11.         DMA_InitStructure.DMA_PeripheralInc                 = DMA_PeripheralInc_Disable; 3 _0 d5 S% V6 X8 R
  12.         DMA_InitStructure.DMA_MemoryInc                         = DMA_MemoryInc_Enable; " ^( R: d# J) i0 q
  13.         DMA_InitStructure.DMA_PeripheralDataSize         = DMA_PeripheralDataSize_Byte;
    8 C3 h4 r$ R4 D! r* a' A
  14.         DMA_InitStructure.DMA_MemoryDataSize                 = DMA_MemoryDataSize_Byte;
    ( o$ G2 c+ p6 l$ t) _# t  {
  15.         DMA_InitStructure.DMA_Mode                                         = DMA_Mode_Circular; /* 连续模式 */
    * r7 J, ?$ s! v6 G8 k# h6 W0 R" n
  16.         DMA_InitStructure.DMA_Priority                                 = DMA_Priority_VeryHigh;
    ) Z, U. y: X) Q3 v- ^) r& N
  17.         DMA_InitStructure.DMA_M2M                                         = DMA_M2M_Disable;
    : s6 X& [+ I4 n4 _8 D
  18.         DMA_Init(DMA1_Channel5, &DMA_InitStructure); $ V6 L' Y& ^8 p0 ]0 \" a. ?0 r0 o
  19.         DMA_ITConfig(DMA1_Channel5, DMA_IT_TC|DMA_IT_HT|DMA_IT_TE, ENABLE);/* 使能DMA半满、溢满、错误中断 */
    4 r/ H( h3 }+ h9 I! ?: q2 v/ H
  20.         DMA_ClearFlag(DMA1_IT_TC5);        /* 清除相关状态标识 */
    , ^+ Y0 s8 {# {
  21.         DMA_ClearFlag(DMA1_IT_HT5);
    - k3 y0 f* O4 s- S
  22.         DMA_Cmd(DMA1_Channel5, ENABLE);
    3 k. @) a3 K( ?# K+ ~9 _
  23. }
复制代码
1 G: R* n- G4 o/ h8 P4 [
3 c& `5 O: s/ |! o2 A! U/ E5 p

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

0 u0 E9 e# n; {# W5 c5 e
5.3 接收处理

& S5 W6 e; }* D. U# u; {

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

  • DMA通道buf溢满(传输完成)场景
  • DMA通道buf半满场景
  • 串口空闲中断场景5 b, x) n+ I4 Z6 O' |2 d- d+ `
    $ x# Z& o' [+ o# M6 J) ?. R: X) ], B( E

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


& o8 c2 x1 ~! r! b 5.3 .1 接收数据大小
/ [' [( u, [& D

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

  • 数据刚好是DMA接收buf的整数倍,这是理想的状态
  • 数据量小于DMA接收buf或者小于接收buf的一半,此时会触发串口空闲中断  r. I, _) B: g7 `, y4 r; D
    9 i' y7 b0 u- w; a4 l, S+ C

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

  1. /* 获取DMA通道接收buf剩余空间大小 */, y+ i- A. g( {3 p! ?$ o* t# m
  2. uint16_t DMA_GetCurrDataCounter(DMA_Channel_TypeDef* DMAy_Channelx);
复制代码
% J2 [# _! f+ E

DMA通道buf溢满场景计算


1 n' A, M, t' f* r* c

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

( e# A! H! e0 u: i! t3 P- x1 z3 d6 X. [4 l% E6 p0 I

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

  1. void uart_dmarx_done_isr(uint8_t uart_id)2 ^6 ]; r5 T. a( F/ ]- W7 S
  2. {
    7 Q1 \! G7 |6 n! T
  3.           uint16_t recv_size;
    ( @& J! }  ^) G6 M0 X2 ~
  4.         9 Y9 Y. c+ B5 ]" W
  5.         recv_size = s_uart_dev[uart_id].dmarx_buf_size - s_uart_dev[uart_id].last_dmarx_size;
    1 D$ a) W. H) T6 Q2 C4 U( F
  6. - F) r* {% w* k1 F1 A# E) P9 D3 f4 n
  7.         fifo_write(&s_uart_dev[uart_id].rx_fifo, : N% s: i1 J% C/ h4 u" B
  8.                                    (const uint8_t *)&(s_uart_dev[uart_id].dmarx_buf[s_uart_dev[uart_id].last_dmarx_size]), recv_size);# ^; R3 \( k) w
  9. % ^3 s. k6 P- @  }
  10.         s_uart_dev[uart_id].last_dmarx_size = 0;
    ! D* _+ }% D, c  b5 s  ?/ {
  11. }
复制代码
# M2 _/ j1 i; M2 m- {/ h

DMA通道buf半满场景计算

! t: N+ V5 j4 w

  1. 接收数据大小 = DMA通道接收总数据大小 - 上一次接收的总数据大小, v  B! ~4 }; i- o
  2. DMA通道接收总数据大小 = DMA通道buf大小 - DMA通道buf剩余空间大小
复制代码

( ^# Y1 w% B0 V+ \, V/ p

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


$ w7 Y4 }& |  H% X5 K

  1. void uart_dmarx_half_done_isr(uint8_t uart_id)1 b$ B8 d, ~8 y8 r+ X  K- a
  2. {
    ! A( i" {; R; }
  3.           uint16_t recv_total_size;
    ! v! u. f1 ^6 h0 Q2 X
  4.           uint16_t recv_size;
    + z. c# I2 T- {6 a) a- C6 ~, l# [
  5.         3 J) m7 J* C8 J0 D6 k7 M' s
  6.         if(uart_id == 0)/ V7 s4 G, Q& ?% N3 c$ p6 x
  7.         {
    6 W/ U" C9 w) `; `' A+ r
  8.                   recv_total_size = s_uart_dev[uart_id].dmarx_buf_size - bsp_uart1_get_dmarx_buf_remain_size();
    0 U/ m3 h3 x+ b( r
  9.         }7 N% B* `1 }, r8 `
  10.         else if (uart_id == 1)0 O6 c( k# x* i" h
  11.         {+ R/ t/ y) e) T5 N5 y) n) e( D' x
  12.                 recv_total_size = s_uart_dev[uart_id].dmarx_buf_size - bsp_uart2_get_dmarx_buf_remain_size();* b( v( z0 m; P$ ?+ U% y
  13.         }) _0 a, I* y" Y9 C$ b  h  y
  14.         recv_size = recv_total_size - s_uart_dev[uart_id].last_dmarx_size;
    ! y  N6 x8 L( K# O/ c  H
  15.        
    ; F$ g; U) K# g* f7 w
  16.         fifo_write(&s_uart_dev[uart_id].rx_fifo, 8 H/ z9 |5 k* B1 P
  17.                                    (const uint8_t *)&(s_uart_dev[uart_id].dmarx_buf[s_uart_dev[uart_id].last_dmarx_size]), recv_size);
    2 y3 n3 i; f' E
  18.         s_uart_dev[uart_id].last_dmarx_size = recv_total_size;/* 记录接收总数据大小 */
    ) d- r6 i& O( y: z
  19. }
复制代码
4 v5 ?) i  p+ c% R

+ Q/ n, G' `, K5 v

串口空闲中断场景计算


- |/ ~- x5 _3 h9 Q* I

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

串口空闲中断处理函数:

注:
! |/ t) v4 e9 J# l* g3 {9 E串口空闲中断处理函数,除了将数据拷贝到串口接收fifo中,还可以增加特殊处理,如作为串口数据传输完成标识、不定长度数据处理等等。

$ D7 x# c& z. _. J
5.3.2 接收数据偏移地址
1 a! i5 H5 M6 w* `/ [6 l) v

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


9 A/ K: n) Y( d9 t5 |

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


9 ?- Y$ N9 G6 q( c5 f

  1. void uart_dmarx_done_isr(uint8_t uart_id)
    7 B8 e" O+ g1 F# r; S
  2. {7 h2 G8 c! ?/ r; A' g. E! I
  3.         /* todo *// E6 g8 z& r# b/ s0 r8 z2 ~9 g; |
  4.         s_uart_dev[uart_id].last_dmarx_size = 0;
    . d6 H- J9 Q- N  V( n) A( }2 V
  5. }
复制代码

) k# c3 |, b" c5 z3 o" a
; O+ E  Q! r$ I  }6 ~# w! Y5.4 应用读取串口数据方法

5 Z3 m: E# t& o" _2 u

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


5 n7 m* S# _' d: s$ g6 串口DMA发送
; H2 e+ J; H# z# L( p( Q

5 l5 i: s2 X6 J5.1 基本流程
3 N, K# L: o7 _5 J! Q" x/ u

6.png


5 q! ?6 |0 J4 Q+ z8 B3 w- u
* {* c: [3 k+ ?. _# s' y- Z
串口发送流程图
% X) X) X3 u$ _. r" L9 Q5 i
5 b6 y3 P9 K0 i# ?9 q  w/ ^
* B1 {5 S/ A  T; X2 F' s3 I  n2 j( ^
5.2 相关配置
# Y$ V+ K- O, B, i5 s

关键步骤


9 C, [; |* }. g8 M1 `

【1】初始化串口

4 {- F' u" Q* }/ Q

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

6 N0 b9 z0 O9 ^1 o  J& y+ ?

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

/ @* o+ y4 w# h( W

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


$ X  b- G' v1 r' f6 m

  • 串口发送是,DMA通道工作模式设为单次模式(正常模式),每次需要发送数据时重新配置DMA
  • 使能DMA通道传输完成中断,利用该中断信息处理一些必要的任务,如清空发送状态、启动下一次传输
  • 启动DMA通道前清空相关状态标识,防止首次传输错乱数据
      j9 h9 m* n2 A) ^$ P% L
    7 e0 F$ v7 x& U3 ^! M/ M

  • % _1 E7 R, V" |. r" t0 P% d
    . X! H/ M: s( W6 u1 {2 O2 n- o# q1 `
7 x2 L) |0 I$ }% g# ?' ?
5.3 发送处理

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

串口发送处理函数:

" d7 D2 K& ^. ^- L

  1. void uart_poll_dma_tx(uint8_t uart_id)
    $ C/ m1 c( \$ }3 x
  2. {' ~) S5 y$ r" r7 A5 r
  3.           uint16_t size = 0;" W% a3 ]3 ]& C* v6 \) b
  4.        
    . L5 E. y6 ]6 v
  5.         if (0x01 == s_uart_dev[uart_id].status)
    7 X5 Z1 J6 C1 p1 Q& p4 a, T3 ]
  6.     {3 I0 K' Z, i  j8 t9 l0 ?
  7.         return;! r9 l* P& P4 a0 }( Z
  8.     }& H. v% {9 p/ c0 R
  9.         size = fifo_read(&s_uart_dev[uart_id].tx_fifo, s_uart_dev[uart_id].dmatx_buf,0 U( d9 f9 q8 q6 @  M
  10.                                          s_uart_dev[uart_id].dmatx_buf_size);% m8 k  i/ U: P( m0 i' Z
  11.         if (size != 0): d6 n+ a' p" G# u: x8 m
  12.         {
    , O& l- ~9 O% D0 [" L
  13.         s_UartTxRxCount[uart_id*2+0] += size;1 m) u* n5 b8 L7 t9 d
  14.                   if (uart_id == 0)
    : b0 R& t% ?- U8 b, |4 i( E, U1 ^* w% v
  15.                 {
    ( U; L: y/ m3 |7 G9 y8 e
  16.             s_uart_dev[uart_id].status = 0x01;        /* DMA发送状态 */
    7 q) M+ n) |$ d; I
  17.                           bsp_uart1_dmatx_config(s_uart_dev[uart_id].dmatx_buf, size);1 x8 }. p/ r2 c' \; ~. h  L9 z* L$ {
  18.                 }* C) e$ i0 U7 r1 R9 k% h
  19.                 else if (uart_id == 1)
    . \' E$ Z* e9 ?% e
  20.                 {
    5 z  H/ e- b8 R8 k
  21.             s_uart_dev[uart_id].status = 0x01;        /* DMA发送状态,必须在使能DMA传输前置位,否则有可能DMA已经传输并进入中断 */
    ' Y0 q/ V5 l% u( d* ^$ c
  22.                         bsp_uart2_dmatx_config(s_uart_dev[uart_id].dmatx_buf, size);4 P+ a6 ~- I$ _6 n  Q! r
  23.                 }
    8 f; s! {- y5 D' j
  24.         }) e1 W* |9 {- k" ?' p+ a+ S
  25. }
复制代码

3 |+ o- C9 r2 ^" i" q" Z6 V

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

1 k+ n% ]7 E) Z3 J' n


$ E. J. Q& Z- n3 w& k1 U; E

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

6 X" I0 B0 c0 V6 U1 r

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

  1. void uart_dmatx_done_isr(uint8_t uart_id)" a0 q  }7 ?/ @' x# W" o, H
  2. {" O  x7 g" _8 Q+ r- I7 X3 [
  3.         s_uart_dev[uart_id].status = 0;        /* 清空DMA发送状态标识 */
    6 ?* I/ l% B3 C4 ?5 a: u5 d; Y# p# k: V
  4. }
复制代码
8 \6 Y; P( H- g: o' {, E5 R. O6 n


+ k3 U( o2 F9 I. D1 ~. W! y


/ u, [: e$ x4 q  U- k0 w' @4 h

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


/ M! r* y- m: S5 {; B( O+ V

$ L* B( E4 W% a

  • 主线程任务调用,前提是线程不能被其他任务阻塞,否则导致fifo溢出5 z" y0 T3 n1 ]: k

    : q. B: c5 L# }6 P# o& b- b
  1. void thread(void)
    : S; Y# I3 P) R  W! F
  2. {
    * q* K6 \7 F8 v1 _
  3.     uart_dmatx_done_isr(DEV_UART1);/ \. f) k3 L* q( u) e( p
  4.     uart_dmatx_done_isr(DEV_UART2);; K6 c3 Z: F$ x! U9 q9 z  j
  5. }
复制代码
+ p  Z0 O, P! p0 f9 x/ L

' C" a+ @; e- e- s8 Q
  • 定时器中断中调用+ Y3 E3 Q: c' H; M# @3 Z
  1. void TIMx_IRQHandler(void)2 q- L# W3 U2 @5 ?8 q" ?% q
  2. {
    ; G% p: y; N8 v2 j1 u/ `+ q4 M
  3.     uart_dmatx_done_isr(DEV_UART1);% ^- h2 ?) c4 F# U, ~7 s( @2 E+ P
  4.     uart_dmatx_done_isr(DEV_UART2);" b3 K+ Q0 [' V" k
  5. }
复制代码
0 S+ v& ]* {* L/ R" h7 ~
* u# Q9 U, h3 p# c$ U. B/ N- E  I
  • DMA通道传输完成中断中调用& n" y, |( F  {" j
    - D5 v/ B- q) w. T3 t: P* J, p% s
  1. void DMA1_Channel4_5_IRQHandler(void)
    3 X- z3 J( h) a6 Q& x
  2. {
    6 b3 R, y6 U7 z( o/ b3 y
  3.         if(DMA_GetITStatus(DMA1_IT_TC4))" Z5 ~; ?( X" I/ `- O( ^
  4.         {
    # I8 I+ g/ j; R9 o8 W* @3 Y2 w
  5.                 UartDmaSendDoneIsr(UART_2);. T, z) [) W# _& R; \. L
  6.                 DMA_ClearFlag(DMA1_FLAG_TC4);. {; p/ y# O& G
  7.                 uart_dmatx_done_isr(DEV_UART2);
    6 D" U' Z  i- X
  8.         }
    , d; U' @, u1 |) E8 F( N
  9. }
复制代码

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

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

  • 周期查询发送fifo数据,启动DMA传输,充分利用DMA发送效率,但可能降低串口数据流实时性
  • 实时查询发送fifo数据,加上超时处理,理想的方法
  • 在DMA传输完成中断中处理,保证实时连续数据流/ N2 v6 P/ {6 ~: n* I
    1 `( w0 ^* B0 f* |7 d9 Q' d

; y2 \% W, H3 A  m/ y0 B8 C/ R6 串口设备
, B% w* w. Y. w; P% Z. [' N

1 u. W! A  l9 G" d6 U6.1 数据结构! M0 b" d+ j1 ~- G2 u0 k
  1. /* 串口设备数据结构 */( A$ c2 [8 B, B; o$ v
  2. typedef struct7 B& W: h# K8 U( C  O8 a2 w9 |
  3. {0 I; ^- a5 `( I0 q
  4.         uint8_t status;                        /* 发送状态 */$ x/ g9 n( H/ @* A
  5.         _fifo_t tx_fifo;                /* 发送fifo */
    6 }4 m/ ?. b0 f! |# k! G+ D! T
  6.         _fifo_t rx_fifo;                /* 接收fifo */$ c% E2 _* d/ M5 V
  7.         uint8_t *dmarx_buf;                /* dma接收缓存 */
    + r. f3 R9 R( q6 X9 r: f8 s
  8.         uint16_t dmarx_buf_size;/* dma接收缓存大小*/
    & f9 W# B2 Y* S  N3 q9 v/ G
  9.         uint8_t *dmatx_buf;                /* dma发送缓存 */2 ]$ t7 p) e; \2 s- I4 ?# A( p
  10.         uint16_t dmatx_buf_size;/* dma发送缓存大小 */
    . Q# P( L% |! d+ J# e4 ^
  11.         uint16_t last_dmarx_size;/* dma上一次接收数据大小 */
    9 W7 m+ h' @6 z
  12. }uart_device_t;
复制代码

2 p1 T/ Z  B" d# e/ [( t6 g' @6 _: N6 o
6.2 对外接口
. A) q, b5 ?, @0 \1 H

3 N  n  {7 ?" Y
  1. /* 串口注册初始化函数 */
    % A9 P5 Q3 t! E2 v
  2. void uart_device_init(uint8_t uart_id)
    ( S  `9 w% U3 A7 r$ T% u* O) D' z
  3. {
    7 y$ D  J9 B% \5 e& z! r/ A
  4.           if (uart_id == 1)3 n4 @7 g+ ^1 B6 e7 i4 M; v. ]
  5.         {
    + L" C6 K4 t' u: ?* c
  6.                 /* 配置串口2收发fifo */
    1 f  ]  Z" c1 Q- b6 I9 _4 f. X
  7.                 fifo_register(&s_uart_dev[uart_id].tx_fifo, &s_uart2_tx_buf[0],
    8 E0 ]" a% B% ^
  8.                       sizeof(s_uart2_tx_buf), fifo_lock, fifo_unlock);$ n3 v1 @3 ^) T6 Y' L& ^
  9.                 fifo_register(&s_uart_dev[uart_id].rx_fifo, &s_uart2_rx_buf[0],
    3 p" C& @0 {4 z7 K
  10.                       sizeof(s_uart2_rx_buf), fifo_lock, fifo_unlock);% p4 L' I4 [* B- n; ?/ [, ~
  11.                 ) b7 p4 U4 j" f7 [8 o/ `8 B
  12.                 /* 配置串口2 DMA收发buf */
    ; k0 n- {& ~1 D4 d0 Z
  13.                 s_uart_dev[uart_id].dmarx_buf = &s_uart2_dmarx_buf[0];: {; _2 Z& L: _( i1 L" M; h
  14.                 s_uart_dev[uart_id].dmarx_buf_size = sizeof(s_uart2_dmarx_buf);
    * i# k5 a+ F8 h# s
  15.                 s_uart_dev[uart_id].dmatx_buf = &s_uart2_dmatx_buf[0];* k1 E' w9 z7 b- a
  16.                 s_uart_dev[uart_id].dmatx_buf_size = sizeof(s_uart2_dmatx_buf);
    6 T4 P0 D( g2 S# P! T
  17.                 bsp_uart2_dmarx_config(s_uart_dev[uart_id].dmarx_buf, " X, W5 ?! J4 q: \* d- x
  18.                                                            sizeof(s_uart2_dmarx_buf));7 X( n# c6 r1 T. o5 {. @
  19.                 s_uart_dev[uart_id].status  = 0;
    + b' ]8 i! {/ x8 e( @1 ]
  20.         }: B4 T4 j% {- ~9 b4 G
  21. }  e+ R0 \2 u6 Z9 E( n  y8 k
  22. 4 K  h" N6 n, X7 }* H$ ?4 f
  23. /* 串口发送函数 */
    # I* z/ S7 k/ u& Y6 H+ w( C  \
  24. uint16_t uart_write(uint8_t uart_id, const uint8_t *buf, uint16_t size)
    7 i7 w, H% {6 d: D
  25. {/ u! N- t2 ~% F0 @8 q; [4 U# t
  26.         return fifo_write(&s_uart_dev[uart_id].tx_fifo, buf, size);
    1 Q1 I  p2 x; m4 O
  27. }
    : [& H$ Q. ?! x; R( A+ i
  28. 9 D$ l5 W1 f! E+ H
  29. /* 串口读取函数 */3 A) V1 d! f0 `! \
  30. uint16_t uart_read(uint8_t uart_id, uint8_t *buf, uint16_t size)
    0 o$ ?/ o3 M" ^0 H% p% y4 m3 s
  31. {- F7 A9 P/ M# u+ c4 g
  32.         return fifo_read(&s_uart_dev[uart_id].rx_fifo, buf, size);2 q5 W% Z5 z. m; j# a
  33. }
复制代码

: A6 m9 J2 |7 E/ p" S6 G8 完整源码3 m4 V2 W7 ?& G0 F- s5 [# @- Y

; M9 [0 h+ K' H5 w
+ l3 B5 w# n0 J8 f3 I5 @

串口&DMA底层配置:


9 `, O+ e) L8 j7 B5 s4 f

  1. #include <stddef.h>+ a* V  z- d+ @( A
  2. #include <stdint.h>
    $ I3 q' j" N6 ?5 X' [
  3. #include <stdbool.h>
    5 G3 C$ U# o. s( a0 R
  4. #include "stm32f0xx.h"% g; C3 }( \, H; Q5 ^* M2 {  v1 _% |
  5. #include "bsp_uart.h"* E/ \2 u8 a0 g& l) u
  6.   ~: |) _3 u( e9 g, _. [0 w
  7. /**
    $ f1 T* t1 K% k' F
  8. * @brief  
    5 `( T- o% J, Y; r" h$ S/ C
  9. * @param  
    9 c( r" j9 w& v2 M# M. c
  10. * @retval / J/ D8 _, Q: |5 O5 C# G
  11. */
    ! a* E9 M% e) Q& O) i6 Z: Q# Z- k7 U
  12. static void bsp_uart1_gpio_init(void)! w- @+ ]& c2 ^
  13. {
    $ ?0 o* D( t7 V6 l* Z! C1 @7 T5 V
  14.     GPIO_InitTypeDef    GPIO_InitStructure;1 @, V6 C, {  e3 I* B
  15. #if 0
    0 R3 O: Q5 i- M+ c8 t
  16.         RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOB, ENABLE);4 h) n" [, d2 x% G
  17.         ! b' z( x1 x5 }: P+ E8 Y. m" P3 E9 y
  18.     GPIO_PinAFConfig(GPIOB, GPIO_PinSource6, GPIO_AF_0);1 x  \+ T  n7 A5 B% P
  19.     GPIO_PinAFConfig(GPIOB, GPIO_PinSource7, GPIO_AF_0);   i1 ^  o( d6 h5 f* c3 r
  20.         % p3 [8 V' p7 D" e- I; ^0 c
  21.         GPIO_InitStructure.GPIO_Pin         = GPIO_Pin_6 | GPIO_Pin_7;, n! F1 M( P% W
  22.     GPIO_InitStructure.GPIO_Mode         = GPIO_Mode_AF;
    7 a1 C# ~+ A6 H/ w/ h7 B6 h% @
  23.         GPIO_InitStructure.GPIO_OType         = GPIO_OType_PP;
    + j' F2 ~/ h! E3 r4 f$ r
  24.     GPIO_InitStructure.GPIO_Speed          = GPIO_Speed_Level_3;
    , \5 A" s! \+ M  e! o5 |8 p  W
  25.     GPIO_InitStructure.GPIO_PuPd         = GPIO_PuPd_UP;
    * y+ o6 a+ w1 H4 T
  26.     GPIO_Init(GPIOB, &GPIO_InitStructure);0 m, x4 e$ h$ G- n$ W; a3 b8 T! Q+ W
  27. #else
    7 T1 N  D; s/ _- {1 ^% d
  28.         RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOA, ENABLE);3 h! N" \/ L, u& W0 g) h
  29.        
    7 w; B/ j- B' p2 V- g1 n' a
  30.     GPIO_PinAFConfig(GPIOB, GPIO_PinSource9, GPIO_AF_1);( X4 ]0 C) V5 Y
  31.     GPIO_PinAFConfig(GPIOB, GPIO_PinSource10, GPIO_AF_1);
    5 t) H- Y# T3 ?* U) n) B* {/ @
  32.        
    9 r0 n2 R1 _0 v- o9 I
  33.         GPIO_InitStructure.GPIO_Pin         = GPIO_Pin_9 | GPIO_Pin_10;1 r% s" S* P" V- j: Q: D% M
  34.     GPIO_InitStructure.GPIO_Mode         = GPIO_Mode_AF;5 Z9 U9 \6 v" ~# C) F
  35.         GPIO_InitStructure.GPIO_OType         = GPIO_OType_PP;
    " x* g8 ?# a# I! D6 _
  36.     GPIO_InitStructure.GPIO_Speed          = GPIO_Speed_Level_3;
    1 I) z" a3 ~( Z2 t5 q4 y
  37.     GPIO_InitStructure.GPIO_PuPd         = GPIO_PuPd_UP;2 N# E" R/ U. L/ Z2 o9 y; Y
  38.     GPIO_Init(GPIOA, &GPIO_InitStructure);% f4 v2 p  P, N) W3 Z& W. ~
  39. #endif$ q( Q* u. S4 _! M& `% ]
  40. }
    2 Z0 A6 O; P* I, y

  41. , [. s/ ~( ]' H/ _- p
  42. /**
    , n) n' Z4 h1 G9 u4 S2 T
  43. * @brief  # g/ o0 J7 B1 S6 S: l6 w! s
  44. * @param  
    # \8 h% Z- U, x
  45. * @retval 1 b! C/ g1 F# D; V% B
  46. */$ X4 B* f8 w0 d+ G* d3 h, p! T/ e2 Z
  47. static void bsp_uart2_gpio_init(void)8 O) n5 S$ T# J' r% G( ]
  48. {
    1 V: X; F& Q  F+ Y/ u. t
  49.         GPIO_InitTypeDef GPIO_InitStructure;
    7 s  j- a8 X3 D
  50.        
    " P1 D* ]5 U5 R& B- `0 s
  51.         RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOB, ENABLE);
    ) Y: H" e/ P: _- v4 B% l
  52.        
    1 s' ^5 W( G& K8 _8 L% A
  53.         GPIO_PinAFConfig(GPIOA, GPIO_PinSource2, GPIO_AF_1);; }# S6 l1 c9 V" C; {
  54.         GPIO_PinAFConfig(GPIOA, GPIO_PinSource3, GPIO_AF_1);- C! V6 i5 W- m# n# t
  55.           T% ~. Y  d: k+ v+ J/ `
  56.         GPIO_InitStructure.GPIO_Pin   = GPIO_Pin_2 | GPIO_Pin_3;
    ! O6 a( g/ [$ F4 h+ i7 s
  57.         GPIO_InitStructure.GPIO_Mode  = GPIO_Mode_AF;
    ! I3 m( Y+ S' b9 @
  58.         GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;5 A9 P* ^4 s! m2 S1 i* {
  59.         GPIO_InitStructure.GPIO_Speed = GPIO_Speed_10MHz;) |8 B% r. [: V) [
  60.         GPIO_InitStructure.GPIO_PuPd  = GPIO_PuPd_UP;4 \0 m- `2 D. K+ t% {/ }) K( i" w
  61.         GPIO_Init(GPIOA, &GPIO_InitStructure);- |. Q1 P! h# ~- M8 N
  62. }
    3 Q+ b; P0 `2 P4 x
  63. 4 {8 y+ s, I3 @6 S/ f
  64. /**
    ' T+ R; [/ G( H
  65. * @brief  : @" o# S. v2 z
  66. * @param  - |2 a' k9 q) M1 q9 @: v6 s
  67. * @retval
      T' _* U2 Z( N
  68. */
    : f. n! D3 R. f$ t" d- l  l
  69. void bsp_uart1_init(void)1 {6 f$ W; Q+ E$ r
  70. {
    6 A1 c3 U+ [+ d* {+ z
  71.         USART_InitTypeDef USART_InitStructure;' y) y3 U; ~8 N5 _, m
  72.         NVIC_InitTypeDef NVIC_InitStructure;
    / d9 e6 I  ~% ?3 N% Y/ _
  73.        
    , O! i7 s, z3 R- Q
  74.         bsp_uart1_gpio_init();
    ( b' G9 x1 S5 x1 E
  75.         $ n; {1 i+ z* {1 d8 }) S# O* b
  76.         /* 使能串口和DMA时钟 */
    3 M: u6 {0 A5 E. r; k# T
  77.         RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);! v8 v0 G+ A4 v" p' _6 W+ ^
  78.         RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);
    8 I) B: Y. l$ ~5 G3 r9 p8 U
  79.        
    0 G- S: p9 {+ p
  80.         USART_InitStructure.USART_BaudRate            = 57600;0 O/ A1 d: ?. Z+ U0 F* D
  81.         USART_InitStructure.USART_WordLength          = USART_WordLength_8b;, p$ R2 e/ |# L5 K( Z# e
  82.         USART_InitStructure.USART_StopBits            = USART_StopBits_1;# Q2 g" D7 E- w  L9 h  }+ w/ p) D
  83.         USART_InitStructure.USART_Parity              = USART_Parity_No;0 ~" c( [$ c2 O' c0 E! T1 B9 f. u
  84.         USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
    ! L' B# j) O) t; |7 S
  85.         USART_InitStructure.USART_Mode                = USART_Mode_Rx | USART_Mode_Tx;. E; ]- p9 p- I7 ?  x+ k) A" [
  86.         USART_Init(USART1, &USART_InitStructure);; X4 Q9 [3 s- ]. }2 e( E. U& ^
  87.         1 f* h  M* j1 c
  88.         USART_ITConfig(USART1, USART_IT_IDLE, ENABLE);        /* 使能空闲中断 */
    $ _: V( t* e, }1 N
  89.         USART_OverrunDetectionConfig(USART1, USART_OVRDetection_Disable);
    , y" M8 ?  d9 ]" J! M9 |, `
  90.        
    : q. P6 R' d1 ?9 v
  91.         USART_Cmd(USART1, ENABLE);
    6 A; N3 c$ I7 X' i9 K% n  a
  92.         USART_DMACmd(USART1, USART_DMAReq_Rx|USART_DMAReq_Tx, ENABLE); /* 使能DMA收发 */5 z4 V# a; l0 l# [3 Q2 A; y/ w
  93. ' x7 L: N! I8 p7 ^, e$ g
  94.         /* 串口中断 */8 C- r  _, M/ _- k, }  {( ~! ?2 y
  95.         NVIC_InitStructure.NVIC_IRQChannel         = USART1_IRQn;
    ' F& w" m' q- U) V& i- }8 A4 R, n; N
  96.         NVIC_InitStructure.NVIC_IRQChannelPriority = 2;
    ) ~4 i! t5 [% Y4 u
  97.         NVIC_InitStructure.NVIC_IRQChannelCmd      = ENABLE;
    : ~3 {8 ^0 r0 U4 U) z
  98.         NVIC_Init(&NVIC_InitStructure);
    ; D0 a" l& i# v/ F# q: p
  99. ) k0 B8 b& F7 \3 G& f6 h, N3 z
  100.         /* DMA中断 */
    * f9 s+ _; G/ _, y+ d$ U* @3 ~
  101.           NVIC_InitStructure.NVIC_IRQChannel                    = DMA1_Channel2_3_IRQn;      
    # H3 h. R/ j# }- P
  102.           NVIC_InitStructure.NVIC_IRQChannelPriority = 0;
    * c- t" a+ f4 X, |* \" l" {! V
  103.         NVIC_InitStructure.NVIC_IRQChannelCmd      = ENABLE;
    * O) f* [8 g3 f  {$ S/ z1 m9 b
  104.           NVIC_Init(&NVIC_InitStructure);
    ! ]& F* i4 E! G8 f
  105. }4 m/ O0 H+ P6 x' c

  106. " V9 i9 {: T) v5 a
  107. /**# K) G. r, Y, z
  108. * @brief  
    : c2 j$ x1 c: t& I& S# Z& B2 h5 J
  109. * @param  6 N5 n+ f; V. }# `; E1 z8 I
  110. * @retval ' z6 ]. L0 N4 O7 \, q! L
  111. */
    - v( E4 ~; _* D$ E
  112. void bsp_uart2_init(void)8 Y6 @2 D% ]: d& T  J
  113. {- i" O$ C1 v1 j0 j" h3 }" a
  114.         USART_InitTypeDef USART_InitStructure;
    ' h+ @( w% d& I# K- \
  115.         NVIC_InitTypeDef NVIC_InitStructure;
    8 ?  Q8 R; r" D0 f3 m* b
  116.         % N) h& w& }4 r5 d' b
  117.         bsp_uart2_gpio_init();
    3 n8 x* {6 v/ P* e( K0 a
  118.        
    7 e8 t. Z9 s2 Q: p8 S
  119.         /* 使能串口和DMA时钟 */3 g9 n- {" c1 d% D
  120.         RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);
    . a  e& @2 F9 \
  121.         RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2, ENABLE);& r5 u2 w0 F5 A( F' t$ X& O
  122. # w& ^' w$ h" u3 E0 F. W7 N
  123.         USART_InitStructure.USART_BaudRate            = 57600;
    . s6 c/ `1 `) l3 W8 R# ~" n
  124.         USART_InitStructure.USART_WordLength          = USART_WordLength_8b;
    # l4 U& z8 Z: K5 m( ~/ F8 l
  125.         USART_InitStructure.USART_StopBits            = USART_StopBits_1;& v4 _7 b) ~% g
  126.         USART_InitStructure.USART_Parity              = USART_Parity_No;/ `" U! N/ }; [! H. |* {
  127.         USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
    9 O& L0 g- ~( X8 k- T
  128.         USART_InitStructure.USART_Mode                = USART_Mode_Rx | USART_Mode_Tx;0 f3 K# L: f4 I" v1 h, A
  129.         USART_Init(USART2, &USART_InitStructure);5 p  a, B+ V  ?
  130.         ( _3 u6 m. n; S  b& d: C3 c
  131.         USART_ITConfig(USART2, USART_IT_IDLE, ENABLE);        /* 使能空闲中断 */
    2 T9 Y$ ^* h7 {( B4 l- G# I' ~# C
  132.         USART_OverrunDetectionConfig(USART2, USART_OVRDetection_Disable);1 d9 M% B5 ?+ a6 p
  133.         ' u. u6 [, z) A8 P( o4 G* O. F
  134.         USART_Cmd(USART2, ENABLE);- L+ P- E5 _/ S8 j2 |
  135.         USART_DMACmd(USART2, USART_DMAReq_Rx|USART_DMAReq_Tx, ENABLE);         /* 使能DMA收发 */+ _0 n3 K; g% x- q3 L" f
  136. 0 W  M5 T/ M( W5 D" z
  137.         /* 串口中断 */
    ' R0 d; O! S( n4 I  Q- s. y6 d: B
  138.         NVIC_InitStructure.NVIC_IRQChannel         = USART2_IRQn;6 n6 l4 W& V1 i0 |/ K# K0 b8 ]: u# e  m
  139.         NVIC_InitStructure.NVIC_IRQChannelPriority = 2;
    - b7 S( `8 `& M3 g
  140.         NVIC_InitStructure.NVIC_IRQChannelCmd      = ENABLE;
    $ y. D9 e! B3 G4 N7 C  x2 Q
  141.         NVIC_Init(&NVIC_InitStructure);0 t0 ]' f, B& d

  142. % b" k2 v4 m$ N$ r5 p% @  ?9 w! x
  143.         /* DMA中断 */
    2 z- X8 t& ]& a; |
  144.         NVIC_InitStructure.NVIC_IRQChannel         = DMA1_Channel4_5_IRQn;       / j/ S$ x4 e& d. M  |% j5 n! h
  145.           NVIC_InitStructure.NVIC_IRQChannelPriority = 0; ' _: e- S$ H  F- \) w' o
  146.         NVIC_InitStructure.NVIC_IRQChannelCmd      = ENABLE;
    2 s6 r! N$ _1 \* T; u! @
  147.           NVIC_Init(&NVIC_InitStructure);
    & Z; a& ^. p* u1 u, t7 B2 C
  148. }6 H& `$ U% q& P- j7 P5 g4 K

  149. , j0 x( d* d0 `& c# q8 J! s
  150. void bsp_uart1_dmatx_config(uint8_t *mem_addr, uint32_t mem_size)% J0 \# `  o5 X8 K+ M
  151. {4 P( \9 U7 e6 y/ T
  152.           DMA_InitTypeDef DMA_InitStructure;- ?! S: M: w# p* J0 R
  153.         " u$ _; ^$ m5 G. M2 o
  154.         DMA_DeInit(DMA1_Channel2);5 O% l6 x7 H: D9 V( {
  155.         DMA_Cmd(DMA1_Channel2, DISABLE);- }2 X# W" w* \& H
  156.         DMA_InitStructure.DMA_PeripheralBaseAddr         = (uint32_t)&(USART1->TDR);
    ) r9 q8 y- z4 L. N7 W5 U
  157.         DMA_InitStructure.DMA_MemoryBaseAddr                 = (uint32_t)mem_addr;
    : D3 R# f' K- v, K% n, U5 J
  158.         DMA_InitStructure.DMA_DIR                                         = DMA_DIR_PeripheralDST;         /* 传输方向:内存->外设 */
    0 ]1 I' s  j! y9 x% h9 H# g0 E0 v
  159.         DMA_InitStructure.DMA_BufferSize                         = mem_size;
    - ~5 r" n9 S1 I' M
  160.         DMA_InitStructure.DMA_PeripheralInc                 = DMA_PeripheralInc_Disable; - t4 m; w9 J4 s7 }# ~7 R* ~, {3 Z, i
  161.         DMA_InitStructure.DMA_MemoryInc                         = DMA_MemoryInc_Enable; 9 v0 l" U& g# E; Y, l
  162.         DMA_InitStructure.DMA_PeripheralDataSize         = DMA_PeripheralDataSize_Byte; ( v  S5 M9 i8 q. @
  163.         DMA_InitStructure.DMA_MemoryDataSize                 = DMA_MemoryDataSize_Byte;
    ' ^0 M" b( e7 c" A6 Z1 N- k
  164.         DMA_InitStructure.DMA_Mode                                         = DMA_Mode_Normal; ! U0 F& K+ ^4 }' R, Z8 V
  165.         DMA_InitStructure.DMA_Priority                                 = DMA_Priority_High; 5 @0 s( V+ E* ]- j1 [& Y
  166.         DMA_InitStructure.DMA_M2M                                         = DMA_M2M_Disable;
    ' m# b1 ?8 l& U+ K) P
  167.         DMA_Init(DMA1_Channel2, &DMA_InitStructure);  
    8 H- S. ^9 S' \; x. h, }4 c' M1 c
  168.         DMA_ITConfig(DMA1_Channel2, DMA_IT_TC|DMA_IT_TE, ENABLE);
    ( a! Z- A) |7 v
  169.         DMA_ClearFlag(DMA1_IT_TC2);        /* 清除发送完成标识 */$ o! T% k+ r% `0 S3 x1 k; p8 s
  170.         DMA_Cmd(DMA1_Channel2, ENABLE); $ @$ r, [& @4 G8 C# r& T2 w# S
  171. }
    5 `9 T9 a" {* y1 p8 B5 s

  172. 1 j* s. Q  i. W5 {- Y7 T
  173. void bsp_uart1_dmarx_config(uint8_t *mem_addr, uint32_t mem_size)7 Q9 S8 ]7 H; l0 Q; A3 x; C
  174. {: d9 ^" G" L1 y" y* ~5 D* q$ f9 e
  175.           DMA_InitTypeDef DMA_InitStructure;
    7 m/ |" {* E1 o
  176.        
    ( x, m- X- p" [* k' _/ ^; o+ Y- x
  177.         DMA_DeInit(DMA1_Channel3);
    # n5 C4 y4 \0 t/ N% f/ J
  178.         DMA_Cmd(DMA1_Channel3, DISABLE);
    % k( P# J; @* k% T% A; S
  179.         DMA_InitStructure.DMA_PeripheralBaseAddr         = (uint32_t)&(USART1->RDR);
    8 ^4 `% P. m+ E- `3 _* |0 H$ {
  180.         DMA_InitStructure.DMA_MemoryBaseAddr                 = (uint32_t)mem_addr; " ?7 |0 n2 J. L& K% n" {
  181.         DMA_InitStructure.DMA_DIR                                         = DMA_DIR_PeripheralSRC;         /* 传输方向:外设->内存 */+ X5 _+ R4 i5 ]
  182.         DMA_InitStructure.DMA_BufferSize                         = mem_size;
    1 ]* ?' Y9 K1 q$ [9 R7 b9 M# S
  183.         DMA_InitStructure.DMA_PeripheralInc                 = DMA_PeripheralInc_Disable; - ~" s; |% t5 G1 l
  184.         DMA_InitStructure.DMA_MemoryInc                         = DMA_MemoryInc_Enable;
    ( h3 w4 R6 P) D- q
  185.         DMA_InitStructure.DMA_PeripheralDataSize         = DMA_PeripheralDataSize_Byte;
    % _' y1 X% m  u7 a) Y* I0 i
  186.         DMA_InitStructure.DMA_MemoryDataSize                 = DMA_MemoryDataSize_Byte;- a4 l, e( |: M. {' j1 \: g# I
  187.         DMA_InitStructure.DMA_Mode                                         = DMA_Mode_Circular; 5 ]( b2 |/ G' r# l
  188.         DMA_InitStructure.DMA_Priority                                 = DMA_Priority_VeryHigh; + T( `" M/ E' i" v2 g% a
  189.         DMA_InitStructure.DMA_M2M                                         = DMA_M2M_Disable;
    + m. o) }0 o3 V9 t- `! `8 f
  190.         DMA_Init(DMA1_Channel3, &DMA_InitStructure);
    ( }3 A+ S: ~( \1 ?+ F/ s, |& Z  E- L
  191.         DMA_ITConfig(DMA1_Channel3, DMA_IT_TC|DMA_IT_HT|DMA_IT_TE, ENABLE);/* 使能DMA半满、全满、错误中断 */
    " p# u% S7 O: b
  192.         DMA_ClearFlag(DMA1_IT_TC3);; j1 U0 N( i, G+ ^7 P
  193.         DMA_ClearFlag(DMA1_IT_HT3);
    ' H( c5 c* `) ^6 J$ T
  194.         DMA_Cmd(DMA1_Channel3, ENABLE); ( z! R# u  V6 z2 {* |  i7 D
  195. }
    9 X$ p& Y5 e7 e
  196. 3 g+ F. ?) v+ J8 b
  197. uint16_t bsp_uart1_get_dmarx_buf_remain_size(void)
    , j$ b) i- T. i% N6 \3 g' j
  198. {( K5 L& f( @+ t1 N& l, d
  199.         return DMA_GetCurrDataCounter(DMA1_Channel3);        /* 获取DMA接收buf剩余空间 */
    0 N1 n* F' T. I0 C7 b8 \. r
  200. }" k; @. a) N$ r9 w) h
  201. . C. L8 A3 Q7 E
  202. void bsp_uart2_dmatx_config(uint8_t *mem_addr, uint32_t mem_size); I. d* N6 N% p3 [1 u5 R
  203. {0 o) [, T% y6 r1 N
  204.           DMA_InitTypeDef DMA_InitStructure;
    & j2 x! ~( x" y7 n- {' Y, c
  205.         ; Q3 ^, m- I9 K8 I8 }" O
  206.         DMA_DeInit(DMA1_Channel4);( M5 \6 {& O1 `2 o7 T
  207.         DMA_Cmd(DMA1_Channel4, DISABLE);7 a: `0 s8 u% l0 U1 N) r" `
  208.         DMA_InitStructure.DMA_PeripheralBaseAddr         = (uint32_t)&(USART2->TDR);
    ( H1 T8 q9 p; M0 z# x0 x7 M
  209.         DMA_InitStructure.DMA_MemoryBaseAddr                 = (uint32_t)mem_addr; * }' g/ H7 d, i+ G
  210.         DMA_InitStructure.DMA_DIR                                         = DMA_DIR_PeripheralDST;         /* 传输方向:内存->外设 */  d% K$ Z0 T( g" T* u
  211.         DMA_InitStructure.DMA_BufferSize                         = mem_size; ; Z5 [% l1 W% e
  212.         DMA_InitStructure.DMA_PeripheralInc                 = DMA_PeripheralInc_Disable; 9 N& m2 f+ q& X) r) E
  213.         DMA_InitStructure.DMA_MemoryInc                         = DMA_MemoryInc_Enable;
    1 y4 D( R6 B. u0 q8 k' Y
  214.         DMA_InitStructure.DMA_PeripheralDataSize         = DMA_PeripheralDataSize_Byte; ' @  x' u. |" b% L: O  c8 L
  215.         DMA_InitStructure.DMA_MemoryDataSize                 = DMA_MemoryDataSize_Byte;
    % }, v' M! J# p
  216.         DMA_InitStructure.DMA_Mode                                         = DMA_Mode_Normal;
    6 [( m8 G# n& P' G: L
  217.         DMA_InitStructure.DMA_Priority                                 = DMA_Priority_High; / o& H, e3 p5 l
  218.         DMA_InitStructure.DMA_M2M                                         = DMA_M2M_Disable;
    4 N# ]  [! A& ~7 c: L$ a9 m
  219.         DMA_Init(DMA1_Channel4, &DMA_InitStructure);  0 f" j# e$ g+ [8 T. [. i
  220.         DMA_ITConfig(DMA1_Channel4, DMA_IT_TC|DMA_IT_TE, ENABLE);
    8 m: C  D8 J2 y  s2 e) |* Z7 {; v; |
  221.         DMA_ClearFlag(DMA1_IT_TC4);        /* 清除发送完成标识 */
    * x# j# D& i" ?" n8 i+ v% T6 ?9 M# u, S
  222.         DMA_Cmd(DMA1_Channel4, ENABLE);
    + B' {; q/ l1 C  x1 ^2 b
  223. }
    * Y$ I) `7 }; z2 y# H$ Z3 T. C

  224. , P2 y$ k# T7 Z7 }
  225. void bsp_uart2_dmarx_config(uint8_t *mem_addr, uint32_t mem_size)
      v. q7 H4 I3 s6 S$ ^5 [
  226. {$ l$ }3 y# C& I( K
  227.           DMA_InitTypeDef DMA_InitStructure;
    , F0 {' X) W8 N3 K/ C! l
  228.         0 s+ Y+ B! s; d+ ?( a! [
  229.         DMA_DeInit(DMA1_Channel5);
    % Y8 F5 ~3 f+ c, }$ g/ y
  230.         DMA_Cmd(DMA1_Channel5, DISABLE);
    , w& e4 s! s1 |; O8 a5 A
  231.         DMA_InitStructure.DMA_PeripheralBaseAddr         = (uint32_t)&(USART2->RDR);
    7 n5 P' K4 l: A- @  I- f
  232.         DMA_InitStructure.DMA_MemoryBaseAddr                 = (uint32_t)mem_addr;
    , M. ^- H) s  @$ E+ i/ l3 R* X8 D
  233.         DMA_InitStructure.DMA_DIR                                         = DMA_DIR_PeripheralSRC;         /* 传输方向:外设->内存 */
    # H/ C* ~5 S% L% E
  234.         DMA_InitStructure.DMA_BufferSize                         = mem_size; - `2 \4 y" c- c7 \- `8 P- w
  235.         DMA_InitStructure.DMA_PeripheralInc                 = DMA_PeripheralInc_Disable;
    ! D% s+ [0 _: ~' `5 H; I
  236.         DMA_InitStructure.DMA_MemoryInc                         = DMA_MemoryInc_Enable;
    2 p% \. \1 E9 @4 l( ]
  237.         DMA_InitStructure.DMA_PeripheralDataSize         = DMA_PeripheralDataSize_Byte;
    9 Z9 g; U9 ^# w& \$ n- ?
  238.         DMA_InitStructure.DMA_MemoryDataSize                 = DMA_MemoryDataSize_Byte;( E8 C8 D5 t3 P0 u0 Q% j
  239.         DMA_InitStructure.DMA_Mode                                         = DMA_Mode_Circular;
    ; P: G  D3 C; _8 |3 v
  240.         DMA_InitStructure.DMA_Priority                                 = DMA_Priority_VeryHigh; 0 m# e$ f& C( ?( `7 S, V# e1 |
  241.         DMA_InitStructure.DMA_M2M                                         = DMA_M2M_Disable;
    / v! K, s" H2 j9 b) r" x7 k5 C
  242.         DMA_Init(DMA1_Channel5, &DMA_InitStructure); 5 t3 J1 p" u' S( C0 z/ ^7 j+ S
  243.         DMA_ITConfig(DMA1_Channel5, DMA_IT_TC|DMA_IT_HT|DMA_IT_TE, ENABLE);/* 使能DMA半满、全满、错误中断 */
    $ V, m& D, g4 {" I
  244.         DMA_ClearFlag(DMA1_IT_TC5);
    " U! Q, U# V# Y" s5 W4 K9 f( }  U( T
  245.         DMA_ClearFlag(DMA1_IT_HT5);0 E3 a+ {- I' l  q/ O3 g3 D  l
  246.         DMA_Cmd(DMA1_Channel5, ENABLE);
    + p+ c* O) b& g! \* A8 T9 n
  247. }
    ! X# J/ @# c9 V- E

  248. & {- @+ J2 ]  e* Y
  249. uint16_t bsp_uart2_get_dmarx_buf_remain_size(void)2 Y( g" S. D4 E, O
  250. {* J& N* E) K4 T- o
  251.         return DMA_GetCurrDataCounter(DMA1_Channel5);        /* 获取DMA接收buf剩余空间 */0 J* S; H/ }& n' S  s3 |# j
  252. }
复制代码
, ]  D8 V# ]9 }6 u$ c% a) P

3 n3 F# z" b. s$ J" p4 `. ]# ?

压力测试:

  • 1.5Mbps波特率,串口助手每毫秒发送1k字节数据,stm32f0 DMA接收数据,再通过DMA发送回串口助手,毫无压力。
  • 1.5Mbps波特率,可传输大文件测试,将接受数据保存为文件,与源文件比较。
  • 串口高波特率测试需要USB转TLL工具及串口助手都支持才可行,推荐CP2102、FT232芯片的USB转TTL工具。
    0 Q' G! V+ P' V2 w3 _  U
    ; z7 E: E3 I+ V8 w
* k4 c$ ?' Y+ G$ p

7.png

, {/ V3 [1 T+ c+ s! G& G
1.5Mbps串口回环压力测试
0 D5 j7 _& o% K7 Z4 d+ H; n) \

- e8 j4 A: V7 _
  O9 p# P7 t0 C
收藏 4 评论2 发布时间:2020-9-15 15:49

举报

2个回答
killalarm 回答时间:2024-7-31 13:42:57

大牛能不能放一个代码示例出来学** 这里的fifo函数如何实现的?谢谢了

lixnif 回答时间:2024-8-26 11:20:41

半满和全满中断函数是不是没有添加清中断的代码。

所属标签

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