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

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

[复制链接]
STMCU-管管 发布时间:2020-9-15 15:49
1 前言
  u/ X: P5 D% P) T! c" P' N( {$ Y$ P! f

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

  • 内存—>内存,内存间拷贝
  • 外设—>内存,如uart、spi、i2c等总线接收数据过程
  • 内存—>外设,如uart、spi、i2c等总线发送数据过程% g+ Q3 V, p- v: _, u. J5 ]( D8 T

    & f, T( G& P0 K! w' G

9 b% }( a5 j# Z! O+ m/ z4 Y" h: A2 串口有必要使用DMA吗

% v9 Z1 ?& A% V  W7 G6 H5 W, }$ {2 J1 D. m: c, y+ m2 \
4 i% M) }! M. z8 M6 V

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


. ^6 O/ K; t& u" q$ c  _

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

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

    & [6 Q" f0 S) Z0 c# q6 ]2 ]2 _8 T
* ^6 J0 i7 K7 Y

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

3 K. w6 H( s. X3 }. g1 L
3 实现方式

( i  }- p  P8 B8 R- F9 ?2 y0 B/ N6 e$ g. l5 X9 P1 v

1 (2).png


5 }  {- S1 {# i: a# I9 n  }6 V! [6 |4 G! d9 K$ b0 J+ r# l; v
4 STM32串口使用DMA
; k# ^" v6 i6 `4 V2 ]1 V! `7 a

- P' Y3 _+ N: W8 e2 ?! [
2 i) r5 @$ g  A% Z3 D1 A, B

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

- Y/ L0 Y4 D, ^0 Q: `

测试平台:

  • STM32F030C8T6
  • UART1/UART2
  • DMA1 Channel2—Channel5
  • ST标准库
  • 主频48MHz(外部12MHz晶振)
    , M1 c! _: d" r) m) Q

: W% {1 J2 z: H7 O5 c) |3 o

# v* A! x; S: E( b" V
2.png
9 ^0 @: N( I/ F) `# Y

/ N/ _" F% i4 t$ V  P6 F& ~

5 串口DMA接收
8 z' V7 b# h3 _
- N- }9 F3 f+ R8 z2 Q9 J5.1 基本流程2 A: O2 h+ a( \5 O$ q

3.png

8 F1 w, ?  z4 H2 o  K
串口接收流程图

* c. O$ v6 E# h+ L% x
. h; P4 L8 R+ A- {. b" A4 K4 w5.2 相关配置

: i. ?9 l1 x+ f* D1 {) t. q( n6 S& x/ z& g9 A7 V, y# y. Q

关键步骤

【1】初始化串口

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

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

1 L! P( j- K% u! \; E* S

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

; B( u( a% ?& a. q) I1 Y: k- `

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

7 f& L( d; B. y8 p

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

  C/ \" D- W5 E0 h- {  w

【1】第一步,DMA先将数据搬运到buf1,搬运完成通知CPU来拷贝buf1数据
" x" A- t- x6 L【2】第二步,DMA将数据搬运到buf2,与CPU拷贝buf1数据不会冲突
; x& d$ H  R1 r  _+ ]# d: d; v【3】第三步,buf2数据搬运完成,通知CPU来拷贝buf2数据
/ q! @- k7 G. Y' E. n  f【4】执行完第三步,DMA返回执行第一步,一直循环

* [7 j6 R2 E. h6 G, S6 M( L7 j


8 q& x4 i  o, [, h) {

4.png


& e6 {" n# q- F# B9 C
1 n  z$ k) `5 E- z; M& r双缓存DMA数据搬运过程% ^8 U& ]! I4 D' U2 l
. q7 Q* v( r  C/ X$ T7 m' n2 d

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

% y# ]9 }; Z. w  B0 A; a2 l4 j

【1】第一步,DMA将数据搬运完成buf的前一半时,产生“半满中断”,CPU来拷贝buf前半部分数据
9 R4 h3 I/ O% }" x# r【2】第二步,DMA继续将数据搬运到buf的后半部分,与CPU拷贝buf前半部数据不会冲突: E% a" o* n. m6 m% D8 c% u+ U
【3】第三步,buf后半部分数据搬运完成,触发“溢满中断”,CPU来拷贝buf后半部分数据* [% K$ y4 J8 m
【4】执行完第三步,DMA返回执行第一步,一直循环

( C. |6 ~) [8 {8 A1 c) r

$ a7 [, y, Q7 i% C/ e
% X& n! D, u' q2 o: e

5.png

/ g& X0 V7 a5 v9 Y- V- a# B0 [

' Q# _( u& r5 g7 Y
使用半满中断DMA数据搬运过程

9 z) E6 c, r; n0 c' O, O5 m' I

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

  • 串口接收,DMA通道工作模式设为连续模式
  • 使能DMA通道接收buf半满中断、溢满(传输完成)中断
  • 启动DMA通道前清空相关状态标识,防止首次传输错乱数据
    7 l7 O* `) s4 n/ A: H; r* _
# c! q+ g) R( I5 A! }) ]
8 c9 p9 _5 B7 h4 g+ W! }4 j
' n1 a/ E; t: I* \2 T

3 @8 W& Z8 ?' n; D/ u" A) {$ Z( v& J
  1. void bsp_uart2_dmarx_config(uint8_t *mem_addr, uint32_t mem_size)
    ! V8 q  u% s7 ^+ y9 T, f3 p$ e% x
  2. {2 S# r" ?7 L: Z2 `. F
  3.           DMA_InitTypeDef DMA_InitStructure;
    - m& h) `# x2 ?' U/ `, q0 E
  4.         
    2 j" M9 t3 C- C6 K- b9 H  [
  5.         DMA_DeInit(DMA1_Channel5);
    2 B1 X% F, v; O6 J0 A  k3 ^
  6.         DMA_Cmd(DMA1_Channel5, DISABLE);
    0 n3 h) G$ j$ K; r, Q8 S. x
  7.         DMA_InitStructure.DMA_PeripheralBaseAddr         = (uint32_t)&(USART2->RDR);/* UART2接收数据地址 */9 `2 E: M# r7 p# X+ I7 x
  8.         DMA_InitStructure.DMA_MemoryBaseAddr                 = (uint32_t)mem_addr; /* 接收buf */
    6 F% F  u- d$ W; v
  9.         DMA_InitStructure.DMA_DIR                                         = DMA_DIR_PeripheralSRC;         /* 传输方向:外设->内存 */# s6 F5 @; l- b; c6 }! E. q4 t2 K2 _
  10.         DMA_InitStructure.DMA_BufferSize                         = mem_size; /* 接收buf大小 */
    / Y0 \: A) M5 l6 n* q
  11.         DMA_InitStructure.DMA_PeripheralInc                 = DMA_PeripheralInc_Disable;
    0 f) f! ^" h# `9 @
  12.         DMA_InitStructure.DMA_MemoryInc                         = DMA_MemoryInc_Enable; ' n- [7 O3 l$ L
  13.         DMA_InitStructure.DMA_PeripheralDataSize         = DMA_PeripheralDataSize_Byte; " b9 |3 J4 M( P! a
  14.         DMA_InitStructure.DMA_MemoryDataSize                 = DMA_MemoryDataSize_Byte;+ S9 P+ `5 P/ s) i8 V) l
  15.         DMA_InitStructure.DMA_Mode                                         = DMA_Mode_Circular; /* 连续模式 */
    3 ^9 Y% u. r5 N3 @/ A7 Y1 i8 t" ~9 @
  16.         DMA_InitStructure.DMA_Priority                                 = DMA_Priority_VeryHigh; 3 y0 |2 j2 x' v: K
  17.         DMA_InitStructure.DMA_M2M                                         = DMA_M2M_Disable; + Y+ N6 H4 Z/ U$ F# f8 k
  18.         DMA_Init(DMA1_Channel5, &DMA_InitStructure);
    1 D$ k7 Q2 o$ j' M: r9 l! y2 Y
  19.         DMA_ITConfig(DMA1_Channel5, DMA_IT_TC|DMA_IT_HT|DMA_IT_TE, ENABLE);/* 使能DMA半满、溢满、错误中断 */
    * r/ s$ z) m+ ~: S) \5 Z, _6 v$ B
  20.         DMA_ClearFlag(DMA1_IT_TC5);        /* 清除相关状态标识 */
    / T  s2 O, P5 y. B+ z
  21.         DMA_ClearFlag(DMA1_IT_HT5);
    : D* D6 ~/ V, [/ T( y/ ^
  22.         DMA_Cmd(DMA1_Channel5, ENABLE);
    1 ~% l$ B, B0 l5 Y  C2 O
  23. }
复制代码
9 c/ n* z. A1 p8 Y

6 B' G/ D+ ]( {% V- o

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

2 @/ p% @. T7 S' D) f
5.3 接收处理
9 R3 P, s* l" K, q: _6 m/ @

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

  • DMA通道buf溢满(传输完成)场景
  • DMA通道buf半满场景
  • 串口空闲中断场景$ i) Y" R" S% D. b$ h' O

    - H2 C/ g5 i% c- L/ D

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


4 |% b) }  U' k7 Q2 Y' \ 5.3 .1 接收数据大小
; H' G, t3 G1 y6 o% m4 G7 |% H7 B1 M3 K

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

  • 数据刚好是DMA接收buf的整数倍,这是理想的状态
  • 数据量小于DMA接收buf或者小于接收buf的一半,此时会触发串口空闲中断+ a. s# F0 B; L3 J2 u
    # d! S4 h" i5 ~7 ?' p" x' J& p# O* P

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

  1. /* 获取DMA通道接收buf剩余空间大小 */
    0 M8 J! B$ [% p/ l: @
  2. uint16_t DMA_GetCurrDataCounter(DMA_Channel_TypeDef* DMAy_Channelx);
复制代码
6 v0 }; F0 d& @: v  ~3 S. B

DMA通道buf溢满场景计算


/ K2 O0 ~+ i. j3 N; _

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

9 ?% Y5 x5 e6 m8 F
, d8 y0 K% Z4 A' x1 K

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

  1. void uart_dmarx_done_isr(uint8_t uart_id)
    9 m% m0 ?! G4 \3 p' D& e) O( Z5 X; `
  2. {
    - |5 M* Z) a& v( ^0 g1 B9 D
  3.           uint16_t recv_size;: C3 N4 {& W' W0 H
  4.         % N# h& H+ r6 x$ }! w+ W
  5.         recv_size = s_uart_dev[uart_id].dmarx_buf_size - s_uart_dev[uart_id].last_dmarx_size;+ s, t3 Y+ {) O$ N' R) s5 ~

  6. # o! o/ t+ J- R: c! y. i2 C
  7.         fifo_write(&s_uart_dev[uart_id].rx_fifo, 5 D3 y* o# D7 V3 U) N* ~
  8.                                    (const uint8_t *)&(s_uart_dev[uart_id].dmarx_buf[s_uart_dev[uart_id].last_dmarx_size]), recv_size);6 l) t% i7 e5 ~$ f1 A$ V

  9. 3 _/ f( j6 G# b% `
  10.         s_uart_dev[uart_id].last_dmarx_size = 0;
    ! ^% o" L# G. G, `; H. w
  11. }
复制代码

4 H5 `9 h* n: T

DMA通道buf半满场景计算


, J. u" {* [: F! U" R

  1. 接收数据大小 = DMA通道接收总数据大小 - 上一次接收的总数据大小
    $ w. z9 q! R* P' y* R
  2. DMA通道接收总数据大小 = DMA通道buf大小 - DMA通道buf剩余空间大小
复制代码

8 a* _$ H, O9 ?' r: w; O5 {' Z

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


* M; k+ \" f$ r9 M5 ]

  1. void uart_dmarx_half_done_isr(uint8_t uart_id)! q/ r; {9 r& f) C$ N2 v( ^
  2. {' |! t* M/ K7 v6 V
  3.           uint16_t recv_total_size;$ e5 a' `1 L  V1 B3 j; f4 Y! P! z" y
  4.           uint16_t recv_size;
    + `4 k7 |2 g0 P! O
  5.         ' ]0 n2 S8 q  ^, q  g, A
  6.         if(uart_id == 0)
    3 i) M, Q2 O- E, B/ [! ?
  7.         {. V" l, A1 M9 a/ f
  8.                   recv_total_size = s_uart_dev[uart_id].dmarx_buf_size - bsp_uart1_get_dmarx_buf_remain_size();: q5 X, S- ]5 D% w0 n' @' g9 K
  9.         }, G, i5 h3 ~" w3 f1 ], D
  10.         else if (uart_id == 1)9 T; \! R, A5 g0 t" f1 z) G
  11.         {! g! p+ M; H. N; S5 u; g
  12.                 recv_total_size = s_uart_dev[uart_id].dmarx_buf_size - bsp_uart2_get_dmarx_buf_remain_size();
    / g2 C9 Q' \; Z% D# V
  13.         }. Q6 ^( U$ e! }3 _
  14.         recv_size = recv_total_size - s_uart_dev[uart_id].last_dmarx_size;4 ?' }) B* S& i1 g, b
  15.         2 I: K2 }8 X& X5 D8 `* q
  16.         fifo_write(&s_uart_dev[uart_id].rx_fifo,   c: T( R( Z7 d* f
  17.                                    (const uint8_t *)&(s_uart_dev[uart_id].dmarx_buf[s_uart_dev[uart_id].last_dmarx_size]), recv_size);3 F3 m( P; s% M
  18.         s_uart_dev[uart_id].last_dmarx_size = recv_total_size;/* 记录接收总数据大小 */
    9 q# \8 D( @4 o8 C. W+ t+ i' \' j
  19. }
复制代码

- L& Q$ h& u) l! e( K( {1 e. L. |0 P' R; h6 v6 L* s

串口空闲中断场景计算


; v. q" \% r; j: u$ ~) r

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

串口空闲中断处理函数:

注:
6 b% B  W9 U, Q/ K; V$ @串口空闲中断处理函数,除了将数据拷贝到串口接收fifo中,还可以增加特殊处理,如作为串口数据传输完成标识、不定长度数据处理等等。

0 i2 {( A5 e7 L" u. j" U
5.3.2 接收数据偏移地址

  D+ X0 K: y0 Y) P9 Z& ^$ j3 |) b% G

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


  o1 M) u# X& ^$ a) ~

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

) j5 e& `8 X* H! V' n6 y  F

  1. void uart_dmarx_done_isr(uint8_t uart_id)2 t( F, |; w+ r* r' p
  2. {
    3 Y- P  i4 j0 n9 I7 S8 W1 V  y; N
  3.         /* todo */
    8 P' C2 m5 f+ ^* I2 G
  4.         s_uart_dev[uart_id].last_dmarx_size = 0;2 D" `9 b0 r( c+ l: g$ f* U
  5. }
复制代码
. M0 C. @1 q# ~  g
  w. X! x$ x6 a  R# n7 W3 k9 O
5.4 应用读取串口数据方法

. Q# b, V6 I: F+ T" ?" g

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

% z$ |, P2 @! Q! M+ p7 @1 R6 X: b4 H
6 串口DMA发送
6 C6 Q1 B, U' i; P
8 K! o+ h3 F! Y2 I
5.1 基本流程9 K7 g: e% t# J# w$ @9 d: E

6.png

' H2 h" \4 ]( z7 v/ l0 ]
1 M( ~7 \) u5 C$ `
串口发送流程图
9 K, {; y) C7 `! ~8 u1 M2 _" B: {0 P
- a- ]6 S; z7 b' R; s

6 z: ^" X; }& W' Y4 Q) `, d( c5.2 相关配置

' b1 S8 o, L+ J) W" ?/ X

关键步骤


( I7 F3 Y8 V0 Q/ q0 H

【1】初始化串口

3 T9 I5 [7 C) x" e

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

) L. k( U. O& @$ t0 T1 S2 ]2 y

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

2 M8 c+ u, d# P5 a1 E

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

( S. F1 s! s# g  @" E6 h6 [

  • 串口发送是,DMA通道工作模式设为单次模式(正常模式),每次需要发送数据时重新配置DMA
  • 使能DMA通道传输完成中断,利用该中断信息处理一些必要的任务,如清空发送状态、启动下一次传输
  • 启动DMA通道前清空相关状态标识,防止首次传输错乱数据
    $ \. p2 Q, U6 P( n' R

    9 C3 B$ F$ [9 G' C% R+ I

  • - r! O7 k6 Y) v) ^7 Q
    . Y, r- s  o& @( v# x

- n3 J8 h. ^# S7 m6 @5.3 发送处理

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

串口发送处理函数:


$ y6 @2 W3 R  V5 l' m7 j

  1. void uart_poll_dma_tx(uint8_t uart_id)( G5 B& G8 t3 {' }4 J, g1 h% m8 n
  2. {
    ; a1 _) A" h/ U, T  q
  3.           uint16_t size = 0;. h6 m- L5 p- I. Q9 L6 l9 O+ S
  4.         + N; S' l) n# {0 |7 a
  5.         if (0x01 == s_uart_dev[uart_id].status)
    - F5 A* J! H8 S: v& L# v. Q
  6.     {
    & M, S+ ~; k' h$ D
  7.         return;# l: u; Z6 {, d0 k
  8.     }
    3 N8 Y  L+ _, h- k
  9.         size = fifo_read(&s_uart_dev[uart_id].tx_fifo, s_uart_dev[uart_id].dmatx_buf,' {7 [) U8 V' f9 Q; `; J
  10.                                          s_uart_dev[uart_id].dmatx_buf_size);/ r* x! U- ?* Q+ A; ^
  11.         if (size != 0)
    ' D* u" O2 n' p$ |1 U* _) r
  12.         {
    : h2 p4 U3 ^1 m
  13.         s_UartTxRxCount[uart_id*2+0] += size;, g% d* Y  u/ G7 ?
  14.                   if (uart_id == 0)% ]6 E7 X/ a/ D) z1 u" S8 p
  15.                 {* m( W% V' k& }3 S/ Q2 m& B3 U
  16.             s_uart_dev[uart_id].status = 0x01;        /* DMA发送状态 */
    8 l4 O& i" E: B7 a
  17.                           bsp_uart1_dmatx_config(s_uart_dev[uart_id].dmatx_buf, size);1 B- F8 o' r7 j1 z
  18.                 }) q1 M  S% \2 q5 S' \
  19.                 else if (uart_id == 1)
    5 d' ?* Y& M% Q# @3 k+ J
  20.                 {6 E" V, Z& p) U( h' j) g. I
  21.             s_uart_dev[uart_id].status = 0x01;        /* DMA发送状态,必须在使能DMA传输前置位,否则有可能DMA已经传输并进入中断 */
    7 Y- \! g. v  S. }* j% J  g& t
  22.                         bsp_uart2_dmatx_config(s_uart_dev[uart_id].dmatx_buf, size);, X$ k" `" w& j9 G' ]" n
  23.                 }, I' e; w5 O& \& r4 n& l( M8 v4 j
  24.         }
    4 l" k5 C1 n& Y
  25. }
复制代码
# S/ r$ x& e0 }! p

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


- h+ u- F' O. T4 z& O+ x, B


1 a* N$ K5 q& p, t' x" \

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

: @, ~/ i& b( @( l

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

  1. void uart_dmatx_done_isr(uint8_t uart_id)
    " {, Y4 e6 h+ u- B* u: ]2 G$ G4 q
  2. {
    " Z; b- J3 N7 {- ?9 |! e
  3.         s_uart_dev[uart_id].status = 0;        /* 清空DMA发送状态标识 */
    " g% }7 X0 L$ [3 ~5 n+ L
  4. }
复制代码
( o% }4 n' E* }$ i  g

2 s3 H% s/ R& R7 L$ ?3 e" f3 c5 D# q


" n+ A) Q: w1 p4 S$ N& c/ a. J0 Z

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


) C6 P" S/ Z8 g3 a

: c# ?7 V+ ?$ u7 @# B9 v! J0 R: J3 M  Y

  • 主线程任务调用,前提是线程不能被其他任务阻塞,否则导致fifo溢出/ I2 L' Z/ W2 L8 J1 F* K5 Z
    $ M' C* v7 P& E+ H4 T
  1. void thread(void)6 y" ~3 E3 x6 `6 U
  2. {
    8 k. R  ]9 M) Q) g0 l' {- @
  3.     uart_dmatx_done_isr(DEV_UART1);
    # b" E& T* ^* d6 E3 u; _
  4.     uart_dmatx_done_isr(DEV_UART2);
    $ J5 _' s7 Z; k  N+ ], S
  5. }
复制代码

* s4 l( @, B8 Y2 z6 X; l# D8 h; A! k* V
  • 定时器中断中调用
    + B" \2 `" [# K7 t
  1. void TIMx_IRQHandler(void)
    " T' R! p4 Q/ A: Y
  2. {
    * O! P; c) \2 u) x: B6 G
  3.     uart_dmatx_done_isr(DEV_UART1);
    : V" ^3 j; Q0 D5 D  `$ n! t# L
  4.     uart_dmatx_done_isr(DEV_UART2);3 Z7 r3 e; e0 j2 ]( t
  5. }
复制代码
9 {6 ^, x2 t0 Y

; M( A0 J9 O! a
  • DMA通道传输完成中断中调用
    3 G+ ?% F$ m5 b
    8 w+ I4 n9 t% P3 I' d
  1. void DMA1_Channel4_5_IRQHandler(void)% R& p8 b% j6 ^% q$ Z
  2. {
    : n# g% z, t' i  C* L/ X$ k
  3.         if(DMA_GetITStatus(DMA1_IT_TC4)): k: m% }0 C( n3 f
  4.         {
    2 c& f5 p: t, N$ K2 a
  5.                 UartDmaSendDoneIsr(UART_2);
    ) r8 I; [# `! j4 p) l8 _
  6.                 DMA_ClearFlag(DMA1_FLAG_TC4);
    % w0 |+ I8 f* W! \& b2 r1 o0 |
  7.                 uart_dmatx_done_isr(DEV_UART2);7 Z4 |. i7 W$ K( f; j# ~: a0 [; Y
  8.         }$ A: u& _( U  h' v5 V7 r
  9. }
复制代码

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

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

  • 周期查询发送fifo数据,启动DMA传输,充分利用DMA发送效率,但可能降低串口数据流实时性
  • 实时查询发送fifo数据,加上超时处理,理想的方法
  • 在DMA传输完成中断中处理,保证实时连续数据流( Y7 v" {, c" S4 I
    8 K3 o6 F) Q6 k. T# q1 L/ w& u

$ _% Q6 A( b* t/ s6 串口设备
1 |" C( @1 E- ]/ Z) v4 W1 B

+ ~+ _" I& v! X8 o* j* ]) t6.1 数据结构& N  p- L% O' M0 _- x9 j% u1 l
  1. /* 串口设备数据结构 */
    , A+ [% L5 ?8 ]* I* n/ u8 K9 S
  2. typedef struct# f. X' R& C$ l2 K. E2 Q
  3. {. r6 }1 {: B) F6 a5 I' g( r1 s
  4.         uint8_t status;                        /* 发送状态 */+ V: [( s. e$ c$ n8 g
  5.         _fifo_t tx_fifo;                /* 发送fifo */
    ' z' g( s: q4 g; z% }- C4 W
  6.         _fifo_t rx_fifo;                /* 接收fifo */! B3 q) d8 l$ v" p# ^
  7.         uint8_t *dmarx_buf;                /* dma接收缓存 */
    9 [# W# a3 D0 F& d# c" M4 r' ~
  8.         uint16_t dmarx_buf_size;/* dma接收缓存大小*/
    + s  K7 h6 B( |" H
  9.         uint8_t *dmatx_buf;                /* dma发送缓存 */1 p" W4 a/ m4 [% X; @3 U) ]9 E
  10.         uint16_t dmatx_buf_size;/* dma发送缓存大小 */9 P  d$ k  M# P  c) B7 f/ K
  11.         uint16_t last_dmarx_size;/* dma上一次接收数据大小 */
    ) L/ g' Y1 s8 n7 F
  12. }uart_device_t;
复制代码
  C# V, i% |0 ?8 \. k; z+ E* W' ^

$ C5 h2 N0 s1 F* e% Q* R& V$ x6.2 对外接口
5 s1 y' u, W. m+ T
( H2 d1 }/ B  F4 H) L6 }
  1. /* 串口注册初始化函数 */
      n  {' e' ]2 i1 G" P  i) C2 W6 J
  2. void uart_device_init(uint8_t uart_id)
      ^/ n* H1 a: T4 g& J, ^
  3. {9 _- f" _7 U" `8 `
  4.           if (uart_id == 1)
    7 m* w8 R% ?  D: R8 b" K
  5.         {
    % q! _% f+ ]0 e
  6.                 /* 配置串口2收发fifo */; C* p* c( u- A4 Y0 D! ?, S
  7.                 fifo_register(&s_uart_dev[uart_id].tx_fifo, &s_uart2_tx_buf[0], . J8 a, E3 D  R# A7 [: u$ p& t
  8.                       sizeof(s_uart2_tx_buf), fifo_lock, fifo_unlock);
    . W( c4 C9 V% S5 {
  9.                 fifo_register(&s_uart_dev[uart_id].rx_fifo, &s_uart2_rx_buf[0], & O* Z! O% I  ~! U7 |: t( }
  10.                       sizeof(s_uart2_rx_buf), fifo_lock, fifo_unlock);
    4 F; S  [4 {6 r4 a
  11.                 + \! `6 l0 h! ~% y3 {8 g
  12.                 /* 配置串口2 DMA收发buf */" l! N! g# M# G( K" Q( U
  13.                 s_uart_dev[uart_id].dmarx_buf = &s_uart2_dmarx_buf[0];
    * _- L& b4 H; q1 F% F) D2 h
  14.                 s_uart_dev[uart_id].dmarx_buf_size = sizeof(s_uart2_dmarx_buf);
    7 W# S, n5 E, k* U2 Z
  15.                 s_uart_dev[uart_id].dmatx_buf = &s_uart2_dmatx_buf[0];" Z2 h2 j) k6 S: d, Z# `7 Y
  16.                 s_uart_dev[uart_id].dmatx_buf_size = sizeof(s_uart2_dmatx_buf);
    : o5 B+ A+ L# n, l
  17.                 bsp_uart2_dmarx_config(s_uart_dev[uart_id].dmarx_buf,
    5 d( u) ?$ E" s6 |
  18.                                                            sizeof(s_uart2_dmarx_buf));8 y! Y- Z6 j6 q, W# G$ K  E
  19.                 s_uart_dev[uart_id].status  = 0;6 Y, Q  d' M. I  C
  20.         }
    9 f; @' p; `' y1 V- m7 t
  21. }
    $ v8 x/ w* c' B$ m1 s4 N* e; s) Y! A

  22. " d  [, B! \$ e1 p% V# q# w
  23. /* 串口发送函数 */
    1 R0 ?8 m+ k: C* i
  24. uint16_t uart_write(uint8_t uart_id, const uint8_t *buf, uint16_t size)6 s) z7 N' \2 S; D; h0 V
  25. {
    5 x( t, [4 B5 y. N1 R9 o
  26.         return fifo_write(&s_uart_dev[uart_id].tx_fifo, buf, size);! m, b% O  ^: s# i4 g
  27. }
    : a1 N; E" f. s' G
  28. ! G9 Z0 @. h! C3 p" S, O
  29. /* 串口读取函数 */3 y; X5 w% T9 ?0 u
  30. uint16_t uart_read(uint8_t uart_id, uint8_t *buf, uint16_t size)) H, x$ \6 @' F1 S& c
  31. {
    ! j) O: c; J' L( Q/ I  T7 M' v
  32.         return fifo_read(&s_uart_dev[uart_id].rx_fifo, buf, size);
    5 b6 C/ S2 J, k% A" k
  33. }
复制代码
& N' }: X2 f* n2 ~+ [
8 完整源码
, j, _3 |! `( o4 Z0 Z

- L5 Z' Y0 R9 h' J8 k4 H, p9 B4 @& }4 F4 A! q: }2 ~

串口&DMA底层配置:

( \! z+ \7 Z# i

  1. #include <stddef.h>
    7 c3 t- `1 g: ^: a, x  ]2 K0 ^
  2. #include <stdint.h>
    % Z; ?7 ^: H6 U9 ^1 H' u) v) W
  3. #include <stdbool.h>
    3 t8 c) w( d5 o  z$ Z; @4 ~6 c3 z
  4. #include "stm32f0xx.h"0 H, H# `$ E/ t; b7 C
  5. #include "bsp_uart.h"
    , S2 m" {5 z! o
  6. 3 y# j3 U0 L4 o) Z, T
  7. /**
    % ^1 F# R0 ^7 d. ?
  8. * @brief  1 F  v3 T8 I0 i9 J$ z
  9. * @param  # F+ N& F! r; b1 [# @
  10. * @retval ( U5 R/ O( E$ T+ @( I& _& Y
  11. */+ U& D/ ]" v2 }2 ^+ a7 b/ A
  12. static void bsp_uart1_gpio_init(void)- a5 N8 J7 b% K, Y9 F: R' p' P
  13. {' o# `9 J. _$ _
  14.     GPIO_InitTypeDef    GPIO_InitStructure;
    2 h$ @! K# M' }
  15. #if 0
    1 k$ ^8 Y; X+ X! p
  16.         RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOB, ENABLE);
    8 M  g( D' b) O8 N. C
  17.           G2 h, t$ m8 w. i: t! A
  18.     GPIO_PinAFConfig(GPIOB, GPIO_PinSource6, GPIO_AF_0);/ @, B' ^" b, i9 r# l
  19.     GPIO_PinAFConfig(GPIOB, GPIO_PinSource7, GPIO_AF_0); : I  c" G8 q7 ]& p* W# [+ Y3 x
  20.         8 p7 D8 J9 K0 n6 M! f
  21.         GPIO_InitStructure.GPIO_Pin         = GPIO_Pin_6 | GPIO_Pin_7;
    ! n2 ^& ~+ R; o) F# W
  22.     GPIO_InitStructure.GPIO_Mode         = GPIO_Mode_AF;
    & _' B0 ?1 r4 s0 [1 h" L, k+ Z6 J
  23.         GPIO_InitStructure.GPIO_OType         = GPIO_OType_PP;
    3 Z# {' a, v5 ?# y& r! h
  24.     GPIO_InitStructure.GPIO_Speed          = GPIO_Speed_Level_3;- w/ H! r: z4 i2 ~0 F' L# a
  25.     GPIO_InitStructure.GPIO_PuPd         = GPIO_PuPd_UP;9 N  e& ]% g  ?- w/ V2 E
  26.     GPIO_Init(GPIOB, &GPIO_InitStructure);
    ( ]- Y+ r( n& f( s* g  Z
  27. #else
    7 Q2 t0 T$ L* d$ ~% `
  28.         RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOA, ENABLE);
      }- k* x- [7 w9 ^
  29.         * t' P5 q& J4 Z5 b; R) l
  30.     GPIO_PinAFConfig(GPIOB, GPIO_PinSource9, GPIO_AF_1);# r6 A# B$ G$ P% Y5 S$ O
  31.     GPIO_PinAFConfig(GPIOB, GPIO_PinSource10, GPIO_AF_1);
    4 t# M) ^+ ~" z) C2 ?. {# y
  32.        
    - c3 ]5 c; v/ G$ G
  33.         GPIO_InitStructure.GPIO_Pin         = GPIO_Pin_9 | GPIO_Pin_10;( n; [+ m1 U( S& X6 b$ b8 G. D* f
  34.     GPIO_InitStructure.GPIO_Mode         = GPIO_Mode_AF;
    , L" b% M0 F7 F# p* S
  35.         GPIO_InitStructure.GPIO_OType         = GPIO_OType_PP;
    5 Q  d; ?1 l; {/ \: S% P& H
  36.     GPIO_InitStructure.GPIO_Speed          = GPIO_Speed_Level_3;
    ; N+ W, v' [2 g) F/ v0 `, `
  37.     GPIO_InitStructure.GPIO_PuPd         = GPIO_PuPd_UP;8 V# p6 m; z6 w) i  P) [; s: ]8 X
  38.     GPIO_Init(GPIOA, &GPIO_InitStructure);2 q5 N$ Q' p4 }) _5 u
  39. #endif# l$ \+ d1 D; I( c& f
  40. }+ A. L9 {8 u+ Y& i) Y
  41. 2 x! g0 Q" I: w" w
  42. /**- z5 V1 i  F1 V: o* w! F2 W, q, D
  43. * @brief  
    9 E- n7 b6 ^) D  U, g
  44. * @param  4 @; b% V. E5 y& i+ r
  45. * @retval 4 y4 z; @# ^0 t' ^9 B6 x
  46. */
    4 S% J3 \3 g1 \( w- U
  47. static void bsp_uart2_gpio_init(void), {4 x5 M: m# N8 _. t1 j
  48. {
    0 A2 \) y8 T2 [' T  S3 q' y
  49.         GPIO_InitTypeDef GPIO_InitStructure;$ {  M  \" T8 }$ {; L
  50.         " y2 B. i' _7 Z% y+ `% Y# i8 {
  51.         RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOB, ENABLE);
    6 q; z! D& {, ^$ l, ?
  52.        
    / O8 |! u. k3 d/ n" [
  53.         GPIO_PinAFConfig(GPIOA, GPIO_PinSource2, GPIO_AF_1);! |; o/ {& S# ]2 O& w/ h7 k
  54.         GPIO_PinAFConfig(GPIOA, GPIO_PinSource3, GPIO_AF_1);
      ]6 m4 c" A$ y5 T
  55.        
    - o6 [. U7 X, U) K; {& q
  56.         GPIO_InitStructure.GPIO_Pin   = GPIO_Pin_2 | GPIO_Pin_3;- j$ g7 v  N9 r4 K
  57.         GPIO_InitStructure.GPIO_Mode  = GPIO_Mode_AF;
    6 G0 W: v' t7 F% o1 @
  58.         GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;7 s$ d) t" p6 M( U9 ~( C
  59.         GPIO_InitStructure.GPIO_Speed = GPIO_Speed_10MHz;4 G9 p/ b, X3 s$ B  ^
  60.         GPIO_InitStructure.GPIO_PuPd  = GPIO_PuPd_UP;6 m1 I1 H3 P; O) U, m% z$ I. L
  61.         GPIO_Init(GPIOA, &GPIO_InitStructure);
    0 P& a! e. k$ R
  62. }" W3 m3 j7 S$ b0 G7 h# t
  63. / ?( ]* [! u! Q* A
  64. /**/ u- I; M. Q# q' F
  65. * @brief  
    5 `5 Y! P2 p" {4 ?, `
  66. * @param  
    ! j0 D8 h: s9 K
  67. * @retval 5 C: z, J1 H, D, i( [; m
  68. */
    - C; G2 w& k: M8 }3 _- }
  69. void bsp_uart1_init(void)
    9 \5 {' ]- N7 N* l7 y( u, l! d
  70. {+ b; s" L  \% `4 V+ D  e! R7 \9 Q6 t
  71.         USART_InitTypeDef USART_InitStructure;
    ) u/ a0 m# m! H) r, n+ W9 c
  72.         NVIC_InitTypeDef NVIC_InitStructure;2 p: O" i) R. C
  73.         ( P8 q4 f9 @* C+ m4 F0 J
  74.         bsp_uart1_gpio_init();; I$ j5 @8 N6 e  y; {+ O
  75.        
    ! V6 r$ r9 I- }
  76.         /* 使能串口和DMA时钟 */
    9 l) U* k& y+ s) s, `
  77.         RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);
    ) t8 q3 c& K! U) G3 j5 x
  78.         RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);4 a( r, k  @3 N, }  z: U( z' c' I
  79.         . x  B4 h, C  A4 D2 Y
  80.         USART_InitStructure.USART_BaudRate            = 57600;
    ' m, b/ H; }2 @# y. t: u! q
  81.         USART_InitStructure.USART_WordLength          = USART_WordLength_8b;! _! d0 |1 K4 }$ Y" p: d/ T
  82.         USART_InitStructure.USART_StopBits            = USART_StopBits_1;
    / {5 K& q8 C  h
  83.         USART_InitStructure.USART_Parity              = USART_Parity_No;% c" q& R2 F6 {( ~" V8 N
  84.         USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
    0 X7 J  u" P! |1 w1 m
  85.         USART_InitStructure.USART_Mode                = USART_Mode_Rx | USART_Mode_Tx;
    : @5 G5 G: {0 b+ O: v7 a3 C* ?
  86.         USART_Init(USART1, &USART_InitStructure);
    ) X( x& W. X8 e
  87.        
    & l4 Q- i+ d9 A5 S( [3 ?
  88.         USART_ITConfig(USART1, USART_IT_IDLE, ENABLE);        /* 使能空闲中断 */
    3 ~6 S9 i, b  ^2 s& J& e
  89.         USART_OverrunDetectionConfig(USART1, USART_OVRDetection_Disable);: j1 g9 t# \* J
  90.         3 `( U: `, E9 m! ]0 j2 a5 n
  91.         USART_Cmd(USART1, ENABLE);
    $ x& f+ z% \3 C$ D
  92.         USART_DMACmd(USART1, USART_DMAReq_Rx|USART_DMAReq_Tx, ENABLE); /* 使能DMA收发 */
    4 ]- }$ t3 Q" N6 }/ |
  93. * B: u2 h/ K" K
  94.         /* 串口中断 */& H$ d! ?5 i! H" d6 K. N
  95.         NVIC_InitStructure.NVIC_IRQChannel         = USART1_IRQn;# X9 q9 J# E3 R7 l1 E; d
  96.         NVIC_InitStructure.NVIC_IRQChannelPriority = 2;
    1 q" ^  e) g7 H; E
  97.         NVIC_InitStructure.NVIC_IRQChannelCmd      = ENABLE;
    & g( i7 q) Y8 d  g' Y
  98.         NVIC_Init(&NVIC_InitStructure);
    ' q7 Z- L4 i2 N, ]# {

  99. , k8 X6 J2 o3 v, M1 z  k; `
  100.         /* DMA中断 */
    2 U1 v% c9 V* @6 y
  101.           NVIC_InitStructure.NVIC_IRQChannel                    = DMA1_Channel2_3_IRQn;      
      N0 E/ _$ {! {
  102.           NVIC_InitStructure.NVIC_IRQChannelPriority = 0;
    $ C1 I- d3 K7 G$ Z1 t$ t4 P: }# q
  103.         NVIC_InitStructure.NVIC_IRQChannelCmd      = ENABLE;
    9 g: ]/ D- t2 ?/ ~+ L% k' r
  104.           NVIC_Init(&NVIC_InitStructure);
    8 ^/ }2 C. ]- U1 s
  105. }
    $ d* i1 M5 L- _% _" W( d
  106. - d/ W" p3 m& n7 u% [3 k
  107. /**
    $ v0 O8 U% B% O; @; A8 b$ {
  108. * @brief  
    3 s! x- O/ g( _1 L4 L- F  N
  109. * @param  
    4 B$ R( @' J3 ?. S
  110. * @retval 5 d7 O0 Y' V3 \/ L4 g
  111. */" {; l7 w: s, `+ Y
  112. void bsp_uart2_init(void)$ ?# F% ?$ w0 o7 u8 K7 c
  113. {. J$ _+ j3 {8 f+ h/ Q6 ?. b
  114.         USART_InitTypeDef USART_InitStructure;: ?! ~( ?" b: X# b8 M% U0 F
  115.         NVIC_InitTypeDef NVIC_InitStructure;
    % M! R) q4 T7 m
  116.         6 }! s! |6 ~/ c# z0 W
  117.         bsp_uart2_gpio_init();. y# B. o$ R/ Y
  118.         ( T, N2 w4 O; c* Z! ^
  119.         /* 使能串口和DMA时钟 */
    8 J6 R4 B, u- [! y4 P, }
  120.         RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);4 V6 F8 w3 U0 l  D
  121.         RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2, ENABLE);
    7 f" o, Q0 ?$ z3 H3 D# q$ j0 \
  122.   u4 I) K3 k7 {8 h. @
  123.         USART_InitStructure.USART_BaudRate            = 57600;
    / h# s7 K# X% ]! Q. {% @
  124.         USART_InitStructure.USART_WordLength          = USART_WordLength_8b;" a6 l  t0 d2 g0 o6 Y' E) M+ Y  A
  125.         USART_InitStructure.USART_StopBits            = USART_StopBits_1;
    % S  ?; }5 g4 n; v( L
  126.         USART_InitStructure.USART_Parity              = USART_Parity_No;
    7 Q, S$ N% p6 n: l
  127.         USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;5 }) o9 \- g( A4 |' f6 ^
  128.         USART_InitStructure.USART_Mode                = USART_Mode_Rx | USART_Mode_Tx;1 j3 }! u$ @$ w& ^& [) ~
  129.         USART_Init(USART2, &USART_InitStructure);/ o) P, Q4 S/ `. l& E! r
  130.        
    $ ]# R& p* r: V5 K
  131.         USART_ITConfig(USART2, USART_IT_IDLE, ENABLE);        /* 使能空闲中断 */) h! [8 I' {) P' Y
  132.         USART_OverrunDetectionConfig(USART2, USART_OVRDetection_Disable);
    3 b. i; F. h. F$ r
  133.        
    2 s- W) q; w( K+ u2 O
  134.         USART_Cmd(USART2, ENABLE);1 v& R% }9 h8 A
  135.         USART_DMACmd(USART2, USART_DMAReq_Rx|USART_DMAReq_Tx, ENABLE);         /* 使能DMA收发 */
      L5 K7 ~) }7 {2 J) _; K- O5 ~0 n+ r
  136. / D: Q+ _, y* I2 J# h4 ~9 }# d
  137.         /* 串口中断 */
    ! p" p8 Q/ I& e) v
  138.         NVIC_InitStructure.NVIC_IRQChannel         = USART2_IRQn;9 ?" W" o) o% F% O* D5 e. N6 \  l  p
  139.         NVIC_InitStructure.NVIC_IRQChannelPriority = 2;* {2 y2 E) o$ z+ g3 v8 ^% d
  140.         NVIC_InitStructure.NVIC_IRQChannelCmd      = ENABLE;  F  h) k* Z9 ]8 l
  141.         NVIC_Init(&NVIC_InitStructure);, C# m# t* S  D" k

  142. . J7 b4 L" O. |4 k( d( K  V+ x
  143.         /* DMA中断 */
    9 |- a" f- D- f! |2 b
  144.         NVIC_InitStructure.NVIC_IRQChannel         = DMA1_Channel4_5_IRQn;       / Z0 }) K# c0 z( e
  145.           NVIC_InitStructure.NVIC_IRQChannelPriority = 0; 8 y0 r, I+ j0 I
  146.         NVIC_InitStructure.NVIC_IRQChannelCmd      = ENABLE;
    0 P, f. b% {( I, z( s; \
  147.           NVIC_Init(&NVIC_InitStructure);* F* f+ R& K0 I" F9 V/ _3 f8 H2 ]
  148. }
    : T+ M3 m( D& u. d' {
  149. - P6 l1 v+ M( w$ e" O9 |0 d$ q: @+ V
  150. void bsp_uart1_dmatx_config(uint8_t *mem_addr, uint32_t mem_size)/ z/ Y" g+ [  @2 I$ r
  151. {4 ]. G: I; I" I8 `; X/ S, A9 w
  152.           DMA_InitTypeDef DMA_InitStructure;
    4 @, G) P9 K' m) k% J
  153.        
    2 y. l1 W7 I9 e. j4 N7 N1 S
  154.         DMA_DeInit(DMA1_Channel2);1 x0 x- S6 [7 ~+ N9 I
  155.         DMA_Cmd(DMA1_Channel2, DISABLE);5 Z( `* I, j) P& m! d* q
  156.         DMA_InitStructure.DMA_PeripheralBaseAddr         = (uint32_t)&(USART1->TDR);
    7 S5 d$ ^) W0 o2 H/ M  g5 q
  157.         DMA_InitStructure.DMA_MemoryBaseAddr                 = (uint32_t)mem_addr;
    % k; L' _, E) i6 h4 V
  158.         DMA_InitStructure.DMA_DIR                                         = DMA_DIR_PeripheralDST;         /* 传输方向:内存->外设 */
      r0 p- V( B% G
  159.         DMA_InitStructure.DMA_BufferSize                         = mem_size;
    # Z2 M' l$ H; j2 y& _
  160.         DMA_InitStructure.DMA_PeripheralInc                 = DMA_PeripheralInc_Disable; 7 [2 g) N9 e" X; b3 F
  161.         DMA_InitStructure.DMA_MemoryInc                         = DMA_MemoryInc_Enable;
    & h8 T0 P, S' d2 {4 `3 v1 @
  162.         DMA_InitStructure.DMA_PeripheralDataSize         = DMA_PeripheralDataSize_Byte; 5 A1 A2 H. p. m* z7 X7 e: w8 U
  163.         DMA_InitStructure.DMA_MemoryDataSize                 = DMA_MemoryDataSize_Byte;
    , u0 M5 k! m! I
  164.         DMA_InitStructure.DMA_Mode                                         = DMA_Mode_Normal; 1 X# _1 A1 z$ w- G2 V
  165.         DMA_InitStructure.DMA_Priority                                 = DMA_Priority_High;
    . D5 B. `/ W7 g1 b* K
  166.         DMA_InitStructure.DMA_M2M                                         = DMA_M2M_Disable; / v4 g) E" h: j1 o7 \
  167.         DMA_Init(DMA1_Channel2, &DMA_InitStructure);  6 i1 F" S* R/ l! M. P) m) S
  168.         DMA_ITConfig(DMA1_Channel2, DMA_IT_TC|DMA_IT_TE, ENABLE); ) q; _7 N- k% l- {
  169.         DMA_ClearFlag(DMA1_IT_TC2);        /* 清除发送完成标识 */
    " j( C4 n3 o) l/ J
  170.         DMA_Cmd(DMA1_Channel2, ENABLE);
    / x8 F1 q% Q8 B* Q9 y7 j& y' x6 e
  171. }4 Q0 }- C9 U% ?+ V8 M/ }
  172. 2 j& \' y- c( ]: ~
  173. void bsp_uart1_dmarx_config(uint8_t *mem_addr, uint32_t mem_size)% K3 \  {1 ~# U. z& I
  174. {# D0 Y$ b2 B! k  G( f7 ~* B
  175.           DMA_InitTypeDef DMA_InitStructure;
    ) H9 h+ E4 h; R- t
  176.         ; f  k9 y$ ]4 g. K7 e# T
  177.         DMA_DeInit(DMA1_Channel3); 2 c, N# Z4 `' e/ J+ M' ~
  178.         DMA_Cmd(DMA1_Channel3, DISABLE);: P4 k" w/ p1 H# G; r5 [
  179.         DMA_InitStructure.DMA_PeripheralBaseAddr         = (uint32_t)&(USART1->RDR);
    ( f9 }: g2 r: ?+ f
  180.         DMA_InitStructure.DMA_MemoryBaseAddr                 = (uint32_t)mem_addr; : m9 B8 \5 A" R8 T
  181.         DMA_InitStructure.DMA_DIR                                         = DMA_DIR_PeripheralSRC;         /* 传输方向:外设->内存 */
    # n8 r- R5 l2 o  ^/ J6 E
  182.         DMA_InitStructure.DMA_BufferSize                         = mem_size; # F! \. N# a; H! f3 w% r) a
  183.         DMA_InitStructure.DMA_PeripheralInc                 = DMA_PeripheralInc_Disable; 6 F8 Y( V/ H, H1 [
  184.         DMA_InitStructure.DMA_MemoryInc                         = DMA_MemoryInc_Enable; / f# B9 g9 V+ w! K% I
  185.         DMA_InitStructure.DMA_PeripheralDataSize         = DMA_PeripheralDataSize_Byte; 4 B! G9 L$ o9 q6 ?
  186.         DMA_InitStructure.DMA_MemoryDataSize                 = DMA_MemoryDataSize_Byte;0 G  @" Z8 p$ }
  187.         DMA_InitStructure.DMA_Mode                                         = DMA_Mode_Circular; * T0 g# Z  H+ b& h" L
  188.         DMA_InitStructure.DMA_Priority                                 = DMA_Priority_VeryHigh;
    - m+ _- {2 K& J* z
  189.         DMA_InitStructure.DMA_M2M                                         = DMA_M2M_Disable; + t; Q# L. O/ T* J1 X5 B3 L: G4 g) y
  190.         DMA_Init(DMA1_Channel3, &DMA_InitStructure);
    ; N! k/ g5 _/ g3 ]* s! D
  191.         DMA_ITConfig(DMA1_Channel3, DMA_IT_TC|DMA_IT_HT|DMA_IT_TE, ENABLE);/* 使能DMA半满、全满、错误中断 */
    5 q, g0 D' o5 m& ~
  192.         DMA_ClearFlag(DMA1_IT_TC3);
    6 O+ i9 q: d- Y; t
  193.         DMA_ClearFlag(DMA1_IT_HT3);
    6 l" v, k+ S9 E  E: ^: F
  194.         DMA_Cmd(DMA1_Channel3, ENABLE);
    % b% j* W- Y6 U9 ]& L
  195. }
    ) K9 T2 u2 p6 \

  196. # y, E8 p8 b0 Y1 ]) }9 x+ r' w4 R9 ~
  197. uint16_t bsp_uart1_get_dmarx_buf_remain_size(void)
    1 P. ?8 W3 Z7 J. B# ?8 u+ B
  198. {
    + H' f5 B$ t+ u3 M7 ^3 b' d
  199.         return DMA_GetCurrDataCounter(DMA1_Channel3);        /* 获取DMA接收buf剩余空间 */
    % C( c- |- C( x9 o! b, M
  200. }
    - y8 r5 |* n" N

  201. 9 N8 p4 M# h: R
  202. void bsp_uart2_dmatx_config(uint8_t *mem_addr, uint32_t mem_size)
    2 u& E' U2 X$ n$ h$ |
  203. {
    ) A0 ?7 V# B. ~& @( h0 l" ]- o# r
  204.           DMA_InitTypeDef DMA_InitStructure;: N+ C7 L% H5 n  C
  205.        
    : J* X' L4 w4 r+ @
  206.         DMA_DeInit(DMA1_Channel4);
    # d& c$ g( g: U& a1 A
  207.         DMA_Cmd(DMA1_Channel4, DISABLE);
    . t. [1 t5 u- M
  208.         DMA_InitStructure.DMA_PeripheralBaseAddr         = (uint32_t)&(USART2->TDR);
    5 |" ], t/ o/ Y8 o) p9 q
  209.         DMA_InitStructure.DMA_MemoryBaseAddr                 = (uint32_t)mem_addr; ( R- H! b) F% a8 s5 K- r; E
  210.         DMA_InitStructure.DMA_DIR                                         = DMA_DIR_PeripheralDST;         /* 传输方向:内存->外设 */+ G7 y: X* G5 o  |! J. l
  211.         DMA_InitStructure.DMA_BufferSize                         = mem_size; 3 X% e" O- Z- L9 z! o
  212.         DMA_InitStructure.DMA_PeripheralInc                 = DMA_PeripheralInc_Disable;
    ' }' \3 m/ j( a* {/ ?  z. F
  213.         DMA_InitStructure.DMA_MemoryInc                         = DMA_MemoryInc_Enable; " I, d! S  @' T  W8 e3 T
  214.         DMA_InitStructure.DMA_PeripheralDataSize         = DMA_PeripheralDataSize_Byte;
    " D/ X$ ^% m& o
  215.         DMA_InitStructure.DMA_MemoryDataSize                 = DMA_MemoryDataSize_Byte;5 T9 N! w+ K7 y) D
  216.         DMA_InitStructure.DMA_Mode                                         = DMA_Mode_Normal; 8 p8 @3 \& M0 T7 {0 u& o
  217.         DMA_InitStructure.DMA_Priority                                 = DMA_Priority_High; 5 y5 w$ w4 q1 y0 A- s; m1 A. M0 O0 V
  218.         DMA_InitStructure.DMA_M2M                                         = DMA_M2M_Disable; . S' B4 X+ t% [7 o3 o: j
  219.         DMA_Init(DMA1_Channel4, &DMA_InitStructure);  
    1 E* W; V7 a& `1 ~/ u  Y1 W
  220.         DMA_ITConfig(DMA1_Channel4, DMA_IT_TC|DMA_IT_TE, ENABLE);
    ! `$ H; _! d; M; X& w
  221.         DMA_ClearFlag(DMA1_IT_TC4);        /* 清除发送完成标识 */7 S5 C0 M' `( w* W
  222.         DMA_Cmd(DMA1_Channel4, ENABLE);
    4 M& v6 h7 B( A" P; c
  223. }% i& O- Z1 Y' f9 X. y7 w- L

  224. 1 \4 x, r3 |$ I. O, e/ d/ U% B; x+ ]: {
  225. void bsp_uart2_dmarx_config(uint8_t *mem_addr, uint32_t mem_size)
    ) t  F! Z* j4 J% C  w
  226. {, t) ^0 r" n1 s7 G9 l4 ?! L
  227.           DMA_InitTypeDef DMA_InitStructure;
    & |6 @/ I' l$ h
  228.         0 A" c* m* z7 e4 D7 [2 I/ {) i; [  U
  229.         DMA_DeInit(DMA1_Channel5); ( f, U6 `6 E2 R3 C
  230.         DMA_Cmd(DMA1_Channel5, DISABLE);# O7 H9 H' W* x) P
  231.         DMA_InitStructure.DMA_PeripheralBaseAddr         = (uint32_t)&(USART2->RDR);
    0 p+ r; e. W, C8 X9 C
  232.         DMA_InitStructure.DMA_MemoryBaseAddr                 = (uint32_t)mem_addr;
    ' H8 h7 [- \" ]4 P; ]
  233.         DMA_InitStructure.DMA_DIR                                         = DMA_DIR_PeripheralSRC;         /* 传输方向:外设->内存 */
    6 z" D; ?- d3 f$ Y( h1 F
  234.         DMA_InitStructure.DMA_BufferSize                         = mem_size;
    : c% q' [9 |6 u
  235.         DMA_InitStructure.DMA_PeripheralInc                 = DMA_PeripheralInc_Disable; + Z! V+ e6 m; U4 o" U1 I
  236.         DMA_InitStructure.DMA_MemoryInc                         = DMA_MemoryInc_Enable;
    + G& Y* P" x1 ~: B
  237.         DMA_InitStructure.DMA_PeripheralDataSize         = DMA_PeripheralDataSize_Byte;
    " M- \. P! C, n" V6 M5 C' M7 A( Q8 N
  238.         DMA_InitStructure.DMA_MemoryDataSize                 = DMA_MemoryDataSize_Byte;
    2 c, X1 C8 w! c9 |, i  V
  239.         DMA_InitStructure.DMA_Mode                                         = DMA_Mode_Circular;
    " T$ P# s1 F3 q; l$ f  W
  240.         DMA_InitStructure.DMA_Priority                                 = DMA_Priority_VeryHigh; ( A: q: x, X' N
  241.         DMA_InitStructure.DMA_M2M                                         = DMA_M2M_Disable;
    # E' N5 s- b$ f+ `7 K& Y
  242.         DMA_Init(DMA1_Channel5, &DMA_InitStructure); " z2 _% Q" w4 Q# o6 W  g
  243.         DMA_ITConfig(DMA1_Channel5, DMA_IT_TC|DMA_IT_HT|DMA_IT_TE, ENABLE);/* 使能DMA半满、全满、错误中断 */% \8 ~$ }8 S1 z
  244.         DMA_ClearFlag(DMA1_IT_TC5);( w$ S2 M1 X1 s0 Y5 }' [" U" l! h& i
  245.         DMA_ClearFlag(DMA1_IT_HT5);
    % T# }/ H* k& G
  246.         DMA_Cmd(DMA1_Channel5, ENABLE); - L+ T$ |4 I% s) S
  247. }
    0 |" y/ S# L# u6 l( d/ N7 m
  248. # \( \5 C7 ^) e( t+ E4 E
  249. uint16_t bsp_uart2_get_dmarx_buf_remain_size(void)
    " ^3 Z' v9 e  `2 v
  250. {3 S6 ?" L# n! d/ a
  251.         return DMA_GetCurrDataCounter(DMA1_Channel5);        /* 获取DMA接收buf剩余空间 */
    2 F3 Q# n$ G. v  x2 B& c
  252. }
复制代码

+ m% d0 c6 |' b


" _+ a% y$ r% u& M& E7 f

压力测试:

  • 1.5Mbps波特率,串口助手每毫秒发送1k字节数据,stm32f0 DMA接收数据,再通过DMA发送回串口助手,毫无压力。
  • 1.5Mbps波特率,可传输大文件测试,将接受数据保存为文件,与源文件比较。
  • 串口高波特率测试需要USB转TLL工具及串口助手都支持才可行,推荐CP2102、FT232芯片的USB转TTL工具。7 ?. T; a* Y& l* O) x8 M
    6 u" T( @8 n5 J+ K# ^; @% I

/ z4 o/ [  A4 {- F

7.png


2 I# k- f' i8 ?- O/ N! X
1.5Mbps串口回环压力测试

0 m) r, _# R. e+ h1 e. X

; J- v' d! e* [$ P7 J6 k- g  a4 t% W) L6 |. R0 y  Z, K
收藏 3 评论0 发布时间:2020-9-15 15:49

举报

0个回答

所属标签

相似分享

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