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

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

[复制链接]
STMCU-管管 发布时间:2020-9-15 15:49
1 前言' P# I3 ~/ r% p/ z

: W8 L$ j7 Y2 c9 r. [+ L

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

  • 内存—>内存,内存间拷贝
  • 外设—>内存,如uart、spi、i2c等总线接收数据过程
  • 内存—>外设,如uart、spi、i2c等总线发送数据过程
    7 Y" C) `) h* E
    $ c, a# ~6 p, ]: E& @
) ^' I& o+ Y' b) F- x4 X4 ]
2 串口有必要使用DMA吗
: _) [6 E+ l& b1 k! _4 H: m

8 f4 U9 d0 G! w$ d& X  U/ B+ \% X# c3 B* y- ^5 @

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


9 y! X( S5 e+ n; j( y4 v. ^

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

  • 对于发送,使用循环发送,可能阻塞线程,需要消耗大量CPU资源“搬运”数据,浪费CPU
  • 对于发送,使用中断发送,不会阻塞线程,但需浪费大量中断资源,CPU频繁响应中断;以115200bps波特率,1s传输11520字节,大约69us需响应一次中断,如波特率再提高,将消耗更多CPU资源
  • 对于接收,如仍采用传统的中断模式接收,同样会因为频繁中断导致消耗大量CPU资源
    + P7 ]( c4 t* r2 C
    + q6 o5 H, D' |7 V. ^& I" i

% }% t' f! L7 d

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


" X5 d! j& D1 d" e/ C( n+ I3 实现方式
3 H! w) [' Q, Q  N& D5 x
& d4 Q+ B& X) P! {0 k

1 (2).png


6 Y9 K; ?# T8 `* k6 \3 L
1 E; [; j+ }# K5 z' r4 STM32串口使用DMA
5 L3 Y/ j: ~* g& M: J& G
# e9 [, i* [/ j0 i* A
# A1 l, g7 i1 m8 t+ L  c$ x8 V8 ?

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

0 C1 W2 S: }' L- V+ h! K; F4 e; Z) T

测试平台:

  • STM32F030C8T6
  • UART1/UART2
  • DMA1 Channel2—Channel5
  • ST标准库
  • 主频48MHz(外部12MHz晶振)& D2 m$ G3 k( `. G; H% W4 g  N
$ B) b4 M) I; y9 {7 I2 `# t
3 w2 Q+ w  N! ?/ S  _
2.png

3 M- s; n8 |: |$ W6 X& e


2 ]/ s. W# p* o- a

5 串口DMA接收
" Z* X& |* s: _2 u1 x2 F+ {+ [8 U: X/ n" M( _; I$ E
5.1 基本流程
  F3 _9 E8 t: u9 ?) p" N" H

3.png

5 s8 Q3 q/ X3 ^# |; T
串口接收流程图
& X' x) S$ o# M
# Z9 L# P  W; O2 F& V- ]
5.2 相关配置
) f. A2 g4 F+ B2 ^
, f4 t9 S! E2 `% m" [

关键步骤

【1】初始化串口

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

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

) Y6 ?* D8 h5 X/ y/ ]

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


9 G+ S8 g- {. g+ \; F1 x" m' c

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

4 H% s/ l7 l- P3 p+ {% d

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


& |$ }9 y- `* z8 T' Z6 m* F. ?

【1】第一步,DMA先将数据搬运到buf1,搬运完成通知CPU来拷贝buf1数据
: v) b  F+ p, s: u( x【2】第二步,DMA将数据搬运到buf2,与CPU拷贝buf1数据不会冲突
4 O0 e$ y& h2 d! b% Z" A【3】第三步,buf2数据搬运完成,通知CPU来拷贝buf2数据
4 n! u* Y& K) O. ]1 V2 E【4】执行完第三步,DMA返回执行第一步,一直循环

6 O1 I+ T8 H* p; I6 x$ L/ a( f

9 r- l- c- R" y/ C/ u$ |. g

4.png

6 L! R5 K4 n2 Y  |5 [4 @

& t* J1 B8 z2 U. }% R& t双缓存DMA数据搬运过程5 n# |5 V, O+ y7 s- N

) o: [- w/ P" |' \/ K/ D# A& {$ j

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


% M1 }' {. z6 Q& j# X* A) w

【1】第一步,DMA将数据搬运完成buf的前一半时,产生“半满中断”,CPU来拷贝buf前半部分数据
6 s9 h0 S7 S  f- E/ f( p: w: m【2】第二步,DMA继续将数据搬运到buf的后半部分,与CPU拷贝buf前半部数据不会冲突
& J/ x9 R, |! _【3】第三步,buf后半部分数据搬运完成,触发“溢满中断”,CPU来拷贝buf后半部分数据  d, h0 G6 x1 i; z# I% b
【4】执行完第三步,DMA返回执行第一步,一直循环


  B3 z/ V/ i. f9 n5 e" a- _( @

1 ]: |: X# g5 e: e* c4 j, T. w: Z; a& S) V4 V, r# f& v

5.png

7 S) H; Z' `; ~% s6 {1 m4 p/ }5 J' _

