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

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

[复制链接]
STMCU-管管 发布时间:2020-9-15 15:49
1 前言
5 y+ j) J& l4 p6 ~0 s
9 R, v1 q  ?, n* y

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

  • 内存—>内存,内存间拷贝
  • 外设—>内存,如uart、spi、i2c等总线接收数据过程
  • 内存—>外设,如uart、spi、i2c等总线发送数据过程
    ! g, L: S3 i7 x# b
    : _- _$ k; U, y0 [' `2 Z
/ ?  s* j% R* ?9 Y
2 串口有必要使用DMA吗

. Z& o' V3 R9 ]! [3 h
3 k# [  W1 E# o. o6 ~* U
! B8 P( i+ M) f* n% Z5 r% a

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

, X9 B" l2 i) w( \. i# h' s3 L

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

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

    $ K! X, L0 ?: B
; Z3 V: |3 m* E) z

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

$ n* P9 h) S1 I* ~  l, ^& ]
3 实现方式
: {* ~: |# g& ~& h1 y  _

+ n' a- L2 E2 E# j5 Y! d/ u

1 (2).png

' ^* p. \- E7 ?9 a1 l% G& X

5 y7 p2 [* A+ U+ f4 STM32串口使用DMA

2 z5 \# l: Q9 m
4 t1 K0 M! N2 |1 {/ e& z
) @) `1 F# a; V* L- ^8 N- p

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


8 Q$ S  |7 L6 c. ^: E5 {

测试平台:

  • STM32F030C8T6
  • UART1/UART2
  • DMA1 Channel2—Channel5
  • ST标准库
  • 主频48MHz(外部12MHz晶振)
    ) f3 U" Y7 F' B6 k+ K7 Q. ?
8 e6 X. U4 |, z
6 |* G$ {4 x$ E9 b5 {& z
2.png
5 v$ J5 N& h9 s/ f


/ P: @7 b8 ]$ D3 t- C

5 串口DMA接收
& f7 X4 ~0 y4 ]: R0 I& Y- |& }
; {* @; D' Z# E$ ?4 I; N8 b5.1 基本流程# l. U  O8 H' p) R, W& c1 \

3.png


/ T2 N5 U; |1 u+ S" [$ u
串口接收流程图

( m% d% y' @  @7 ]
3 z3 ?' m9 X$ v0 w8 G; c; H. D5.2 相关配置
; m$ d' D  f3 g0 V

2 J: Z9 z) h7 X

关键步骤

【1】初始化串口

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

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

; u# L- `! U2 p

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


8 j# e% T- q+ c' }

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


. l* \5 z2 e* g

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


/ q7 n: Z* T  n6 [+ g5 B6 n$ C+ W

【1】第一步,DMA先将数据搬运到buf1,搬运完成通知CPU来拷贝buf1数据% \6 d9 `8 q) {, k
【2】第二步,DMA将数据搬运到buf2,与CPU拷贝buf1数据不会冲突
6 x1 O' a2 A+ t3 m1 }0 g# _【3】第三步,buf2数据搬运完成,通知CPU来拷贝buf2数据( J% b& U. P2 B% m
【4】执行完第三步,DMA返回执行第一步,一直循环

" p/ P1 M" w9 y2 V1 s' T

; ?) ~$ Z* \1 w; C0 A3 p! y( a4 m

4.png


) U& J$ V( t3 I5 s& `
- B0 o& j: z* e9 C: I双缓存DMA数据搬运过程# \8 ?2 m3 V' {- w; i. j6 ~) [
9 W& u0 v$ v, y( N* O- d

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

2 [' b( `1 `4 U

【1】第一步,DMA将数据搬运完成buf的前一半时,产生“半满中断”,CPU来拷贝buf前半部分数据
2 l' F; f/ F! ^/ S【2】第二步,DMA继续将数据搬运到buf的后半部分,与CPU拷贝buf前半部数据不会冲突! H+ P6 G& D9 A, q
【3】第三步,buf后半部分数据搬运完成,触发“溢满中断”,CPU来拷贝buf后半部分数据# R, x. p5 x! X4 B
【4】执行完第三步,DMA返回执行第一步,一直循环


3 U  y: }2 Y! Z! F
1 f2 ^- X2 z3 i& c

- h' H) U8 \/ G& r

5.png


! x* A4 h1 V3 f" n+ ]$ k+ |, f+ b" A" |. J1 t# M- b
使用半满中断DMA数据搬运过程
" E/ `  D, A, ~
0 j4 d* Z4 |! N5 A

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

  • 串口接收,DMA通道工作模式设为连续模式
  • 使能DMA通道接收buf半满中断、溢满(传输完成)中断
  • 启动DMA通道前清空相关状态标识,防止首次传输错乱数据
    3 U9 C0 q0 r1 u9 |3 O1 M% v+ E. b
: O% M0 k  }2 a6 Z: o) J
+ l1 \1 D% q8 G4 ]5 w
+ s2 @# J3 `/ A, ?! R
1 J6 u8 K% i1 P- W) o3 I
  1. void bsp_uart2_dmarx_config(uint8_t *mem_addr, uint32_t mem_size)
      T; n' z) Y$ q) s# |" _
  2. {
    5 ^% U$ [4 }1 t" a' @
  3.           DMA_InitTypeDef DMA_InitStructure;
    . V6 H  P% }* q' k! f
  4.         
    ( I' y2 V) o& G/ v% E
  5.         DMA_DeInit(DMA1_Channel5); # l% Y2 s( x* f% x* k9 ^
  6.         DMA_Cmd(DMA1_Channel5, DISABLE);
    - a2 f$ P; D4 @
  7.         DMA_InitStructure.DMA_PeripheralBaseAddr         = (uint32_t)&(USART2->RDR);/* UART2接收数据地址 */
    / I. s( A4 h: ~
  8.         DMA_InitStructure.DMA_MemoryBaseAddr                 = (uint32_t)mem_addr; /* 接收buf */6 i' T8 j) k; M5 I& z" V+ X
  9.         DMA_InitStructure.DMA_DIR                                         = DMA_DIR_PeripheralSRC;         /* 传输方向:外设->内存 *// C! j" [" t7 W  Y3 U" j3 U# q
  10.         DMA_InitStructure.DMA_BufferSize                         = mem_size; /* 接收buf大小 */! W6 C1 H& P, |
  11.         DMA_InitStructure.DMA_PeripheralInc                 = DMA_PeripheralInc_Disable;
    ; `; I# R$ g0 p% N2 f" Z
  12.         DMA_InitStructure.DMA_MemoryInc                         = DMA_MemoryInc_Enable;
    7 n! m$ O4 k) L; D4 f
  13.         DMA_InitStructure.DMA_PeripheralDataSize         = DMA_PeripheralDataSize_Byte; ( _4 l% Y7 i, A# q+ y/ y
  14.         DMA_InitStructure.DMA_MemoryDataSize                 = DMA_MemoryDataSize_Byte;
    / P) Z3 j2 J8 x. L- Y3 U# |
  15.         DMA_InitStructure.DMA_Mode                                         = DMA_Mode_Circular; /* 连续模式 */+ ~% |4 x8 x' t- R8 w
  16.         DMA_InitStructure.DMA_Priority                                 = DMA_Priority_VeryHigh;
    5 O' K3 P8 @; A' i' m4 M# C7 d
  17.         DMA_InitStructure.DMA_M2M                                         = DMA_M2M_Disable; 3 m( f4 ]2 C" j* A+ A* W1 k
  18.         DMA_Init(DMA1_Channel5, &DMA_InitStructure); 5 n# w8 K" r5 L: N6 `4 m; {  }5 V. E
  19.         DMA_ITConfig(DMA1_Channel5, DMA_IT_TC|DMA_IT_HT|DMA_IT_TE, ENABLE);/* 使能DMA半满、溢满、错误中断 */
    0 a2 ?+ l9 `' ^% a, k  O
  20.         DMA_ClearFlag(DMA1_IT_TC5);        /* 清除相关状态标识 */
    ! p3 z0 H" g! ]* A: [
  21.         DMA_ClearFlag(DMA1_IT_HT5);
    # H, v, g6 @$ a3 k
  22.         DMA_Cmd(DMA1_Channel5, ENABLE);
    & t( j5 U( W  {
  23. }
复制代码

! Q; [' G8 B& q; `6 r# ^) p& A$ I) ~) t

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

- k2 m( I" B' T/ D
5.3 接收处理

. W9 [% G" l% R% [" g/ D

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

  • DMA通道buf溢满(传输完成)场景
  • DMA通道buf半满场景
  • 串口空闲中断场景# g: y' }" w, n, g) ?
    ) _7 \# [3 f+ P; ]2 _2 n" T. \1 j

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

2 A; k6 d9 q& f; |9 L* f) p: t- S  f
5.3 .1 接收数据大小
/ p3 [7 ~! p( m9 g* K1 _1 a

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

  • 数据刚好是DMA接收buf的整数倍,这是理想的状态
  • 数据量小于DMA接收buf或者小于接收buf的一半,此时会触发串口空闲中断* z) E) `0 }, J1 Z/ E" ]" H8 R
    ( z" ]- n6 o5 w2 O2 u% ?

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

  1. /* 获取DMA通道接收buf剩余空间大小 */
    / p# c5 {; x3 n' o
  2. uint16_t DMA_GetCurrDataCounter(DMA_Channel_TypeDef* DMAy_Channelx);
复制代码
9 O. L# F1 `1 H  I- C3 l6 ~

DMA通道buf溢满场景计算

. V# V# d3 G" l! h

  1. 接收数据大小 = DMA通道buf大小 - 上一次接收的总数据大小
复制代码
- p/ e9 F  p( k! a- r$ Y
6 w) t; }- I' Y  d; b" Q: b

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

  1. void uart_dmarx_done_isr(uint8_t uart_id)
    ' Y# M- I- D* x% |! B
  2. {5 M/ O% x0 u2 w
  3.           uint16_t recv_size;3 I/ d/ S2 @( E3 X) K
  4.         - E& y0 y+ v# I; s# m
  5.         recv_size = s_uart_dev[uart_id].dmarx_buf_size - s_uart_dev[uart_id].last_dmarx_size;
    : ?4 z/ a, n2 r
  6. 1 k' u0 A4 A! C6 ?6 |3 q
  7.         fifo_write(&s_uart_dev[uart_id].rx_fifo, : z) ]# n6 K, _& K
  8.                                    (const uint8_t *)&(s_uart_dev[uart_id].dmarx_buf[s_uart_dev[uart_id].last_dmarx_size]), recv_size);. K2 z! y! _- r. e% |- Y. d
  9. ) W1 m, `  ~; l' Z& k5 `& _! F
  10.         s_uart_dev[uart_id].last_dmarx_size = 0;- ~( `/ f/ |3 K6 r' V
  11. }
复制代码
4 k( |  X+ a9 S! z; ]

DMA通道buf半满场景计算

1 C1 i: ~& }4 [% R* C, x5 _; f

  1. 接收数据大小 = DMA通道接收总数据大小 - 上一次接收的总数据大小/ o6 s" p& S: M( V; J
  2. DMA通道接收总数据大小 = DMA通道buf大小 - DMA通道buf剩余空间大小
复制代码

4 i9 S& U$ V$ p- ?; r

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


% w2 z; J7 o' A5 b$ Z) w

  1. void uart_dmarx_half_done_isr(uint8_t uart_id)
    7 c' z' B0 J! l2 c- K
  2. {
    4 M9 N, N" l2 N" a) o
  3.           uint16_t recv_total_size;6 v% {4 I* h2 a+ d0 F, S
  4.           uint16_t recv_size;6 ~- \1 q; _0 Z1 m
  5.         ! ?3 W; A( z; r
  6.         if(uart_id == 0)6 |% K8 b/ E! t' k- E8 E% M. }2 I% D* _3 P
  7.         {2 E: p( E' g; z) n
  8.                   recv_total_size = s_uart_dev[uart_id].dmarx_buf_size - bsp_uart1_get_dmarx_buf_remain_size();
    ( B8 C' C8 R% w, h! D/ s# D. t
  9.         }
    1 o( w( m+ T3 q
  10.         else if (uart_id == 1)
    # ^% o7 {' X& N: a% i! Z7 I
  11.         {
    ! o5 \' B9 [# C1 Q
  12.                 recv_total_size = s_uart_dev[uart_id].dmarx_buf_size - bsp_uart2_get_dmarx_buf_remain_size();
    1 i) _4 c2 X$ e/ J5 p8 K
  13.         }- ~9 U. i+ R$ _
  14.         recv_size = recv_total_size - s_uart_dev[uart_id].last_dmarx_size;
    % z* F5 s9 F( ]; ?" e) h
  15.         2 K; s; M6 l3 I8 g& k3 |4 e
  16.         fifo_write(&s_uart_dev[uart_id].rx_fifo,
    8 {1 _( s$ v" ?
  17.                                    (const uint8_t *)&(s_uart_dev[uart_id].dmarx_buf[s_uart_dev[uart_id].last_dmarx_size]), recv_size);
    " ?# w3 o0 F# ?( N5 A. P
  18.         s_uart_dev[uart_id].last_dmarx_size = recv_total_size;/* 记录接收总数据大小 */
    9 h' U! c$ r% r. X
  19. }
复制代码

. z6 t9 P5 \( B% ]: d! a/ ~+ ]- U% Y2 X

串口空闲中断场景计算

) \" {% ]- }2 R, p; w# b' O

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

串口空闲中断处理函数:

注:
* n& Z  ^$ r; S3 k9 a4 p+ {串口空闲中断处理函数,除了将数据拷贝到串口接收fifo中,还可以增加特殊处理,如作为串口数据传输完成标识、不定长度数据处理等等。


% y' b2 y( L' g* p5 R" Y 5.3.2 接收数据偏移地址
$ y9 u6 ?$ |, k7 y0 G

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


6 Y7 r5 n4 c( ?7 y& K" p8 r

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


0 E. V- T- c  o' {4 q

  1. void uart_dmarx_done_isr(uint8_t uart_id)
    ' V6 A8 `/ I9 c; A6 _
  2. {
    # _9 @5 ^; v. \, N% R3 k! x" E0 e+ p
  3.         /* todo */
    7 q5 j" Q5 c% y* {
  4.         s_uart_dev[uart_id].last_dmarx_size = 0;5 z' {" r: }$ s" l9 [' Z8 T  z
  5. }
复制代码
4 p' `) N# y- i3 x3 q

8 a7 b4 m: U) I9 D5.4 应用读取串口数据方法
* g. b7 `! c! W& Q. \4 s1 m* [3 U

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


) O4 v' A" p; t7 J/ w# a6 串口DMA发送
# V: S) X) v% H/ i' [

* s6 e2 n; p1 ?3 W5.1 基本流程: i0 v/ r! T" l+ x- a

6.png


: H2 `0 z: e" Y& I3 C
/ Q9 A* z3 I0 M( R
串口发送流程图

/ m9 g8 w2 v7 m/ U
# @4 O( ~* P7 `, {+ F3 e2 G! }; b
0 Q) i+ e9 j4 M) {  Q
5.2 相关配置

2 M$ a: A: x% c. I( x7 Y

关键步骤

& e( H4 C) k6 c5 K( M

【1】初始化串口

; s! O, S( ?1 }+ h

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

4 q) d# U! @' C

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

" a$ h9 r' N2 e& B7 L

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


8 Q: \% d3 A0 y

  • 串口发送是,DMA通道工作模式设为单次模式(正常模式),每次需要发送数据时重新配置DMA
  • 使能DMA通道传输完成中断,利用该中断信息处理一些必要的任务,如清空发送状态、启动下一次传输
  • 启动DMA通道前清空相关状态标识,防止首次传输错乱数据) `! \9 k5 J& E* o: o: ~- Q( c

    6 S+ b5 d4 ^0 ?4 Y7 {

  • % R1 \6 w& ?5 w1 P4 k# ^) a' H
    ' j8 t  w$ x1 K+ o3 G: d0 p

  l# _* G7 ?, Y) m- i* u! j5.3 发送处理

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

串口发送处理函数:

4 C( P) L# t4 M4 y* D! ]

  1. void uart_poll_dma_tx(uint8_t uart_id)
    % J5 F1 j( p# u. Y2 a  a
  2. {- r9 q! u' I! [# s
  3.           uint16_t size = 0;
    7 l/ C2 W% d8 V4 V2 _% V* a
  4.        
    ( ~# k- s& P8 V+ m* p
  5.         if (0x01 == s_uart_dev[uart_id].status). m& o; N/ j1 p% }
  6.     {% b* g$ H& ]) ?% i2 V
  7.         return;
    * i! d6 g- g( ~5 F* f
  8.     }
    ; i2 K) F: X% `0 n0 E6 h
  9.         size = fifo_read(&s_uart_dev[uart_id].tx_fifo, s_uart_dev[uart_id].dmatx_buf,9 ~; A+ w. _2 T% R
  10.                                          s_uart_dev[uart_id].dmatx_buf_size);$ [- {- @2 J; A! C
  11.         if (size != 0)6 e; [' _! L; `* k
  12.         {
    ; Q5 k5 S, J! I* I
  13.         s_UartTxRxCount[uart_id*2+0] += size;
    ) X' ^/ Z. v  V# b1 R( `# T* k7 s
  14.                   if (uart_id == 0): M6 v9 T+ z: ^/ b! Q5 S+ i
  15.                 {
    4 g' ^, `* e! p' o
  16.             s_uart_dev[uart_id].status = 0x01;        /* DMA发送状态 */
    0 i$ T5 C- ^9 P+ S1 V
  17.                           bsp_uart1_dmatx_config(s_uart_dev[uart_id].dmatx_buf, size);7 K: i# ]3 L. M+ K
  18.                 }
    / D. J; M% b9 [4 p5 v4 T
  19.                 else if (uart_id == 1)3 Z" E% L! j3 M/ V
  20.                 {
    % C" V2 G0 T/ |' z" P
  21.             s_uart_dev[uart_id].status = 0x01;        /* DMA发送状态,必须在使能DMA传输前置位,否则有可能DMA已经传输并进入中断 */3 P- g' a" B- ^% ]1 s
  22.                         bsp_uart2_dmatx_config(s_uart_dev[uart_id].dmatx_buf, size);, J2 h; K% G8 `" S% F& `5 g3 }
  23.                 }
    + N) ~; q: [+ i" G- ^
  24.         }7 m; n5 s$ c( E6 e
  25. }
复制代码

5 s+ D- a+ k+ Z9 \9 Y

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


7 g" l1 w% h: ?8 x6 b. i

3 C' W0 b1 p4 J1 f9 y0 n! G. z7 E

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

/ i% D+ ?) H+ q6 x. \- r7 q

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

  1. void uart_dmatx_done_isr(uint8_t uart_id)
    & a4 j# z1 R  ~' b/ F( J
  2. {7 f4 x5 D$ h8 r  w) X
  3.         s_uart_dev[uart_id].status = 0;        /* 清空DMA发送状态标识 *// _2 k( X4 D( K, [
  4. }
复制代码
* U6 ^  E& U  k! [4 C( ~+ N

* r2 _1 H' Y$ n! X9 o


# d3 S: F7 }4 X; [& b

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


$ _$ F. p  _* t- N; K( Q4 @

1 h: }, X" D& M

  • 主线程任务调用,前提是线程不能被其他任务阻塞,否则导致fifo溢出
    . i# n9 S8 s7 u+ x6 Q
    9 K1 i9 W& Y3 v3 I' P. V
  1. void thread(void)
    & ^# @0 X. o5 y. A: D2 |
  2. {
    5 t& E; k0 }* z; C! \2 P9 v3 V
  3.     uart_dmatx_done_isr(DEV_UART1);
    0 l' {1 d3 L% a# l$ f
  4.     uart_dmatx_done_isr(DEV_UART2);
    ' X$ O/ S9 I' H  l/ n/ T
  5. }
复制代码

0 U! ?  W# e) F6 N5 F8 F  }# Z  {! [
  • 定时器中断中调用
    " b* E: \) A1 [5 _) a* O# d
  1. void TIMx_IRQHandler(void)
    ! J4 |$ a0 O1 k6 i8 |6 ~
  2. {5 R1 Z% m. }$ J
  3.     uart_dmatx_done_isr(DEV_UART1);3 E9 v  b" }" r( K$ @
  4.     uart_dmatx_done_isr(DEV_UART2);0 ?5 r2 u7 M6 L( m+ n6 w
  5. }
复制代码
: H, G% @/ ]5 V

1 ^4 X7 H  L% b
  • DMA通道传输完成中断中调用; d# i: c: r( I0 `
    0 p6 L) g8 X" J+ {3 J
  1. void DMA1_Channel4_5_IRQHandler(void)0 A& q+ u! I( j& d. z* t3 [% u
  2. {
    - `$ y8 c0 \! q; {, _* U- k
  3.         if(DMA_GetITStatus(DMA1_IT_TC4))# P! S6 ^, B$ F* O8 q! E7 H
  4.         {2 }  m. w$ o0 w+ ~/ ?
  5.                 UartDmaSendDoneIsr(UART_2);) ^. ^$ ^& c/ D$ H* x& ?0 A
  6.                 DMA_ClearFlag(DMA1_FLAG_TC4);
    . e( u6 g  e* ?* c; Q# z- `: p
  7.                 uart_dmatx_done_isr(DEV_UART2);
    ( d/ s9 s' }" ?' |
  8.         }+ y: C; z( I4 m" k" E
  9. }
复制代码

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

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

  • 周期查询发送fifo数据,启动DMA传输,充分利用DMA发送效率,但可能降低串口数据流实时性
  • 实时查询发送fifo数据,加上超时处理,理想的方法
  • 在DMA传输完成中断中处理,保证实时连续数据流8 E) Y1 Z5 q0 [5 q+ K

    & q2 n/ z4 K- {& @+ K
: [! _1 h; S* V& I1 f# _  W
6 串口设备
9 {$ B6 e" ~& b& t7 [$ `+ e
! ?# y! h# ^9 n, v4 b7 _5 [- B
6.1 数据结构
# }7 v+ Z- ~) g" s
  1. /* 串口设备数据结构 */
    6 ]. u/ F: x+ Z8 Z1 _
  2. typedef struct
    . P5 C$ K- H+ a* F% j0 ^, p. Z
  3. {
    / d& Q. {/ o. M: T9 I* D9 l2 K3 v
  4.         uint8_t status;                        /* 发送状态 */1 ^3 T( ^6 v; D
  5.         _fifo_t tx_fifo;                /* 发送fifo */% c' U6 S( n2 p% G1 ~3 t9 {
  6.         _fifo_t rx_fifo;                /* 接收fifo */
    " _4 Y% J7 v0 t9 T/ t" n
  7.         uint8_t *dmarx_buf;                /* dma接收缓存 */
    . a/ G- `0 c! }: Q
  8.         uint16_t dmarx_buf_size;/* dma接收缓存大小*/2 r9 }/ n* }/ A2 K" T' g5 Y& U
  9.         uint8_t *dmatx_buf;                /* dma发送缓存 */
    $ x) O/ Q# @: {) C6 }
  10.         uint16_t dmatx_buf_size;/* dma发送缓存大小 */
    5 E2 w1 h; A$ H( U7 p
  11.         uint16_t last_dmarx_size;/* dma上一次接收数据大小 */7 U- y* X( M* f! Q& D% F; t: F8 ~
  12. }uart_device_t;
复制代码
: x! R* H) C0 g$ }# X; Y

. M) z% T3 z5 U5 ^; K- J4 w9 d; j6.2 对外接口
6 v* p) p$ s! U
. j5 L7 J. O" m0 R) ^) U
  1. /* 串口注册初始化函数 */% C1 r8 L0 _6 Q6 j
  2. void uart_device_init(uint8_t uart_id)
    . O1 p8 _" ?0 q. ~: p3 R( a. E7 \0 h
  3. {
    ) l3 Z" u: \. w! t, C4 W
  4.           if (uart_id == 1), r7 R* y+ L! Q) l
  5.         {; J1 Y- S1 o$ o) U. y# ~4 b
  6.                 /* 配置串口2收发fifo */7 C1 B& U  {3 K3 c
  7.                 fifo_register(&s_uart_dev[uart_id].tx_fifo, &s_uart2_tx_buf[0], 4 X3 C9 g5 I. _/ U2 j# w! d4 k
  8.                       sizeof(s_uart2_tx_buf), fifo_lock, fifo_unlock);
    ( `- j5 n/ Q( I8 o0 x0 i3 f
  9.                 fifo_register(&s_uart_dev[uart_id].rx_fifo, &s_uart2_rx_buf[0],
    + n: B0 ?6 l3 J6 E+ b! ?
  10.                       sizeof(s_uart2_rx_buf), fifo_lock, fifo_unlock);/ Y$ N% d: Y) M. d4 G' ^- y! t4 F* w4 o+ H
  11.                 $ E! v) z5 N+ N. j4 U" Z9 G
  12.                 /* 配置串口2 DMA收发buf */1 }& _4 [  A3 D, L. m8 W' f
  13.                 s_uart_dev[uart_id].dmarx_buf = &s_uart2_dmarx_buf[0];8 p  Q: t" F. O7 Q
  14.                 s_uart_dev[uart_id].dmarx_buf_size = sizeof(s_uart2_dmarx_buf);# D; [9 a3 o8 C  }3 F. t
  15.                 s_uart_dev[uart_id].dmatx_buf = &s_uart2_dmatx_buf[0];- \3 K' A/ G1 u$ f, u
  16.                 s_uart_dev[uart_id].dmatx_buf_size = sizeof(s_uart2_dmatx_buf);
    # ^; j* w  C% I! Q, |3 K/ A* ~5 c
  17.                 bsp_uart2_dmarx_config(s_uart_dev[uart_id].dmarx_buf,
    1 d3 q# h$ Z* K. l, k2 }7 R2 D
  18.                                                            sizeof(s_uart2_dmarx_buf));
    1 k& k% n9 C, Q
  19.                 s_uart_dev[uart_id].status  = 0;8 _: P4 \+ i+ o( q! Y8 s
  20.         }5 q0 S) `! N% k/ _
  21. }! U8 _7 Z* l( E' D: R

  22. . B# |5 Y" s7 V7 Q
  23. /* 串口发送函数 */
    # I  |2 |8 C5 U& j6 A
  24. uint16_t uart_write(uint8_t uart_id, const uint8_t *buf, uint16_t size)
    - F' D/ R% U9 j$ o5 s
  25. {, i/ W3 B6 {. F7 H0 ~
  26.         return fifo_write(&s_uart_dev[uart_id].tx_fifo, buf, size);6 }4 r" x: j' i: k+ ]
  27. }+ @! |8 C) A! t4 v6 K
  28. 8 u& z4 `' J( {5 e+ ~0 M- {
  29. /* 串口读取函数 */
    3 G' A  ^. t- V: B  P
  30. uint16_t uart_read(uint8_t uart_id, uint8_t *buf, uint16_t size), g$ a* ?- K8 k: ]
  31. {* m2 b8 Z& a& R, q4 H! m4 l% g
  32.         return fifo_read(&s_uart_dev[uart_id].rx_fifo, buf, size);( }2 f% \' Y$ A
  33. }
复制代码

+ F% g! H  N& L8 a9 `. Z8 完整源码
$ F( V7 @! g! u9 ]& b

; o1 }5 ]8 x6 g! p! ^) J8 i5 M6 w! G# ]2 \/ f% d

串口&DMA底层配置:


$ T; l1 A+ g: D# n

  1. #include <stddef.h>
    % ]& \. r/ y' B! O! \" V1 \
  2. #include <stdint.h>
    9 e8 ^$ K2 z2 S, I( a7 V
  3. #include <stdbool.h># Q  q7 S( g# z' x" o! m
  4. #include "stm32f0xx.h"
    7 |  G6 y: ~' N( J* o: i
  5. #include "bsp_uart.h"% U: M3 W1 L, R6 T; `; ^
  6. " v$ h( u4 P( {/ O; b- m" Y6 c
  7. /**" U( N6 H( ]# a9 L- P
  8. * @brief  4 D* e3 l+ u# L/ j) I
  9. * @param  
    / n" e, |8 I4 H9 _& t6 f- n. L4 _
  10. * @retval - W1 I) U% r; M4 E
  11. */, h" g5 T. B4 @! J; U0 _
  12. static void bsp_uart1_gpio_init(void)# L8 c& L% Y4 x9 l& O
  13. {
    : p" W; h$ Y5 `% I: L& B& ]) [
  14.     GPIO_InitTypeDef    GPIO_InitStructure;
    8 L  h0 E( {) G/ e, |' K6 M& l
  15. #if 01 v+ s7 o1 z3 Q, n* G5 v0 o, ?$ s8 e
  16.         RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOB, ENABLE);* {+ y' ~8 b# [" G5 Y! `4 q/ Y- h
  17.        
    3 l/ k4 C0 d" C
  18.     GPIO_PinAFConfig(GPIOB, GPIO_PinSource6, GPIO_AF_0);
    ; G4 G5 V2 l9 V' }5 W. e; a
  19.     GPIO_PinAFConfig(GPIOB, GPIO_PinSource7, GPIO_AF_0);
    ; L* y; }5 B' b2 Z7 F
  20.         $ \! ~  d* E/ q0 a! @% Q6 ]
  21.         GPIO_InitStructure.GPIO_Pin         = GPIO_Pin_6 | GPIO_Pin_7;' X! @1 F  _9 ]( Z+ j6 z2 L& G
  22.     GPIO_InitStructure.GPIO_Mode         = GPIO_Mode_AF;/ T8 x" n4 `: V
  23.         GPIO_InitStructure.GPIO_OType         = GPIO_OType_PP;
    : y2 B4 I0 S- ]$ s, _
  24.     GPIO_InitStructure.GPIO_Speed          = GPIO_Speed_Level_3;& u1 n1 F6 P, g6 S; r6 F1 o
  25.     GPIO_InitStructure.GPIO_PuPd         = GPIO_PuPd_UP;
    5 M$ @" P7 }% p3 G2 Y" P
  26.     GPIO_Init(GPIOB, &GPIO_InitStructure);, f1 I# `! p4 r1 Q) o$ u8 H# G
  27. #else
    ! ^1 |8 ]9 T. z$ V9 r7 z# J: ~; Y
  28.         RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOA, ENABLE);
    . ?) o  F+ h3 i2 ^  G
  29.         . @( i- d" N! A: y! E9 O) n4 ?
  30.     GPIO_PinAFConfig(GPIOB, GPIO_PinSource9, GPIO_AF_1);- S' j6 Z7 U1 O! l( L! n3 N( j
  31.     GPIO_PinAFConfig(GPIOB, GPIO_PinSource10, GPIO_AF_1);   w) {* ?# i' U6 H
  32.         # @9 {$ B* l  D2 a: v  y% x
  33.         GPIO_InitStructure.GPIO_Pin         = GPIO_Pin_9 | GPIO_Pin_10;/ I( W/ u% H) Q+ j2 B% P9 Y
  34.     GPIO_InitStructure.GPIO_Mode         = GPIO_Mode_AF;
    ; {, X4 u( v/ r- W6 ~7 d) L0 i( J
  35.         GPIO_InitStructure.GPIO_OType         = GPIO_OType_PP;
    8 J4 |- B  ~2 X  Y3 \
  36.     GPIO_InitStructure.GPIO_Speed          = GPIO_Speed_Level_3;# d. Y% Q( U: K4 ]
  37.     GPIO_InitStructure.GPIO_PuPd         = GPIO_PuPd_UP;; c+ q$ i% s: G) d  ]- P0 f
  38.     GPIO_Init(GPIOA, &GPIO_InitStructure);
    : v9 P. P# R/ y$ S
  39. #endif- u$ l, O' V% K
  40. }$ M- G+ K1 G1 d! u

  41. / l) r( n( Q& e
  42. /**' {! @: N# W* r' n3 S7 ]/ j  R4 w' {
  43. * @brief  " t/ s" b& P6 q1 A
  44. * @param  % w' o- |, P' f! g. b6 G. W, N
  45. * @retval ( H, W+ \5 q. L& ?% }+ b3 O
  46. */; Y7 H. d* E. F# w' T& i8 T
  47. static void bsp_uart2_gpio_init(void)4 {$ A/ D; T) m5 E: M! n% `; h
  48. {. M9 d: `, j% }4 y  E
  49.         GPIO_InitTypeDef GPIO_InitStructure;
    ; x9 {2 A! Z. T) l3 n) \4 L- V, ]9 E
  50.         ' M  a& T8 ~3 ^, f* E, v
  51.         RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOB, ENABLE);
    9 E6 O' k* Q8 ?2 B; l8 r
  52.        
    ) a, w2 _$ Q; i6 x0 W* a& Z. a
  53.         GPIO_PinAFConfig(GPIOA, GPIO_PinSource2, GPIO_AF_1);, h+ q, H$ l* s1 t) a1 Z- b. v5 X$ s
  54.         GPIO_PinAFConfig(GPIOA, GPIO_PinSource3, GPIO_AF_1);- Y9 w$ Y" I( Z; g; G* L
  55.        
    ( ]7 o1 Y8 m! e# L  x
  56.         GPIO_InitStructure.GPIO_Pin   = GPIO_Pin_2 | GPIO_Pin_3;; Z% p- g0 |( G. O/ B
  57.         GPIO_InitStructure.GPIO_Mode  = GPIO_Mode_AF;6 Q* I8 {8 P- G: j4 |' f! E
  58.         GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
    - U' L. C' M9 f3 l9 f3 x% G
  59.         GPIO_InitStructure.GPIO_Speed = GPIO_Speed_10MHz;5 }  C7 F; P) l( ?
  60.         GPIO_InitStructure.GPIO_PuPd  = GPIO_PuPd_UP;) \0 U: C  N2 `
  61.         GPIO_Init(GPIOA, &GPIO_InitStructure);
    , x; z7 a  c6 G; E$ s' D% [3 }: K
  62. }
    / S; D" Z( l0 p! O  i3 o& o, G% l
  63. . Y4 c6 d0 m3 o# |* e1 o* M; e! l& k6 \; K
  64. /**
    0 n7 J7 y9 K6 A3 G. ]/ T
  65. * @brief  * o) m  l, t9 W0 b3 y% {" P
  66. * @param  
      F. N7 h5 V+ M5 _8 J- E+ u# s
  67. * @retval 6 T4 c$ Z$ b6 ?1 I, u
  68. */1 b; W, r; H6 b, I: n& u8 o' t
  69. void bsp_uart1_init(void)# A, P# V4 W; ]. i
  70. {
    + B! S' Y+ e* g* E
  71.         USART_InitTypeDef USART_InitStructure;0 s6 S) `$ |  ]* A- o, J
  72.         NVIC_InitTypeDef NVIC_InitStructure;
    9 G: h) C5 k% l6 w
  73.         5 I. v* N2 [" o5 ~2 J1 |
  74.         bsp_uart1_gpio_init();, m, y3 G6 ^4 r' j
  75.        
    & M1 J  y& Y/ M+ ^2 G, K! r
  76.         /* 使能串口和DMA时钟 */
    ( E3 _1 B- j# W* F0 t& t# M6 k
  77.         RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);4 Y2 f. _, Q- D$ F$ J+ T2 b
  78.         RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);7 D2 |6 y& }  X/ w# p) a) l6 M
  79.        
    ( v# I! n8 K1 e+ N( b
  80.         USART_InitStructure.USART_BaudRate            = 57600;
    4 V+ {) K  |5 `# A/ y4 ~
  81.         USART_InitStructure.USART_WordLength          = USART_WordLength_8b;* B& x2 H9 b* n: @
  82.         USART_InitStructure.USART_StopBits            = USART_StopBits_1;
    # C1 r% h$ m1 q1 s% T
  83.         USART_InitStructure.USART_Parity              = USART_Parity_No;
    5 A" q; l3 P" `7 ?! _# {1 V6 [
  84.         USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
    + \+ ]2 }8 f4 A. v. g4 c
  85.         USART_InitStructure.USART_Mode                = USART_Mode_Rx | USART_Mode_Tx;6 C* S% [& F5 h. ]
  86.         USART_Init(USART1, &USART_InitStructure);* h+ d! T- q& z7 d: Y" s2 a& v9 U6 h
  87.         2 S* B9 ^% H; P. u8 c, V
  88.         USART_ITConfig(USART1, USART_IT_IDLE, ENABLE);        /* 使能空闲中断 */* K/ k5 @" N  K3 ^0 s$ y
  89.         USART_OverrunDetectionConfig(USART1, USART_OVRDetection_Disable);$ w# a6 C9 Q, w7 h+ M
  90.        
    # \* ?. c  {# M0 j
  91.         USART_Cmd(USART1, ENABLE);6 c+ g7 u, o1 ~* K+ q
  92.         USART_DMACmd(USART1, USART_DMAReq_Rx|USART_DMAReq_Tx, ENABLE); /* 使能DMA收发 */
    4 L' z( O9 k6 ~# d1 @* w
  93. ) f# y' B% ~0 @, O9 G
  94.         /* 串口中断 */7 U5 c: {3 z  n
  95.         NVIC_InitStructure.NVIC_IRQChannel         = USART1_IRQn;. s$ i- Q5 \, a0 G4 T6 z
  96.         NVIC_InitStructure.NVIC_IRQChannelPriority = 2;+ O' _7 ^. M- v
  97.         NVIC_InitStructure.NVIC_IRQChannelCmd      = ENABLE;
    , ~1 A. d$ @9 g% \) @/ t
  98.         NVIC_Init(&NVIC_InitStructure);% F5 y5 J% \4 J+ D# O9 S* y, `- _

  99. # M+ C! W. r. P) a
  100.         /* DMA中断 */* H9 J8 l& w' G& r4 K
  101.           NVIC_InitStructure.NVIC_IRQChannel                    = DMA1_Channel2_3_IRQn;      
    " f7 i8 R/ P3 z& O0 ?3 ^! X: V
  102.           NVIC_InitStructure.NVIC_IRQChannelPriority = 0;
    ( @1 V: U6 R) a2 w' d+ j
  103.         NVIC_InitStructure.NVIC_IRQChannelCmd      = ENABLE;
    6 O& b) y& J7 U0 Q: y' M  l
  104.           NVIC_Init(&NVIC_InitStructure);( R# I2 M# j" i" X3 u0 t) Q
  105. }
    " `0 r# a' T* o4 h! b% B
  106. 9 Y# x+ w+ u  ]4 F& f, {' [; {
  107. /**
    . d' Y8 w7 e5 b% M( G
  108. * @brief  
    / Z, A3 H5 g) R! T- b1 o: `1 q
  109. * @param  
    1 ^6 P. N/ D) f: O1 Q% G
  110. * @retval
    * w3 S% H! n( `2 B* O- t
  111. */$ [' x6 z" y# Y0 u
  112. void bsp_uart2_init(void)
    . k' P; c3 ^# j
  113. {* F" C8 @( f" Z' u- ?, j
  114.         USART_InitTypeDef USART_InitStructure;
    ! i  f& [* Q  M& ?) `0 r  q
  115.         NVIC_InitTypeDef NVIC_InitStructure;6 N* c: T  E6 L- q  s7 K! f+ q8 S9 d
  116.         ; Q; x) I8 _5 [
  117.         bsp_uart2_gpio_init();
    ( D; y7 \, \3 D# J, M
  118.         * l/ _% r% f$ i( Y  Q5 r
  119.         /* 使能串口和DMA时钟 */) y+ F5 t# Y1 Q. e$ d  k
  120.         RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);; R5 k6 N# v6 L8 R
  121.         RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2, ENABLE);
    6 V/ l/ d1 P' z) m3 S

  122. ) w) S8 |& {9 m, i1 ?9 h
  123.         USART_InitStructure.USART_BaudRate            = 57600;4 T( A7 }6 z) h" O& D& j  V  Z! u
  124.         USART_InitStructure.USART_WordLength          = USART_WordLength_8b;" L% s4 K, `; k7 H$ E/ C. x
  125.         USART_InitStructure.USART_StopBits            = USART_StopBits_1;0 U( w% H: v1 d
  126.         USART_InitStructure.USART_Parity              = USART_Parity_No;
    & t/ e" F# p& n; ^" o
  127.         USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
    ' Q4 x- O* ^$ R+ [& G! W
  128.         USART_InitStructure.USART_Mode                = USART_Mode_Rx | USART_Mode_Tx;
    / N8 A, a; _: B/ e  H/ ^! E8 C
  129.         USART_Init(USART2, &USART_InitStructure);+ B4 M( L* b1 j* I9 J0 Y3 P
  130.        
    ) k& q3 M3 e' ?
  131.         USART_ITConfig(USART2, USART_IT_IDLE, ENABLE);        /* 使能空闲中断 */' f2 R# `# W; W1 }
  132.         USART_OverrunDetectionConfig(USART2, USART_OVRDetection_Disable);- N; \: M) n  r, }: {& D* g) i# G
  133.        
    + }5 J  T% `# a" j) A9 _9 v' [& ?
  134.         USART_Cmd(USART2, ENABLE);
    7 p1 `3 s6 P) ]) Z9 ]9 F4 w$ S: i- S
  135.         USART_DMACmd(USART2, USART_DMAReq_Rx|USART_DMAReq_Tx, ENABLE);         /* 使能DMA收发 *// U$ ^, I4 }* [; Q# h

  136. ( P3 N5 y  i$ i
  137.         /* 串口中断 */
    ( [. z" V0 q: `1 _
  138.         NVIC_InitStructure.NVIC_IRQChannel         = USART2_IRQn;2 |5 q( a, C; a" U3 a# A3 k; f7 C
  139.         NVIC_InitStructure.NVIC_IRQChannelPriority = 2;& T6 A+ u) C% E$ B! q( e* M
  140.         NVIC_InitStructure.NVIC_IRQChannelCmd      = ENABLE;: s8 Y1 Z3 _  D% L: S
  141.         NVIC_Init(&NVIC_InitStructure);) P( E( [5 ^) Y6 A* B

  142. & ]. @8 U* h* s; t7 ?/ j, X" k
  143.         /* DMA中断 */# a( A+ }; Y2 O0 Y/ |5 b" U
  144.         NVIC_InitStructure.NVIC_IRQChannel         = DMA1_Channel4_5_IRQn;      
    $ w  w+ s, h( A5 {8 O+ e+ t
  145.           NVIC_InitStructure.NVIC_IRQChannelPriority = 0;
    % l' N: Z) V* ^
  146.         NVIC_InitStructure.NVIC_IRQChannelCmd      = ENABLE;$ P# {+ V/ h# x: }5 l
  147.           NVIC_Init(&NVIC_InitStructure);+ {) K" ?6 e' k2 q
  148. }7 i2 y+ W+ w7 Z- i6 v  x. l
  149. ! r! i0 h9 l6 Q  P/ N
  150. void bsp_uart1_dmatx_config(uint8_t *mem_addr, uint32_t mem_size)2 x+ d, N% V+ M, I1 ~! f& f
  151. {
    ' ~% t0 Q$ Y% d9 ?* r# y
  152.           DMA_InitTypeDef DMA_InitStructure;
    9 {# r$ x2 i$ q) L% U
  153.        
    . ~: v* X* p' y
  154.         DMA_DeInit(DMA1_Channel2);
    9 N/ I" D! c, t1 k. f0 i
  155.         DMA_Cmd(DMA1_Channel2, DISABLE);
    $ d/ a. }& Z% S
  156.         DMA_InitStructure.DMA_PeripheralBaseAddr         = (uint32_t)&(USART1->TDR);, c; Y. m& N6 l$ G  j2 Q
  157.         DMA_InitStructure.DMA_MemoryBaseAddr                 = (uint32_t)mem_addr;
    6 X7 |& a, s* P, |& X) i
  158.         DMA_InitStructure.DMA_DIR                                         = DMA_DIR_PeripheralDST;         /* 传输方向:内存->外设 */8 w! E  v: @! @, w
  159.         DMA_InitStructure.DMA_BufferSize                         = mem_size;
    ; q  p" S5 J& ^6 ?9 `# ], x
  160.         DMA_InitStructure.DMA_PeripheralInc                 = DMA_PeripheralInc_Disable; ' E5 M+ R$ \- @
  161.         DMA_InitStructure.DMA_MemoryInc                         = DMA_MemoryInc_Enable; 9 p1 t" Q7 m) Y  {. S! i
  162.         DMA_InitStructure.DMA_PeripheralDataSize         = DMA_PeripheralDataSize_Byte;
    1 p; L, _! F5 l& ^2 X% j9 |& H
  163.         DMA_InitStructure.DMA_MemoryDataSize                 = DMA_MemoryDataSize_Byte;( E2 ], s  e! Q) t" c( C
  164.         DMA_InitStructure.DMA_Mode                                         = DMA_Mode_Normal;
    ( b- Z1 s3 Y, @6 d
  165.         DMA_InitStructure.DMA_Priority                                 = DMA_Priority_High; 9 @! o1 s+ c0 b
  166.         DMA_InitStructure.DMA_M2M                                         = DMA_M2M_Disable; " u8 k4 ^1 N0 q: q
  167.         DMA_Init(DMA1_Channel2, &DMA_InitStructure);  
    " Y! n' ?1 R% P1 D0 ~
  168.         DMA_ITConfig(DMA1_Channel2, DMA_IT_TC|DMA_IT_TE, ENABLE); / l$ z9 k/ }* u# @6 ~6 G% z1 a
  169.         DMA_ClearFlag(DMA1_IT_TC2);        /* 清除发送完成标识 */
    - n5 [6 u  ~7 i  D
  170.         DMA_Cmd(DMA1_Channel2, ENABLE); : D3 p  f" Y9 A
  171. }) _) L( r2 @' u  j, V" ]% g9 v' X
  172. ; N  s  I* U" ?' R- X1 E7 s8 E
  173. void bsp_uart1_dmarx_config(uint8_t *mem_addr, uint32_t mem_size)0 s( I5 s& I+ h/ `7 q
  174. {
    . k& u  \  H4 ^+ s
  175.           DMA_InitTypeDef DMA_InitStructure;
    ) [3 D8 X; N8 G2 U" E
  176.        
    # u5 E! c; q+ d& ?& w
  177.         DMA_DeInit(DMA1_Channel3);
    ' l, K  i$ f4 t; z& \" i: x
  178.         DMA_Cmd(DMA1_Channel3, DISABLE);5 `3 L0 O9 w1 K: l
  179.         DMA_InitStructure.DMA_PeripheralBaseAddr         = (uint32_t)&(USART1->RDR);1 Y7 M7 N& k1 ]" y2 m. _
  180.         DMA_InitStructure.DMA_MemoryBaseAddr                 = (uint32_t)mem_addr;
    3 [4 x1 y' ?% ^5 n1 u% H( \# t
  181.         DMA_InitStructure.DMA_DIR                                         = DMA_DIR_PeripheralSRC;         /* 传输方向:外设->内存 */+ y( E  f; U4 ]  h  ~0 I
  182.         DMA_InitStructure.DMA_BufferSize                         = mem_size;   _. ]4 `1 L0 k; Y+ n
  183.         DMA_InitStructure.DMA_PeripheralInc                 = DMA_PeripheralInc_Disable;
    ' ]1 o; q- V3 G+ M3 y
  184.         DMA_InitStructure.DMA_MemoryInc                         = DMA_MemoryInc_Enable;
    9 V6 @/ Y9 z: Q" S) [# l# ]; D
  185.         DMA_InitStructure.DMA_PeripheralDataSize         = DMA_PeripheralDataSize_Byte;
    # g' l6 Q, o  ?" S) v
  186.         DMA_InitStructure.DMA_MemoryDataSize                 = DMA_MemoryDataSize_Byte;" |# q  y$ w9 ]
  187.         DMA_InitStructure.DMA_Mode                                         = DMA_Mode_Circular; % r  C" Q2 }- K& R
  188.         DMA_InitStructure.DMA_Priority                                 = DMA_Priority_VeryHigh; 8 Z7 P0 q1 |  D% \  H" i
  189.         DMA_InitStructure.DMA_M2M                                         = DMA_M2M_Disable;
    7 l) P* k$ K  C3 Y/ ^' U1 q2 a
  190.         DMA_Init(DMA1_Channel3, &DMA_InitStructure); ! T, u4 \; i9 \: \
  191.         DMA_ITConfig(DMA1_Channel3, DMA_IT_TC|DMA_IT_HT|DMA_IT_TE, ENABLE);/* 使能DMA半满、全满、错误中断 */
    4 @6 ]* b- s+ q4 y/ Q
  192.         DMA_ClearFlag(DMA1_IT_TC3);& M# T" E$ F) h7 ?
  193.         DMA_ClearFlag(DMA1_IT_HT3);1 ?: A' d/ C$ a* X( Y% S; h- h
  194.         DMA_Cmd(DMA1_Channel3, ENABLE);
    : G6 |9 ~4 T' R, @
  195. }
    + X* u' T* }4 [/ i

  196. , w8 U0 u" G, h4 r% X, x
  197. uint16_t bsp_uart1_get_dmarx_buf_remain_size(void)
    ) T. i) K* @$ u' J3 g3 F- H2 M1 ]+ q
  198. {" Z9 n) c! ~+ G6 |; @3 A
  199.         return DMA_GetCurrDataCounter(DMA1_Channel3);        /* 获取DMA接收buf剩余空间 */& |" U3 Z7 {5 T7 H
  200. }
    6 X. m' T% t: O$ f: m; p* M
  201. ! t$ t8 D: o: U$ s' k4 M: A) X
  202. void bsp_uart2_dmatx_config(uint8_t *mem_addr, uint32_t mem_size)
    # ?# W. F- D. F' |
  203. {
    : f+ n! [" U4 k% l) U) o. ]- i- v
  204.           DMA_InitTypeDef DMA_InitStructure;
    8 b7 ~" z& Q/ c( s+ `9 N) E7 v
  205.        
    5 N8 q  B! z0 P/ f8 a7 |
  206.         DMA_DeInit(DMA1_Channel4);9 L8 @( }  \' V
  207.         DMA_Cmd(DMA1_Channel4, DISABLE);$ K: b5 k4 L3 c% R) P3 K- C0 m/ c
  208.         DMA_InitStructure.DMA_PeripheralBaseAddr         = (uint32_t)&(USART2->TDR);
    ' j- Z, w3 I6 w
  209.         DMA_InitStructure.DMA_MemoryBaseAddr                 = (uint32_t)mem_addr; + s5 L; k$ p& q+ Z5 X
  210.         DMA_InitStructure.DMA_DIR                                         = DMA_DIR_PeripheralDST;         /* 传输方向:内存->外设 */
    * q' {/ O. o7 D# |5 a8 m1 m  i
  211.         DMA_InitStructure.DMA_BufferSize                         = mem_size; 8 G) H0 J4 Z+ I4 A
  212.         DMA_InitStructure.DMA_PeripheralInc                 = DMA_PeripheralInc_Disable; + ^3 K: f" b0 W" `+ E% ~  s
  213.         DMA_InitStructure.DMA_MemoryInc                         = DMA_MemoryInc_Enable;
    5 e- s2 p1 Y+ Y; Z- Q
  214.         DMA_InitStructure.DMA_PeripheralDataSize         = DMA_PeripheralDataSize_Byte; " I' W, Z4 @5 u# A7 _! P
  215.         DMA_InitStructure.DMA_MemoryDataSize                 = DMA_MemoryDataSize_Byte;4 `% Q( D8 U7 |- t. N  D
  216.         DMA_InitStructure.DMA_Mode                                         = DMA_Mode_Normal;
    9 k- j8 i0 M* M! J! {1 b! Y
  217.         DMA_InitStructure.DMA_Priority                                 = DMA_Priority_High;   f$ e0 Z2 e& @3 o8 L: Z; Q9 B8 p
  218.         DMA_InitStructure.DMA_M2M                                         = DMA_M2M_Disable; 9 G; b$ D3 F" d3 i$ M8 r
  219.         DMA_Init(DMA1_Channel4, &DMA_InitStructure);  
    ' z( ~% M6 b) J9 O3 U5 y1 G
  220.         DMA_ITConfig(DMA1_Channel4, DMA_IT_TC|DMA_IT_TE, ENABLE); ! H& Z' B3 B9 ]: |% t" J! A# r9 Q; Z
  221.         DMA_ClearFlag(DMA1_IT_TC4);        /* 清除发送完成标识 */
    2 Y/ V9 U& |6 c! |# o9 O( a9 s* T
  222.         DMA_Cmd(DMA1_Channel4, ENABLE);
    & x5 [6 u2 y" \" N
  223. }3 V4 i2 T9 c) y' Z/ ~4 j
  224. 9 T+ P; O( Z* }* T
  225. void bsp_uart2_dmarx_config(uint8_t *mem_addr, uint32_t mem_size)
    6 b* h- \! ]/ y" ~1 Q8 D8 ~# U
  226. {
    ) M# _. K* V: M, m) z* A
  227.           DMA_InitTypeDef DMA_InitStructure;, D8 y, x! R- q' z2 r
  228.         " C  f1 Q+ D; I, b' ]  P1 K2 m
  229.         DMA_DeInit(DMA1_Channel5);
    # c- v' a1 ]  G2 D" A2 j  u' X5 C3 `
  230.         DMA_Cmd(DMA1_Channel5, DISABLE);
    7 G0 H) @' N  l. g0 z
  231.         DMA_InitStructure.DMA_PeripheralBaseAddr         = (uint32_t)&(USART2->RDR);
      Z( S! F. L& R6 B$ d- e; E: ~* N$ j# U0 A
  232.         DMA_InitStructure.DMA_MemoryBaseAddr                 = (uint32_t)mem_addr;   ?% m* u2 p, v
  233.         DMA_InitStructure.DMA_DIR                                         = DMA_DIR_PeripheralSRC;         /* 传输方向:外设->内存 */
    * J; b8 q% i, z+ u
  234.         DMA_InitStructure.DMA_BufferSize                         = mem_size; + N/ h! D  v, S: \' k9 R
  235.         DMA_InitStructure.DMA_PeripheralInc                 = DMA_PeripheralInc_Disable; 5 b; R; l5 M3 @- K$ X( i
  236.         DMA_InitStructure.DMA_MemoryInc                         = DMA_MemoryInc_Enable;
    % |4 t; `5 z) @2 _% p
  237.         DMA_InitStructure.DMA_PeripheralDataSize         = DMA_PeripheralDataSize_Byte; 6 K7 \' g; J( X4 N
  238.         DMA_InitStructure.DMA_MemoryDataSize                 = DMA_MemoryDataSize_Byte;
      X0 D4 T* j" W$ |& _
  239.         DMA_InitStructure.DMA_Mode                                         = DMA_Mode_Circular; 8 m3 j# t  x' y/ r+ J
  240.         DMA_InitStructure.DMA_Priority                                 = DMA_Priority_VeryHigh;
    % z2 P. {7 A* u% p' [% G" E
  241.         DMA_InitStructure.DMA_M2M                                         = DMA_M2M_Disable; $ P& m5 G) Y( n1 @% X" H0 A% H/ d
  242.         DMA_Init(DMA1_Channel5, &DMA_InitStructure); 3 r5 r1 h# D9 ?
  243.         DMA_ITConfig(DMA1_Channel5, DMA_IT_TC|DMA_IT_HT|DMA_IT_TE, ENABLE);/* 使能DMA半满、全满、错误中断 */
    * B/ l. _9 G) s: R+ [4 r
  244.         DMA_ClearFlag(DMA1_IT_TC5);
    & I1 {9 F  a: S% t( ^/ ~% z
  245.         DMA_ClearFlag(DMA1_IT_HT5);
    : P. o! l$ ^& w, Z1 L5 {
  246.         DMA_Cmd(DMA1_Channel5, ENABLE); 2 V" v6 p$ V9 r
  247. }4 Q1 g, J. [$ T* |
  248. 7 C- d: B* Q6 Z3 j9 X' d$ E
  249. uint16_t bsp_uart2_get_dmarx_buf_remain_size(void)
    + E6 V: c# Y/ p; F
  250. {
    ( B1 x+ G/ r  x
  251.         return DMA_GetCurrDataCounter(DMA1_Channel5);        /* 获取DMA接收buf剩余空间 */2 S& w! |/ R- p, _
  252. }
复制代码
, |! R1 W/ m: b% A9 C


$ F1 k; {2 [/ u+ O) V. c

压力测试:

  • 1.5Mbps波特率,串口助手每毫秒发送1k字节数据,stm32f0 DMA接收数据,再通过DMA发送回串口助手,毫无压力。
  • 1.5Mbps波特率,可传输大文件测试,将接受数据保存为文件,与源文件比较。
  • 串口高波特率测试需要USB转TLL工具及串口助手都支持才可行,推荐CP2102、FT232芯片的USB转TTL工具。
    - Q; p* T( v# z& J
    ( d# F4 B. M" g. U
9 K/ D- D1 @; s3 b+ s0 {. L7 Z

7.png


; c5 J+ o& Y5 ~% T$ W) s
1.5Mbps串口回环压力测试

7 O- T2 J/ M7 n5 T; y  K

7 y  N/ ^. U$ _! T- h$ `* Q( U* e! Z* z" P+ _* `# m' q
收藏 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 手机版