$ c$ Z6 z* {% Z
使用半满中断DMA数据搬运过程

6 q1 g2 Y& `/ H2 _! {5 G
* ]% o! d) v# l1 X* ]

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

  • 串口接收,DMA通道工作模式设为连续模式
  • 使能DMA通道接收buf半满中断、溢满(传输完成)中断
  • 启动DMA通道前清空相关状态标识,防止首次传输错乱数据
    % N/ U! p) X5 Q" U/ u
+ H  i( w4 S' c5 K8 o' D6 q& H
" b$ V0 `( G# S. v2 f

& V  Y; [- W6 H

+ U  T  y4 t* h( k6 \
  1. void bsp_uart2_dmarx_config(uint8_t *mem_addr, uint32_t mem_size)
    6 I8 b2 i& U7 ~$ N6 ^
  2. {
      S: j& ]8 X& ]/ a* _
  3.           DMA_InitTypeDef DMA_InitStructure;/ n: S1 J. {) h3 _
  4.         / [+ [: U" U8 ]; Q8 Q$ |
  5.         DMA_DeInit(DMA1_Channel5); 6 ?4 l( z, W2 z; q: y
  6.         DMA_Cmd(DMA1_Channel5, DISABLE);% e- n! u- x- K4 y7 o& ~- V
  7.         DMA_InitStructure.DMA_PeripheralBaseAddr         = (uint32_t)&(USART2->RDR);/* UART2接收数据地址 */6 X) z1 V, ^7 a- c
  8.         DMA_InitStructure.DMA_MemoryBaseAddr                 = (uint32_t)mem_addr; /* 接收buf */
    ) c+ I8 G8 @0 o- j  W) Q6 N
  9.         DMA_InitStructure.DMA_DIR                                         = DMA_DIR_PeripheralSRC;         /* 传输方向:外设->内存 */( C* u2 D+ m6 K0 y( P
  10.         DMA_InitStructure.DMA_BufferSize                         = mem_size; /* 接收buf大小 */
    / ~7 ]* u4 M) I" a
  11.         DMA_InitStructure.DMA_PeripheralInc                 = DMA_PeripheralInc_Disable;
    $ |) u( }$ r# x' |$ @) I# n
  12.         DMA_InitStructure.DMA_MemoryInc                         = DMA_MemoryInc_Enable;
    : i* C( x* P9 `/ m9 T) s9 {8 C; d
  13.         DMA_InitStructure.DMA_PeripheralDataSize         = DMA_PeripheralDataSize_Byte;
    , G. o* q7 Y; m. M' M" m9 O
  14.         DMA_InitStructure.DMA_MemoryDataSize                 = DMA_MemoryDataSize_Byte;* h9 }- B2 _& N
  15.         DMA_InitStructure.DMA_Mode                                         = DMA_Mode_Circular; /* 连续模式 */
    ' ]; W" U+ x6 M+ w. E9 l6 Z
  16.         DMA_InitStructure.DMA_Priority                                 = DMA_Priority_VeryHigh;
    3 e7 \8 T& R( i, X0 @# p/ y
  17.         DMA_InitStructure.DMA_M2M                                         = DMA_M2M_Disable;
    ( _) H6 F/ S' {! k9 H1 H
  18.         DMA_Init(DMA1_Channel5, &DMA_InitStructure);
    , Q& S( V8 @& T) u3 Z/ {. K$ w; W9 X
  19.         DMA_ITConfig(DMA1_Channel5, DMA_IT_TC|DMA_IT_HT|DMA_IT_TE, ENABLE);/* 使能DMA半满、溢满、错误中断 */+ [* G0 Y. m& |' m" a+ S
  20.         DMA_ClearFlag(DMA1_IT_TC5);        /* 清除相关状态标识 */+ N+ f' f7 v1 ~* w
  21.         DMA_ClearFlag(DMA1_IT_HT5);
    3 U9 d/ c$ W$ d8 @( V" i
  22.         DMA_Cmd(DMA1_Channel5, ENABLE);
    8 ]7 [! X1 j: y2 E
  23. }
复制代码

+ s; J# K1 i- {* F7 z" H; o  s' f7 @) A# Y6 M

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


, K/ [, [* g& `  w8 A0 }5.3 接收处理
' M7 {3 ]* d2 q1 [

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

  • DMA通道buf溢满(传输完成)场景
  • DMA通道buf半满场景
  • 串口空闲中断场景
    5 s) f3 ]7 W5 n

    ; B. d1 G5 r/ S: `6 {/ ]7 k

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

, {# Y% b5 k" p/ q2 R6 t
5.3 .1 接收数据大小

8 t1 D: X9 U& b- U- h6 Y- _" X

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

  • 数据刚好是DMA接收buf的整数倍,这是理想的状态
  • 数据量小于DMA接收buf或者小于接收buf的一半,此时会触发串口空闲中断/ B; _3 o+ C- H$ {% |
    / T# j* w; ?; a: S' z

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

  1. /* 获取DMA通道接收buf剩余空间大小 */! H* a3 w2 v& S/ l; S1 }7 d
  2. uint16_t DMA_GetCurrDataCounter(DMA_Channel_TypeDef* DMAy_Channelx);
复制代码

) |8 A' S" l8 s) R4 {1 I

DMA通道buf溢满场景计算


5 p5 y2 S- @( A+ i. f; w. g/ V5 X

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

, V( S4 }9 I2 w% Q
8 V0 M; d. P4 z7 ?

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

  1. void uart_dmarx_done_isr(uint8_t uart_id)
    & R: E! E% j1 X+ L) l$ ^  w/ L, @
  2. {
    $ g$ o$ Z3 K% F5 l+ O$ e& k
  3.           uint16_t recv_size;2 S) `9 p- J# T! W4 e
  4.         & H, Z* o& e) i* i
  5.         recv_size = s_uart_dev[uart_id].dmarx_buf_size - s_uart_dev[uart_id].last_dmarx_size;
    * {( P% t( W3 r- T  M5 o

  6. 0 Y* X$ }  u) |( r5 [* u1 @
  7.         fifo_write(&s_uart_dev[uart_id].rx_fifo, & V- q7 P8 O2 d" `2 x# f* M
  8.                                    (const uint8_t *)&(s_uart_dev[uart_id].dmarx_buf[s_uart_dev[uart_id].last_dmarx_size]), recv_size);" V7 U# w3 ^7 C$ Y: ?

  9. / d0 P. W5 @$ r9 O9 ^2 {
  10.         s_uart_dev[uart_id].last_dmarx_size = 0;+ V' \, m, F3 ?0 r7 ~5 `
  11. }
复制代码

- m2 i/ q2 r* Q+ D5 {2 s

DMA通道buf半满场景计算

+ r' h. f2 P) s# p& |/ j5 }3 n

  1. 接收数据大小 = DMA通道接收总数据大小 - 上一次接收的总数据大小0 J5 O1 o! h% z% f' U- j5 z# z
  2. DMA通道接收总数据大小 = DMA通道buf大小 - DMA通道buf剩余空间大小
复制代码
4 h+ X* ]8 ?/ c. p

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

: r$ ^' y3 N! W7 @

  1. void uart_dmarx_half_done_isr(uint8_t uart_id)6 l1 D1 J: |% ^- f* [5 d! K# Z6 u
  2. {% n( r5 O& G; I4 I1 [! h  p0 V
  3.           uint16_t recv_total_size;
    : S# B6 j- X: a" a4 r, ~
  4.           uint16_t recv_size;
    7 p% \9 J2 b: Y& s' B9 t3 O
  5.         5 ~0 e& M  Z, W, C2 N& n
  6.         if(uart_id == 0)
    ! ~3 `: q9 L3 z# G# F9 L
  7.         {9 j! n) c& u( U& W1 |
  8.                   recv_total_size = s_uart_dev[uart_id].dmarx_buf_size - bsp_uart1_get_dmarx_buf_remain_size();3 k# h+ X9 C6 _: ~7 }+ p/ w
  9.         }
    & d2 _5 Z) {! M7 g8 `$ U6 `' g
  10.         else if (uart_id == 1)& p1 L' `1 c. y; C1 {4 y
  11.         {8 E$ G# v' M5 y3 T$ c
  12.                 recv_total_size = s_uart_dev[uart_id].dmarx_buf_size - bsp_uart2_get_dmarx_buf_remain_size();
    / M* p  N8 O( P* \
  13.         }
    2 \( h. ]) z; c0 M& O* @( g6 D: b
  14.         recv_size = recv_total_size - s_uart_dev[uart_id].last_dmarx_size;9 e3 u$ l' g; |0 F1 O5 C8 k
  15.         ) B5 [2 e7 T3 u  Z
  16.         fifo_write(&s_uart_dev[uart_id].rx_fifo,
    * Y, v) Q4 s8 f! J# _* h9 O
  17.                                    (const uint8_t *)&(s_uart_dev[uart_id].dmarx_buf[s_uart_dev[uart_id].last_dmarx_size]), recv_size);7 D6 {. O$ n, S
  18.         s_uart_dev[uart_id].last_dmarx_size = recv_total_size;/* 记录接收总数据大小 */, O$ o9 `: }$ \: y: X9 U
  19. }
复制代码
- L+ O) C5 a- i
0 f% ^3 J0 k: I! ~

串口空闲中断场景计算

1 j1 H! Q+ p4 e  c3 M: ~$ d

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

串口空闲中断处理函数:

注:) e, w9 Z, e/ e9 I- G
串口空闲中断处理函数,除了将数据拷贝到串口接收fifo中,还可以增加特殊处理,如作为串口数据传输完成标识、不定长度数据处理等等。


" H6 L" u" a. `/ _" Y 5.3.2 接收数据偏移地址

1 m) Z' ?6 H% M( _

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

# o9 g3 t/ d2 j, ~1 H' m7 v

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

* W9 s& k/ V/ p1 Q. G0 E& t

  1. void uart_dmarx_done_isr(uint8_t uart_id)- U# O- ]: \0 n% H1 G
  2. {2 @5 l' g' _; |9 x7 g7 A& @2 i7 ^
  3.         /* todo */: U. a( X  o. K" [
  4.         s_uart_dev[uart_id].last_dmarx_size = 0;
    0 W* ~$ S! s9 Z' i7 H' u
  5. }
复制代码
3 Z2 o, h- _0 P) J7 Z
$ p; g5 Y6 @6 v: d
5.4 应用读取串口数据方法
6 V/ j! e! \, ?& ^8 I3 A$ I

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

# q& _1 l* [* F
6 串口DMA发送
, z7 b8 ~: t7 l4 b" v
9 T( m& V4 e$ t% b+ l
5.1 基本流程
+ A6 b4 y- V( |5 c! e8 f

6.png

1 `" Q+ m' q$ l1 C+ s  Y$ T9 E

' {+ z* ~. L7 Q1 w) x
串口发送流程图
. P: j$ [5 c+ j- p! G& H3 L

9 {( z1 {, e' n& v) v2 z; r9 R( J: r% N1 ^; I! S  g; A
5.2 相关配置
1 n' p8 `/ A2 A5 e5 w6 c# x

关键步骤


  \) q  C! J9 z- s

【1】初始化串口


; m" I3 r  n2 X' R2 e

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

9 Q  e3 e7 |4 A6 O+ D- [7 q

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

; q$ N! W; y- [0 E$ |( f

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

( T& b+ |6 w' D, }1 V

  • 串口发送是,DMA通道工作模式设为单次模式(正常模式),每次需要发送数据时重新配置DMA
  • 使能DMA通道传输完成中断,利用该中断信息处理一些必要的任务,如清空发送状态、启动下一次传输
  • 启动DMA通道前清空相关状态标识,防止首次传输错乱数据
    ; k% G3 S3 Q) Q- G4 D3 W0 C! O
    1 }# g" U$ P! k, d: T

  • % X5 t! A! Q) ]0 v9 w( k
    & y4 R7 ~6 a/ P, o0 e9 G: K0 z

+ _7 B% p# L+ h; C9 X6 R& h5.3 发送处理

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

串口发送处理函数:

, [. A: i% ?4 m' l- T

  1. void uart_poll_dma_tx(uint8_t uart_id)
    9 h% L$ T  k/ ]0 U, q# \
  2. {
    2 T+ d6 B' l% P) h) ^
  3.           uint16_t size = 0;2 S1 l- P2 ?/ J
  4.        
    $ e6 h, [- W  n
  5.         if (0x01 == s_uart_dev[uart_id].status)
    6 ]& ]/ j+ }, m, `9 G0 j6 z
  6.     {, y# n) c8 S' p9 Z- F
  7.         return;
    ) s  E; C2 r) r- ?
  8.     }
    3 B$ t  z5 t1 s8 H
  9.         size = fifo_read(&s_uart_dev[uart_id].tx_fifo, s_uart_dev[uart_id].dmatx_buf,) \7 C, X6 V, {. d; `1 p% V- l
  10.                                          s_uart_dev[uart_id].dmatx_buf_size);
    1 |* d- f& w0 o* m
  11.         if (size != 0)$ b9 n- W( @4 X$ l' Z5 a
  12.         {5 }: G, r# M/ i
  13.         s_UartTxRxCount[uart_id*2+0] += size;
    & H- B! l% u# |5 E& l, V0 |' n
  14.                   if (uart_id == 0)
    ' l/ n% x: v, U6 N8 g8 N
  15.                 {
    5 `/ d" F# s6 M$ E$ N. w5 D
  16.             s_uart_dev[uart_id].status = 0x01;        /* DMA发送状态 */
    7 M! S6 h; {6 x7 ]7 X
  17.                           bsp_uart1_dmatx_config(s_uart_dev[uart_id].dmatx_buf, size);4 Z" R3 N5 p% `+ i$ q
  18.                 }4 a; u+ m/ y0 H$ X- R
  19.                 else if (uart_id == 1)1 ]0 H# U, Y% Q: j; W
  20.                 {0 p* O+ z( G' c5 ^! f
  21.             s_uart_dev[uart_id].status = 0x01;        /* DMA发送状态,必须在使能DMA传输前置位,否则有可能DMA已经传输并进入中断 */8 q1 R$ a/ n4 _) \  p9 N
  22.                         bsp_uart2_dmatx_config(s_uart_dev[uart_id].dmatx_buf, size);  t% h8 C" N! j/ d
  23.                 }- G  _$ x+ J! j* z1 k
  24.         }; q6 ]& H+ D, j# t5 X
  25. }
复制代码

; W  j( c% K4 l1 r

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

1 q9 v5 W: p2 M* h! C) \; i9 O


* _) w& O( s$ i# _) o" }

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


9 [: ]# K2 C' L9 T

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

  1. void uart_dmatx_done_isr(uint8_t uart_id)
    ; Z6 G2 K' s! k0 k5 z) A- U
  2. {
    8 a, l2 s2 [4 V' @
  3.         s_uart_dev[uart_id].status = 0;        /* 清空DMA发送状态标识 */$ _* Q* g* c0 Q' K' k
  4. }
复制代码

1 R0 ?0 `* u) I3 ^3 r" [

2 ~, Z: g9 J' ], J; |, w


  L. ^8 L' \+ p2 L9 X

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

" l1 k/ c: B3 g' |3 M


' [. W  Z2 @! z$ ^7 v

  • 主线程任务调用,前提是线程不能被其他任务阻塞,否则导致fifo溢出. B! Q( O9 {; t  _$ n7 `7 c5 o1 m

    ) t+ y8 C9 [) H: e$ g  X% i
  1. void thread(void)
    7 ?; Q; L! Y0 Y5 n9 E2 b
  2. {
    ) ?6 C7 u5 U( h+ i6 _4 ?1 S" O+ o
  3.     uart_dmatx_done_isr(DEV_UART1);
    ! g3 x& R! w$ k& _$ P
  4.     uart_dmatx_done_isr(DEV_UART2);$ P6 h6 q& }7 t
  5. }
复制代码
$ s) {  V! p8 `9 S

4 p9 x1 O7 S& t1 [
  • 定时器中断中调用7 k3 k8 |6 o5 g) [6 p
  1. void TIMx_IRQHandler(void)
    6 J) ^2 {- H  ~
  2. {3 a& Y; ]3 q2 \* f6 D# f2 @8 G
  3.     uart_dmatx_done_isr(DEV_UART1);
    ' W1 s$ g1 r  Z) [
  4.     uart_dmatx_done_isr(DEV_UART2);+ v2 h9 p$ v- n3 w$ A' S
  5. }
复制代码
3 S8 h/ a9 ]# g9 G

0 Q1 A0 j& \/ b. C* l) ~5 V
  • DMA通道传输完成中断中调用  z5 B# U7 K& Z9 G2 Z
    ) x  t2 h2 e7 ?9 J, G
  1. void DMA1_Channel4_5_IRQHandler(void)
    0 O7 g) g# U! o1 V' c# ~6 A
  2. {/ [9 n; m+ M6 Z9 E/ [* M$ G. A
  3.         if(DMA_GetITStatus(DMA1_IT_TC4))* k8 J! W- P9 K
  4.         {  Q1 }& s# Y! n
  5.                 UartDmaSendDoneIsr(UART_2);7 l8 o, o' U- ~: D; ?& b/ W. F1 F
  6.                 DMA_ClearFlag(DMA1_FLAG_TC4);
    ( ~8 M1 Z0 o  K' d8 a
  7.                 uart_dmatx_done_isr(DEV_UART2);* ]2 n" g& x( \) T. }4 F2 Z
  8.         }, }6 x$ o. r! T% I* C* E
  9. }
复制代码

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

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

  • 周期查询发送fifo数据,启动DMA传输,充分利用DMA发送效率,但可能降低串口数据流实时性
  • 实时查询发送fifo数据,加上超时处理,理想的方法
  • 在DMA传输完成中断中处理,保证实时连续数据流# x8 `5 h1 i4 K4 V6 C3 I' I5 ^; L1 L7 e

    5 G  L% S! u1 S- m  X- S

. J8 P5 Z8 M9 A# T' g. q6 串口设备

. W& f$ c" \2 ]- l; a( e* W; M( z  s- B+ \) }( B
6.1 数据结构
. M/ }- S2 [( c
  1. /* 串口设备数据结构 */
    7 Q9 z$ x8 \+ o4 {( P
  2. typedef struct+ `" C8 a4 E0 j" t
  3. {# W9 Q2 M$ c4 d, L6 [
  4.         uint8_t status;                        /* 发送状态 */, x- d4 ?! E( b9 P- [
  5.         _fifo_t tx_fifo;                /* 发送fifo */
    8 a! p2 C' }! e+ ]! P: V/ G0 D
  6.         _fifo_t rx_fifo;                /* 接收fifo */0 D$ b3 M4 A) B- g1 A4 w% Z6 o
  7.         uint8_t *dmarx_buf;                /* dma接收缓存 */, g& i8 X, e5 z3 n+ F+ ^$ R* d7 m
  8.         uint16_t dmarx_buf_size;/* dma接收缓存大小*/
    8 J9 b& [& f; E) W
  9.         uint8_t *dmatx_buf;                /* dma发送缓存 */
    ( R3 ]$ q5 ^+ s" i4 U8 ]& {3 e
  10.         uint16_t dmatx_buf_size;/* dma发送缓存大小 */
    ; O, o! K) f9 d2 K. Q+ j
  11.         uint16_t last_dmarx_size;/* dma上一次接收数据大小 */
    % p1 A$ ^( r, a. t: }! @, b! a: L2 v! O
  12. }uart_device_t;
复制代码
4 H  U4 @9 U) g8 l* @
' c+ s% Z- o  R1 K0 ]
6.2 对外接口

& S2 h1 w/ k: z5 C  S) b; w( I" i6 R) T  d  z9 A
  1. /* 串口注册初始化函数 */
    & p0 C* J% C. b# {- v/ j! i
  2. void uart_device_init(uint8_t uart_id)
    # |# v& {9 |$ _
  3. {
    . ^/ t- f+ e) d7 W% u" m
  4.           if (uart_id == 1)# v: |4 k& @- ^7 `/ Y: g
  5.         {
    - P. p4 z0 G" b
  6.                 /* 配置串口2收发fifo */8 g2 g: u) c6 R* [* k: d0 q( W
  7.                 fifo_register(&s_uart_dev[uart_id].tx_fifo, &s_uart2_tx_buf[0],
    0 R0 [# k1 t. d8 D3 W* v
  8.                       sizeof(s_uart2_tx_buf), fifo_lock, fifo_unlock);) V: z5 x/ F+ }( r; _7 I
  9.                 fifo_register(&s_uart_dev[uart_id].rx_fifo, &s_uart2_rx_buf[0], ! p  \, I4 r" ~) X9 s0 J: ?
  10.                       sizeof(s_uart2_rx_buf), fifo_lock, fifo_unlock);. C8 Q' |( O( E3 w
  11.                
    ( s( u  x+ D- x4 X2 H. l1 l
  12.                 /* 配置串口2 DMA收发buf */
    ) m$ a. c% M" r# x& F( u$ v- g
  13.                 s_uart_dev[uart_id].dmarx_buf = &s_uart2_dmarx_buf[0];
    8 K4 v! V. r9 y
  14.                 s_uart_dev[uart_id].dmarx_buf_size = sizeof(s_uart2_dmarx_buf);  |# L3 l" {# w7 h8 l
  15.                 s_uart_dev[uart_id].dmatx_buf = &s_uart2_dmatx_buf[0];8 W# h3 M  d5 b+ X: A. |
  16.                 s_uart_dev[uart_id].dmatx_buf_size = sizeof(s_uart2_dmatx_buf);7 _  }/ z2 a7 P- m) N1 V
  17.                 bsp_uart2_dmarx_config(s_uart_dev[uart_id].dmarx_buf,
    : K8 {, R3 @& A
  18.                                                            sizeof(s_uart2_dmarx_buf));3 t" ?9 B3 H3 [  ~2 Z: L4 r
  19.                 s_uart_dev[uart_id].status  = 0;
    " ?9 J. ?2 A; t
  20.         }
    & @: \* v  r0 |; ^
  21. }
    2 }) v+ ~6 U& E9 ?9 k
  22. # D( }) C& H5 G5 {/ V4 T: W1 _
  23. /* 串口发送函数 */, U# ]0 u' r* E4 v
  24. uint16_t uart_write(uint8_t uart_id, const uint8_t *buf, uint16_t size)
    5 k6 b/ r, x6 v# {0 R
  25. {9 N0 [4 H$ F+ d' |6 t3 c) A
  26.         return fifo_write(&s_uart_dev[uart_id].tx_fifo, buf, size);
    2 M. u* q$ e. N
  27. }
    3 p) X$ G9 s/ K

  28. - [# j- R+ a: @% h
  29. /* 串口读取函数 */
    1 b9 e+ ^6 w! h8 S2 H7 ]& D
  30. uint16_t uart_read(uint8_t uart_id, uint8_t *buf, uint16_t size)
    + `5 v0 m2 ?) h8 u6 K7 }
  31. {1 |4 O; k/ [: |! R" B
  32.         return fifo_read(&s_uart_dev[uart_id].rx_fifo, buf, size);( \7 ]9 r% L; c) s2 h
  33. }
复制代码

, R/ D# r" F  F! I8 完整源码; C' L. y, _. c% K6 R8 Y! C# ?

# _5 q/ B7 @' {4 n: O0 S% g# J) p( K) u

串口&DMA底层配置:

) H8 j- m& J1 H+ E: U8 b1 V

  1. #include <stddef.h>
    5 s  o) P$ V8 W% q6 Z' K4 K8 x6 l" V: F( m
  2. #include <stdint.h>/ H3 p. X" Q1 Y
  3. #include <stdbool.h>
    . E# z* q$ o9 y, M5 ]) E9 G
  4. #include "stm32f0xx.h"1 ~/ J( z: S8 p  d( r* D6 r
  5. #include "bsp_uart.h"1 j  i7 {8 l! V% K

  6.   z2 E0 X/ }  I: \" A" N) |8 ^
  7. /**1 e, {. S/ E4 p" \5 f% Q* y
  8. * @brief  
    4 e  ^2 M6 F& v
  9. * @param  ; J8 ?% j+ Q& c, d9 O9 k
  10. * @retval
    8 i' u# H  a& C4 j
  11. */  `6 `/ p6 j7 c5 D2 A
  12. static void bsp_uart1_gpio_init(void)
    + c) c5 R0 M- c7 ~% Z  I
  13. {4 `! I( d& E6 E
  14.     GPIO_InitTypeDef    GPIO_InitStructure;9 @% k, v+ b& [2 u- q: f$ }$ O0 D0 U
  15. #if 0
    4 k$ }6 \. _& x: v
  16.         RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOB, ENABLE);  r# {3 h( z" J( W- _
  17.        
    $ @+ {. S1 S6 I/ z( |
  18.     GPIO_PinAFConfig(GPIOB, GPIO_PinSource6, GPIO_AF_0);
    7 @! j# o. r/ o# Z
  19.     GPIO_PinAFConfig(GPIOB, GPIO_PinSource7, GPIO_AF_0); + P0 r0 P5 o7 p) j6 G$ y2 o* \
  20.         8 t$ D6 X! C! r0 F$ b/ Y; E
  21.         GPIO_InitStructure.GPIO_Pin         = GPIO_Pin_6 | GPIO_Pin_7;
    ; N/ s) k; l$ P4 Z- z1 q  r2 Q
  22.     GPIO_InitStructure.GPIO_Mode         = GPIO_Mode_AF;. j: }6 g8 I- [% }
  23.         GPIO_InitStructure.GPIO_OType         = GPIO_OType_PP;
      f, x7 I& Y* D2 e; g0 I% d
  24.     GPIO_InitStructure.GPIO_Speed          = GPIO_Speed_Level_3;' S0 ^) y, z: W9 u, e
  25.     GPIO_InitStructure.GPIO_PuPd         = GPIO_PuPd_UP;$ S* T$ m2 U  r3 _5 d% E
  26.     GPIO_Init(GPIOB, &GPIO_InitStructure);# n7 K3 ?$ o1 z+ D% ?
  27. #else
    4 |8 \7 N! g/ p, K9 S
  28.         RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOA, ENABLE);3 R" x4 `6 g9 a/ G* Z
  29.         / X# V( y1 B3 c3 K/ L; O
  30.     GPIO_PinAFConfig(GPIOB, GPIO_PinSource9, GPIO_AF_1);
    # X3 R* H2 t8 q( |0 E3 }
  31.     GPIO_PinAFConfig(GPIOB, GPIO_PinSource10, GPIO_AF_1);
    * h" `$ t1 H8 b  r+ M
  32.         & R8 N  K$ Q5 A1 w3 _
  33.         GPIO_InitStructure.GPIO_Pin         = GPIO_Pin_9 | GPIO_Pin_10;
    & S+ I+ l5 y+ G, z
  34.     GPIO_InitStructure.GPIO_Mode         = GPIO_Mode_AF;
    & V: J& h. Z( c8 A' |( f+ J/ F; h
  35.         GPIO_InitStructure.GPIO_OType         = GPIO_OType_PP;
    / C1 C# h% c% L3 O4 V3 t
  36.     GPIO_InitStructure.GPIO_Speed          = GPIO_Speed_Level_3;
    1 B- S" _' l0 r" e
  37.     GPIO_InitStructure.GPIO_PuPd         = GPIO_PuPd_UP;2 k1 e" L/ H+ w# |# a0 Q
  38.     GPIO_Init(GPIOA, &GPIO_InitStructure);
    . {  z& R9 W7 N2 z
  39. #endif; I1 k* X4 w' j
  40. }
    0 h9 x6 {5 h: w) ]
  41. 5 W- _0 m/ h1 E5 I8 [
  42. /**
    * ]6 v8 r+ z3 \: o# h  C; h
  43. * @brief  
    5 g, p  a% V5 X' g( J
  44. * @param  
    # B  z3 J$ f3 n# T6 N
  45. * @retval / Y3 e! [/ }& p1 F4 j! Z
  46. */0 b7 q8 \2 U- N$ n
  47. static void bsp_uart2_gpio_init(void)' X, n& D/ |( A% }
  48. {
    - Y6 L/ e$ Y- y
  49.         GPIO_InitTypeDef GPIO_InitStructure;; t! P8 O4 x: z: |; y7 I
  50.         8 U% s. z1 v/ ]$ M; K+ F
  51.         RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOB, ENABLE);1 y# R' u/ [/ |% S5 B
  52.        
    * j& D$ a! J$ H
  53.         GPIO_PinAFConfig(GPIOA, GPIO_PinSource2, GPIO_AF_1);
    9 P; ^# K, J1 }4 a$ Q
  54.         GPIO_PinAFConfig(GPIOA, GPIO_PinSource3, GPIO_AF_1);
    & `' |( l9 ?3 A& F1 j! E
  55.        
    " X, q4 R- O' {' Q0 _
  56.         GPIO_InitStructure.GPIO_Pin   = GPIO_Pin_2 | GPIO_Pin_3;
    1 m; J0 H5 B; o2 B
  57.         GPIO_InitStructure.GPIO_Mode  = GPIO_Mode_AF;
    5 a  m- a: O7 }5 B1 g6 f
  58.         GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;* V7 q$ M5 }: ~1 Z
  59.         GPIO_InitStructure.GPIO_Speed = GPIO_Speed_10MHz;
    ' e& C4 H. h4 j' B
  60.         GPIO_InitStructure.GPIO_PuPd  = GPIO_PuPd_UP;% `$ m$ r' Q! h7 R' B* p/ Q7 d
  61.         GPIO_Init(GPIOA, &GPIO_InitStructure);
    " @$ Z- U8 X7 X
  62. }
    6 T! V7 R3 C1 b3 j# L) j
  63.   n  V6 n% v; Y, u% `
  64. /**' Y! E2 ?+ L" H) _
  65. * @brief  
    - U, U7 o7 g6 B1 c4 G' T$ g
  66. * @param  
    2 e. y9 a6 K0 _  H0 Y2 l" x, j
  67. * @retval
    " x8 H# H- [" T  x1 z7 i+ l
  68. */
    & E! @8 V4 r% a% A* A, Z
  69. void bsp_uart1_init(void)
    1 b( L0 n6 v0 U+ x- A* h
  70. {
    0 s+ l+ `$ H! v6 e6 ?
  71.         USART_InitTypeDef USART_InitStructure;1 l( |9 A  a  ?1 K5 ^
  72.         NVIC_InitTypeDef NVIC_InitStructure;
    / \3 P$ m8 h0 T9 C
  73.        
    + `( i3 ~7 z/ b
  74.         bsp_uart1_gpio_init();
    ! f( S5 @* n! f& a& ^
  75.        
    9 j& }  P2 f" p, l. A
  76.         /* 使能串口和DMA时钟 */
    ; R# @$ ?" o/ G5 l
  77.         RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);
    ( |: ~  r4 v" o% Z7 o
  78.         RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);9 P( W4 x: d# [" i  Q4 k) p
  79.        
    : ~6 m' V1 D/ p4 K. J  {
  80.         USART_InitStructure.USART_BaudRate            = 57600;
    * s4 Y" ~1 E2 K% C) Y2 f9 @
  81.         USART_InitStructure.USART_WordLength          = USART_WordLength_8b;
    , f1 z& g) H1 C4 X
  82.         USART_InitStructure.USART_StopBits            = USART_StopBits_1;
    $ `8 @4 N1 O  y. q! _2 \1 _4 I5 M, [
  83.         USART_InitStructure.USART_Parity              = USART_Parity_No;5 @3 L' k3 X4 t
  84.         USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
    & y! Z2 P3 k8 w
  85.         USART_InitStructure.USART_Mode                = USART_Mode_Rx | USART_Mode_Tx;
    , F4 c' k0 C3 M7 x* D+ m, v& i% G& ^
  86.         USART_Init(USART1, &USART_InitStructure);
    & t1 i1 [  Y% y2 N$ D5 g3 R
  87.        
    3 n# f$ b2 N0 P  S" L6 b9 W, R
  88.         USART_ITConfig(USART1, USART_IT_IDLE, ENABLE);        /* 使能空闲中断 */
    - g  I4 N' l+ m3 j- A' Z* h1 C
  89.         USART_OverrunDetectionConfig(USART1, USART_OVRDetection_Disable);, J1 l8 Q) a2 T8 \) X- X
  90.         & U% `" g, q, a4 n# D& r, {
  91.         USART_Cmd(USART1, ENABLE);' f8 J' O. o# @8 J/ G
  92.         USART_DMACmd(USART1, USART_DMAReq_Rx|USART_DMAReq_Tx, ENABLE); /* 使能DMA收发 */; ~" c/ M5 P6 {; R

  93. 6 i% W) E" @4 X' f2 y) Q
  94.         /* 串口中断 */
    8 c! e% ~0 c5 O
  95.         NVIC_InitStructure.NVIC_IRQChannel         = USART1_IRQn;
    + y; |/ x# F( k) Z2 P4 Z. F
  96.         NVIC_InitStructure.NVIC_IRQChannelPriority = 2;, a: t" p( F+ Q. Q* ~
  97.         NVIC_InitStructure.NVIC_IRQChannelCmd      = ENABLE;" Y: D$ ~& R+ X& C# o1 U
  98.         NVIC_Init(&NVIC_InitStructure);  Q# {( R: h, ^8 y

  99.   \) D. {* p' l6 Y& A+ m
  100.         /* DMA中断 */1 D( T$ P% c! X
  101.           NVIC_InitStructure.NVIC_IRQChannel                    = DMA1_Channel2_3_IRQn;      
    2 C5 @4 X. A5 N) ^" n5 x
  102.           NVIC_InitStructure.NVIC_IRQChannelPriority = 0;
    4 `: n; w/ r7 ]% c8 Q
  103.         NVIC_InitStructure.NVIC_IRQChannelCmd      = ENABLE;+ E7 u1 X+ C0 q$ G5 {- j
  104.           NVIC_Init(&NVIC_InitStructure);
    ! h- Z- R5 n: W% Z6 S9 f- ^
  105. }: x6 {8 V, U( Z1 ]- ~' V) i+ U: o
  106. 3 A  c1 h9 ]2 W
  107. /**1 m- ?# o% `# b
  108. * @brief  
    % j1 |2 G9 V) C
  109. * @param  ; j: S% w& c2 Q5 C
  110. * @retval - a& N: L2 V8 S' d
  111. */
    $ u' K& d% E  W& d% y
  112. void bsp_uart2_init(void)
    . q- I2 O3 p0 v# z& J- a+ ?
  113. {. U& ^1 r0 d4 E+ W
  114.         USART_InitTypeDef USART_InitStructure;& E  B$ \) ]3 Q2 V" U
  115.         NVIC_InitTypeDef NVIC_InitStructure;
    2 h# `3 L4 y' U. ]9 ~8 I
  116.         9 E3 K6 p3 v! [3 t7 Y$ }
  117.         bsp_uart2_gpio_init();
    $ ~& c  l3 c% C: J7 j% x) l
  118.        
    # L) m8 y, K8 O; P0 P  N& a
  119.         /* 使能串口和DMA时钟 */9 ~0 T; K; f% C/ d# d4 f
  120.         RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);
    0 R! A. X5 V! |% f
  121.         RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2, ENABLE);
    ( A. e& B9 }1 F6 l8 v
  122. , v7 B2 I2 J0 p) q
  123.         USART_InitStructure.USART_BaudRate            = 57600;: g! [: f  a) E
  124.         USART_InitStructure.USART_WordLength          = USART_WordLength_8b;) f1 x; C" z6 ^! H& k" S  }- x
  125.         USART_InitStructure.USART_StopBits            = USART_StopBits_1;
    # ?9 M% q! F: T1 G0 g( h
  126.         USART_InitStructure.USART_Parity              = USART_Parity_No;. c* d5 w, X9 P9 {5 j
  127.         USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;6 A4 C3 A6 w% Y  m
  128.         USART_InitStructure.USART_Mode                = USART_Mode_Rx | USART_Mode_Tx;
    ) E1 y- t: _: z& M3 r- n+ |$ S
  129.         USART_Init(USART2, &USART_InitStructure);: Q& ^, C! p5 M! s9 p
  130.         0 K) v- j' z, k- [
  131.         USART_ITConfig(USART2, USART_IT_IDLE, ENABLE);        /* 使能空闲中断 */: s6 q! M: B' d) r- Q4 g2 p
  132.         USART_OverrunDetectionConfig(USART2, USART_OVRDetection_Disable);
    . O+ Q6 Y7 E. m
  133.         % J- t3 j  f  `0 E* U& l* A: C
  134.         USART_Cmd(USART2, ENABLE);
    : Z/ [& b  w: g
  135.         USART_DMACmd(USART2, USART_DMAReq_Rx|USART_DMAReq_Tx, ENABLE);         /* 使能DMA收发 */* ?2 o! n( W9 C. B! U

  136. 9 ?' C% j( ]- P! ~* _; t+ K2 P
  137.         /* 串口中断 */1 _; g& k! G$ q/ z8 h  ^6 I2 U
  138.         NVIC_InitStructure.NVIC_IRQChannel         = USART2_IRQn;
    * g: h3 Y; a3 ], `/ z
  139.         NVIC_InitStructure.NVIC_IRQChannelPriority = 2;
    & P- m% K4 @7 n0 _
  140.         NVIC_InitStructure.NVIC_IRQChannelCmd      = ENABLE;
    ! X* R. h% n3 ^6 n' z9 Z4 f$ [
  141.         NVIC_Init(&NVIC_InitStructure);
    ) w$ k$ j+ l: \$ x' h0 N

  142. 1 r+ d: w- E$ X( S# Z, ^: t
  143.         /* DMA中断 */
    + z/ L: g' T4 p
  144.         NVIC_InitStructure.NVIC_IRQChannel         = DMA1_Channel4_5_IRQn;       * N  M1 c4 u  m* V
  145.           NVIC_InitStructure.NVIC_IRQChannelPriority = 0; - w% o3 B7 m% l" W3 ~( a
  146.         NVIC_InitStructure.NVIC_IRQChannelCmd      = ENABLE;3 \$ ^  S2 }( w$ S
  147.           NVIC_Init(&NVIC_InitStructure);
    $ r5 L6 [* m" q) Z5 F
  148. }; {, i" z( ~- g
  149. " D7 b& W4 B% C) d9 G. I8 V
  150. void bsp_uart1_dmatx_config(uint8_t *mem_addr, uint32_t mem_size)
    * ?% s% D0 y4 E
  151. {' i# ?1 H0 u+ V6 {7 n1 C% q, Z
  152.           DMA_InitTypeDef DMA_InitStructure;
    7 }: r0 z% q) F3 Z0 d
  153.        
    - R& j' K  F' m2 s5 v/ K
  154.         DMA_DeInit(DMA1_Channel2);
    ) D( H% }% K, G1 n" J& B: X
  155.         DMA_Cmd(DMA1_Channel2, DISABLE);
    , t) x( @/ }. `) ~( X6 S2 {
  156.         DMA_InitStructure.DMA_PeripheralBaseAddr         = (uint32_t)&(USART1->TDR);
    ) n( K# H) K! N# j
  157.         DMA_InitStructure.DMA_MemoryBaseAddr                 = (uint32_t)mem_addr;
    / A5 e3 r' D2 q/ k% q3 z
  158.         DMA_InitStructure.DMA_DIR                                         = DMA_DIR_PeripheralDST;         /* 传输方向:内存->外设 */! F4 d" \$ g! c1 b9 L! z
  159.         DMA_InitStructure.DMA_BufferSize                         = mem_size;
    ' `- E' f% {: s
  160.         DMA_InitStructure.DMA_PeripheralInc                 = DMA_PeripheralInc_Disable;
    ( k+ J- l2 ]; C6 {  s. Y* Q, p
  161.         DMA_InitStructure.DMA_MemoryInc                         = DMA_MemoryInc_Enable; 5 l1 @$ I" a* i
  162.         DMA_InitStructure.DMA_PeripheralDataSize         = DMA_PeripheralDataSize_Byte;
    ; x6 u% y  }; c* H
  163.         DMA_InitStructure.DMA_MemoryDataSize                 = DMA_MemoryDataSize_Byte;
    " G2 E6 d$ T* l5 @- q. ^
  164.         DMA_InitStructure.DMA_Mode                                         = DMA_Mode_Normal; % [* p) i' \2 Z- Z3 e/ a
  165.         DMA_InitStructure.DMA_Priority                                 = DMA_Priority_High;
    : h( w! ]% k+ z( u
  166.         DMA_InitStructure.DMA_M2M                                         = DMA_M2M_Disable;
    ( ~% [+ z0 ~3 ~8 n# }$ Q
  167.         DMA_Init(DMA1_Channel2, &DMA_InitStructure);  + n: ]" d$ d# q8 L3 x8 |
  168.         DMA_ITConfig(DMA1_Channel2, DMA_IT_TC|DMA_IT_TE, ENABLE); 2 O, Z& \' i8 U5 C8 P
  169.         DMA_ClearFlag(DMA1_IT_TC2);        /* 清除发送完成标识 *// o3 U8 G) F  S- g3 E
  170.         DMA_Cmd(DMA1_Channel2, ENABLE);
    7 u1 q' n/ x7 r- M5 i/ K3 \/ ]8 F
  171. }
    9 L7 K% Y3 x9 w) R
  172. , K  h/ \, {; f" S- m% Q
  173. void bsp_uart1_dmarx_config(uint8_t *mem_addr, uint32_t mem_size)
    ( ?# A' d, u  m+ S$ ^5 f
  174. {
    0 O! h1 z" ]0 r& @0 x4 T
  175.           DMA_InitTypeDef DMA_InitStructure;) F& m- J, r2 J
  176.         7 G' `2 h$ @2 m
  177.         DMA_DeInit(DMA1_Channel3);
    4 F8 \! H  R- j% m7 j
  178.         DMA_Cmd(DMA1_Channel3, DISABLE);. n9 o1 Y* v5 W
  179.         DMA_InitStructure.DMA_PeripheralBaseAddr         = (uint32_t)&(USART1->RDR);
    * M; r3 O7 ?& ~2 A" _1 f. c
  180.         DMA_InitStructure.DMA_MemoryBaseAddr                 = (uint32_t)mem_addr;
    2 A7 d+ L0 x' p6 J/ {& P
  181.         DMA_InitStructure.DMA_DIR                                         = DMA_DIR_PeripheralSRC;         /* 传输方向:外设->内存 */% S4 z: h. u/ R( q  o
  182.         DMA_InitStructure.DMA_BufferSize                         = mem_size;   E# ?' M3 G' e3 i2 ]4 U
  183.         DMA_InitStructure.DMA_PeripheralInc                 = DMA_PeripheralInc_Disable; - f6 O7 }/ q4 J
  184.         DMA_InitStructure.DMA_MemoryInc                         = DMA_MemoryInc_Enable; ; J& q, h* r/ w/ |# a
  185.         DMA_InitStructure.DMA_PeripheralDataSize         = DMA_PeripheralDataSize_Byte;
    7 s1 S# K' d- v
  186.         DMA_InitStructure.DMA_MemoryDataSize                 = DMA_MemoryDataSize_Byte;/ l  S9 q4 P0 R+ L" h
  187.         DMA_InitStructure.DMA_Mode                                         = DMA_Mode_Circular; ; t0 z( F, N! r) Q
  188.         DMA_InitStructure.DMA_Priority                                 = DMA_Priority_VeryHigh;
    1 ?, T" a# Q4 p2 P3 |8 Z
  189.         DMA_InitStructure.DMA_M2M                                         = DMA_M2M_Disable;
    : g8 w: X, m+ Q+ F" C
  190.         DMA_Init(DMA1_Channel3, &DMA_InitStructure);
    & \# O: x$ E; N9 D7 M+ \* ~
  191.         DMA_ITConfig(DMA1_Channel3, DMA_IT_TC|DMA_IT_HT|DMA_IT_TE, ENABLE);/* 使能DMA半满、全满、错误中断 */1 ^! X# H) e! D' E5 ]: x+ [
  192.         DMA_ClearFlag(DMA1_IT_TC3);
    9 d- C7 G9 H5 k
  193.         DMA_ClearFlag(DMA1_IT_HT3);
    7 h# c3 M' |3 L! e$ |! A
  194.         DMA_Cmd(DMA1_Channel3, ENABLE);
    / j+ Q# d4 d& G
  195. }2 L- O% N7 y" C$ Z! G5 _0 |; J

  196. ' D! ~' O8 c0 F7 W1 b7 {
  197. uint16_t bsp_uart1_get_dmarx_buf_remain_size(void)
    . f8 j# J- d4 A: m) @) i. g6 D9 a
  198. {
    5 t- A% H/ A  X! P* Y' P
  199.         return DMA_GetCurrDataCounter(DMA1_Channel3);        /* 获取DMA接收buf剩余空间 */
    . z" q0 t" V8 d! r7 U+ P+ m1 _: N
  200. }0 p$ l2 d- s( x/ Q6 [& U

  201. # q, F# B" ]/ H0 h8 J: X
  202. void bsp_uart2_dmatx_config(uint8_t *mem_addr, uint32_t mem_size)# c" m& w6 W5 }$ F; I5 d3 M6 U
  203. {
    : H) t1 d" n" e9 `6 f7 X) H& X. ^  o
  204.           DMA_InitTypeDef DMA_InitStructure;
    , C$ q- I7 q  O; x7 m4 s! n
  205.         2 \$ [0 o( h3 B
  206.         DMA_DeInit(DMA1_Channel4);1 q- _* G8 P! Q. g1 Z7 `$ K- r
  207.         DMA_Cmd(DMA1_Channel4, DISABLE);
    7 `4 H  ~' R6 R% b  b. B" s- r
  208.         DMA_InitStructure.DMA_PeripheralBaseAddr         = (uint32_t)&(USART2->TDR);
    + a2 \( J. _. I2 v0 }
  209.         DMA_InitStructure.DMA_MemoryBaseAddr                 = (uint32_t)mem_addr; ; s+ @1 T+ y0 Z$ T
  210.         DMA_InitStructure.DMA_DIR                                         = DMA_DIR_PeripheralDST;         /* 传输方向:内存->外设 */
    $ t" a- ?, R0 B0 `- z" a: r0 d2 D
  211.         DMA_InitStructure.DMA_BufferSize                         = mem_size; 4 X) j8 r; O$ B! O4 b! ~
  212.         DMA_InitStructure.DMA_PeripheralInc                 = DMA_PeripheralInc_Disable; $ j9 X( e" d* B
  213.         DMA_InitStructure.DMA_MemoryInc                         = DMA_MemoryInc_Enable; 0 k/ ?. B: `& t. Q, Q+ ]7 }" I" B
  214.         DMA_InitStructure.DMA_PeripheralDataSize         = DMA_PeripheralDataSize_Byte;
    7 z, Q$ {" F3 L3 m6 ~8 D! u
  215.         DMA_InitStructure.DMA_MemoryDataSize                 = DMA_MemoryDataSize_Byte;
    . d! A) P6 m8 s- D  q. N7 X' \: H3 l
  216.         DMA_InitStructure.DMA_Mode                                         = DMA_Mode_Normal; 7 k8 r" B0 O5 r
  217.         DMA_InitStructure.DMA_Priority                                 = DMA_Priority_High; $ @7 E2 X" _& x5 W$ p5 ~) ?8 ~
  218.         DMA_InitStructure.DMA_M2M                                         = DMA_M2M_Disable;
    2 _0 J+ b0 l' f* r- T' B0 l
  219.         DMA_Init(DMA1_Channel4, &DMA_InitStructure);  6 s" p/ q2 A5 q2 f0 b6 b
  220.         DMA_ITConfig(DMA1_Channel4, DMA_IT_TC|DMA_IT_TE, ENABLE); 5 R/ T9 h6 E( q/ V7 l
  221.         DMA_ClearFlag(DMA1_IT_TC4);        /* 清除发送完成标识 *// V* j. y6 [' q% P4 r% ~; _
  222.         DMA_Cmd(DMA1_Channel4, ENABLE); ( `  n) ]5 l- t, k& a! C& P9 E
  223. }
    5 u4 o9 V0 @, e+ o8 b

  224. ' w5 {. z5 E/ L1 N% P2 n
  225. void bsp_uart2_dmarx_config(uint8_t *mem_addr, uint32_t mem_size)
    7 i0 H6 U& G  V9 f* ?, ]' l
  226. {
    ' J! C; B* b# M4 e
  227.           DMA_InitTypeDef DMA_InitStructure;
    : i. R. A& ?5 r) Y' d
  228.         1 S2 b* c5 |' s% z+ I' }
  229.         DMA_DeInit(DMA1_Channel5); # i& ?1 W1 @+ e- ?
  230.         DMA_Cmd(DMA1_Channel5, DISABLE);) W, t. I4 J9 H, l- Q6 ?
  231.         DMA_InitStructure.DMA_PeripheralBaseAddr         = (uint32_t)&(USART2->RDR);3 H+ e  G8 [/ S, o* t/ N
  232.         DMA_InitStructure.DMA_MemoryBaseAddr                 = (uint32_t)mem_addr; ) v' p" s, N6 R
  233.         DMA_InitStructure.DMA_DIR                                         = DMA_DIR_PeripheralSRC;         /* 传输方向:外设->内存 */# V# y3 A/ W# `+ g
  234.         DMA_InitStructure.DMA_BufferSize                         = mem_size;
      z3 ]% t8 P/ }- P* t% j
  235.         DMA_InitStructure.DMA_PeripheralInc                 = DMA_PeripheralInc_Disable;
    3 ?* y. t$ K& J: B/ a( ?7 u! \
  236.         DMA_InitStructure.DMA_MemoryInc                         = DMA_MemoryInc_Enable; ( D6 P7 [, S- y6 ]  S8 h. ^2 E
  237.         DMA_InitStructure.DMA_PeripheralDataSize         = DMA_PeripheralDataSize_Byte;
    - |3 ?( i) k  p
  238.         DMA_InitStructure.DMA_MemoryDataSize                 = DMA_MemoryDataSize_Byte;
    : g. x# |8 q4 h; p1 P, `( C/ J
  239.         DMA_InitStructure.DMA_Mode                                         = DMA_Mode_Circular; ' G2 s- W  \4 }( `1 N8 d8 q
  240.         DMA_InitStructure.DMA_Priority                                 = DMA_Priority_VeryHigh; 8 ^# Y8 i/ V7 v6 n0 {, b7 w
  241.         DMA_InitStructure.DMA_M2M                                         = DMA_M2M_Disable;
    1 V6 c+ f5 B6 n2 K6 A* {
  242.         DMA_Init(DMA1_Channel5, &DMA_InitStructure); & Y* u( y# t$ q7 Y% Q) v; K. }
  243.         DMA_ITConfig(DMA1_Channel5, DMA_IT_TC|DMA_IT_HT|DMA_IT_TE, ENABLE);/* 使能DMA半满、全满、错误中断 */
    " r$ g+ l- h7 l. _9 M' |' f
  244.         DMA_ClearFlag(DMA1_IT_TC5);
    / A. {1 j8 N! f1 X
  245.         DMA_ClearFlag(DMA1_IT_HT5);4 V! d" @, d# `
  246.         DMA_Cmd(DMA1_Channel5, ENABLE); 4 u$ o/ m  w( c3 t7 F8 [( o8 v2 @
  247. }
    & \. j; D/ _# j+ V: |1 ?$ x0 k

  248. 6 f' Y3 n/ T0 }' ^; J$ t) W+ |7 k( r
  249. uint16_t bsp_uart2_get_dmarx_buf_remain_size(void)6 M; w; N. C4 _0 n  S
  250. {
    & K- ?3 N- ]' U3 `2 }& ^' {
  251.         return DMA_GetCurrDataCounter(DMA1_Channel5);        /* 获取DMA接收buf剩余空间 */
    ( j6 z1 Y& D- r
  252. }
复制代码
+ {5 l& H% G1 B+ K  l, f! v6 R. i


( X5 T, Q$ u. }

压力测试:

  • 1.5Mbps波特率,串口助手每毫秒发送1k字节数据,stm32f0 DMA接收数据,再通过DMA发送回串口助手,毫无压力。
  • 1.5Mbps波特率,可传输大文件测试,将接受数据保存为文件,与源文件比较。
  • 串口高波特率测试需要USB转TLL工具及串口助手都支持才可行,推荐CP2102、FT232芯片的USB转TTL工具。/ \! k2 G. L: ~9 Q! d5 d

    1 N3 x$ w% D$ ~! p, @7 o* |! z: h

8 N( b# i/ w) m) }2 j: X8 W# f

7.png


( q" N5 i# d. b! k7 {0 ], k
1.5Mbps串口回环压力测试

- C5 C$ Z% y( x) R' y

9 P; f' q6 ^2 A( _& `/ v. b5 H
3 u$ T, U: H" N4 x4 U* [+ }9 H( Z
收藏 3 评论0 发布时间:2020-9-15 15:49

举报

0个回答

所属标签

相似分享

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