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

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

[复制链接]
STMCU-管管 发布时间:2020-9-15 15:49
1 前言
, H! L! d3 z9 ^# [" e; k4 M& _# r! @7 R/ ?  A& o" Y' @5 J- X1 A

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

  • 内存—>内存,内存间拷贝
  • 外设—>内存,如uart、spi、i2c等总线接收数据过程
  • 内存—>外设,如uart、spi、i2c等总线发送数据过程
    % e$ S& k2 B# k7 F, V

    4 D- u( Y7 b+ U$ C7 s. a9 }4 {

/ Y: b0 [2 ^7 y) p2 串口有必要使用DMA吗
+ Z0 q' l3 h8 y) R* u* Y
( X1 k! M3 \$ q: p9 B8 O  r
  @- z- u+ |2 [- `9 o1 I% m+ Y

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


) t0 M# O' e. ?+ D; j

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

  • 对于发送,使用循环发送,可能阻塞线程,需要消耗大量CPU资源“搬运”数据,浪费CPU
  • 对于发送,使用中断发送,不会阻塞线程,但需浪费大量中断资源,CPU频繁响应中断;以115200bps波特率,1s传输11520字节,大约69us需响应一次中断,如波特率再提高,将消耗更多CPU资源
  • 对于接收,如仍采用传统的中断模式接收,同样会因为频繁中断导致消耗大量CPU资源2 d4 c  u+ d' q) c* ~! p
    / F& b1 B: f: i* U* E; ?$ M8 S

* X1 G* j+ H! M$ V9 V% A/ ^+ B

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


  ]0 B7 k* }# @6 Y0 M3 实现方式

, p1 _$ L/ ?" P
7 o9 i6 j1 M% ?3 _0 x" X& i

1 (2).png

2 \% t' _, {) P
5 A/ ]% u1 r0 m( x  f- N+ n
4 STM32串口使用DMA

8 f  [0 S+ ~" _1 y5 Z5 a  ?
2 p* D8 u  w, J/ ]1 N; s! H! L! F+ t) p) P. J) R

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

; z# r. b- q) }1 r. n% k

测试平台:

  • STM32F030C8T6
  • UART1/UART2
  • DMA1 Channel2—Channel5
  • ST标准库
  • 主频48MHz(外部12MHz晶振)
    , o: [  P6 w( t+ l1 N
$ D, ]' r8 a+ j4 h9 Q
0 p6 }- K( m, R& Q( c2 b+ w7 {
2.png

% W9 Z, S; V4 P* V0 @; ^) d# m' u$ n


" s* [9 h' c4 x# E

5 串口DMA接收" V6 \: q2 _8 W4 H3 _
) H+ `7 O/ V5 s6 J. k
5.1 基本流程. s" U# p( o+ A4 |( U1 l

3.png

+ D  S0 I4 L7 y! ^4 U1 |/ I9 C) X
串口接收流程图

" @, \- {; B: B6 o* W, b1 T
5 p% s4 G3 L% H& ^* i) i9 b5.2 相关配置

0 \. v0 Q5 L' _+ @3 r0 |0 N
* v+ ]9 j6 q' Y0 ~

关键步骤

【1】初始化串口

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

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


9 z* i4 W( g, e3 i% R

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


+ _0 X7 Z* z* Q. ?

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

) H- c, Q' i, u, M

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

6 S0 N) a6 U3 {6 X% ]+ {& h

【1】第一步,DMA先将数据搬运到buf1,搬运完成通知CPU来拷贝buf1数据
+ Q" e+ d* ]/ ^& _( l1 Q8 @7 z【2】第二步,DMA将数据搬运到buf2,与CPU拷贝buf1数据不会冲突
+ U0 U) M+ U0 \4 B2 [【3】第三步,buf2数据搬运完成,通知CPU来拷贝buf2数据
  m1 e: z* |" Q% d【4】执行完第三步,DMA返回执行第一步,一直循环

! u) P/ `9 ~$ w/ h9 }- K: W( \

- V8 E6 i; s% a3 m+ C8 J

4.png


; ]5 [& d5 D  W* V6 v! U2 }  G. w/ A+ d8 l$ s1 ^( a
双缓存DMA数据搬运过程* Q% V5 u% e$ ^, V

5 u8 `9 d; t8 ~" `

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

) }/ ]! W! P& p: z

【1】第一步,DMA将数据搬运完成buf的前一半时,产生“半满中断”,CPU来拷贝buf前半部分数据/ E# U; H  Z% e& g2 W
【2】第二步,DMA继续将数据搬运到buf的后半部分,与CPU拷贝buf前半部数据不会冲突
, @: C' M" z! p! B; P# J( n2 D0 @【3】第三步,buf后半部分数据搬运完成,触发“溢满中断”,CPU来拷贝buf后半部分数据
$ y" T$ R" r1 f/ r" y, T【4】执行完第三步,DMA返回执行第一步,一直循环

# l9 v8 w# w/ {3 k, t
, d' ?7 y1 S. b0 ?9 l. ^/ S, |
. P5 `6 h- E( E

5.png


% t0 x+ ?/ }2 e" m$ a* z+ x( e( P
  l- w$ R8 w; ]* H
使用半满中断DMA数据搬运过程

- `* D' U- b5 t, _: E5 \" s  O
; `, o- o; }6 F2 C7 F' H/ O

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

  • 串口接收,DMA通道工作模式设为连续模式
  • 使能DMA通道接收buf半满中断、溢满(传输完成)中断
  • 启动DMA通道前清空相关状态标识,防止首次传输错乱数据0 H% d$ g7 o: J9 L1 Q
& ^+ `1 W2 E5 C9 E4 v  V& ?
7 h. h4 K/ g' }5 w1 [$ B; y

  h0 b: E5 G  w6 u, v* t! j4 F

; e/ G, ?: g, U# @+ \8 E! r" u% r2 f
  1. void bsp_uart2_dmarx_config(uint8_t *mem_addr, uint32_t mem_size)8 p  |# P0 D; u3 g
  2. {
    4 `2 G( g  y" W5 }1 X
  3.           DMA_InitTypeDef DMA_InitStructure;
    / Q9 j) x* W  E4 C
  4.         
    . M4 r- s( B) H+ L5 _( U" N: @5 G
  5.         DMA_DeInit(DMA1_Channel5); . K- C( z' i% b# ^  \
  6.         DMA_Cmd(DMA1_Channel5, DISABLE);
    * o9 A! d" b: w3 H" {
  7.         DMA_InitStructure.DMA_PeripheralBaseAddr         = (uint32_t)&(USART2->RDR);/* UART2接收数据地址 */2 N( F/ l% f2 ?& V3 V0 U
  8.         DMA_InitStructure.DMA_MemoryBaseAddr                 = (uint32_t)mem_addr; /* 接收buf */
    - [7 D# U$ S' H9 {* Q
  9.         DMA_InitStructure.DMA_DIR                                         = DMA_DIR_PeripheralSRC;         /* 传输方向:外设->内存 */& |; l: ^' [# I' B2 e0 O* y) m
  10.         DMA_InitStructure.DMA_BufferSize                         = mem_size; /* 接收buf大小 */, ^! ]5 T% j6 b; K9 f9 M6 |% ~% U
  11.         DMA_InitStructure.DMA_PeripheralInc                 = DMA_PeripheralInc_Disable;
    ( K" G; g1 b: W" R3 J
  12.         DMA_InitStructure.DMA_MemoryInc                         = DMA_MemoryInc_Enable;
    . q; x$ V4 `$ `5 Z
  13.         DMA_InitStructure.DMA_PeripheralDataSize         = DMA_PeripheralDataSize_Byte; - a2 f9 u* S# K
  14.         DMA_InitStructure.DMA_MemoryDataSize                 = DMA_MemoryDataSize_Byte;
    - N# V. ~+ P" E& v$ Q
  15.         DMA_InitStructure.DMA_Mode                                         = DMA_Mode_Circular; /* 连续模式 */
    % a) D/ N3 K, y( @' z" q
  16.         DMA_InitStructure.DMA_Priority                                 = DMA_Priority_VeryHigh;
    & C5 i6 c* E9 K4 ~
  17.         DMA_InitStructure.DMA_M2M                                         = DMA_M2M_Disable;   ]! g! f, ?' q2 H% {
  18.         DMA_Init(DMA1_Channel5, &DMA_InitStructure);
    $ ~) N! z6 I3 n! K5 Y0 N1 A
  19.         DMA_ITConfig(DMA1_Channel5, DMA_IT_TC|DMA_IT_HT|DMA_IT_TE, ENABLE);/* 使能DMA半满、溢满、错误中断 */, Z# L  E4 Y% \$ H
  20.         DMA_ClearFlag(DMA1_IT_TC5);        /* 清除相关状态标识 */, ]/ T* {# e& d, ?
  21.         DMA_ClearFlag(DMA1_IT_HT5);
    ( b7 [% W9 A$ ?# [
  22.         DMA_Cmd(DMA1_Channel5, ENABLE);
    * L9 z7 C% Q' {; M  l7 g
  23. }
复制代码
! n: B$ y6 s# e7 e$ s0 B
4 E. D' ?1 {7 J5 i  B

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

9 p3 N2 U' {: c" P4 T7 ]
5.3 接收处理

4 y$ y) M/ W8 a1 h4 b4 `

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

  • DMA通道buf溢满(传输完成)场景
  • DMA通道buf半满场景
  • 串口空闲中断场景
    / A8 y' M: x1 `0 M6 K! o

    - p8 R. M; Z; p# t

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


* S# \2 j! m+ i1 I2 G$ V5 p! m 5.3 .1 接收数据大小

8 C% D$ j. h7 v$ ]" ?6 ]# k

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

  • 数据刚好是DMA接收buf的整数倍,这是理想的状态
  • 数据量小于DMA接收buf或者小于接收buf的一半,此时会触发串口空闲中断( p- J3 L4 {- a" O; ^* A% r/ r
    ) W8 S& y3 T! _3 Q/ `& _

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

  1. /* 获取DMA通道接收buf剩余空间大小 */8 d; @0 M  A8 X" G5 _: @& {3 ^8 a
  2. uint16_t DMA_GetCurrDataCounter(DMA_Channel_TypeDef* DMAy_Channelx);
复制代码

4 m* u* O' n4 [$ I1 I

DMA通道buf溢满场景计算

  |) y# l" l2 S$ ^7 s

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

6 [' ~% @! N. r! v( B/ B- W# g8 i) j( X+ w6 r( j) W5 C

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

  1. void uart_dmarx_done_isr(uint8_t uart_id)0 s# k! \0 i# K
  2. {7 j7 [& z  N% A! P4 N
  3.           uint16_t recv_size;( `! g9 j/ e0 P, O' Y2 Q
  4.        
    . Y9 `! [: Z- U2 ]! G% Y; }
  5.         recv_size = s_uart_dev[uart_id].dmarx_buf_size - s_uart_dev[uart_id].last_dmarx_size;, o3 u& `) r* V, c* }7 z
  6. $ k# v- h; x( o/ q6 O" T" z
  7.         fifo_write(&s_uart_dev[uart_id].rx_fifo,
    3 Y3 i% u! L. j$ Z
  8.                                    (const uint8_t *)&(s_uart_dev[uart_id].dmarx_buf[s_uart_dev[uart_id].last_dmarx_size]), recv_size);3 o( F/ D1 D; J3 t- G
  9. ! `# u' T8 j) P& [3 S5 y
  10.         s_uart_dev[uart_id].last_dmarx_size = 0;
    % B4 \/ H( i0 u; r
  11. }
复制代码

1 V9 V- u% T- i& K; `2 H! q! d% }, E

DMA通道buf半满场景计算


! \: g: t2 P1 W" d, E; W) L

  1. 接收数据大小 = DMA通道接收总数据大小 - 上一次接收的总数据大小3 E, s1 r7 A; g
  2. DMA通道接收总数据大小 = DMA通道buf大小 - DMA通道buf剩余空间大小
复制代码

. Q) l0 s9 G6 z8 R' S9 M

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

# _+ Y8 s8 G$ x9 z$ s9 c, g/ A  m

  1. void uart_dmarx_half_done_isr(uint8_t uart_id)# S: `$ t. C0 q: `" W
  2. {6 q& G  `& W2 R
  3.           uint16_t recv_total_size;; p) X8 m6 }9 |) B5 Q& G, E
  4.           uint16_t recv_size;
    - a# \  _5 I4 Q5 {
  5.         * [2 W" o  m6 @  z; k& }8 S
  6.         if(uart_id == 0)
    6 K# S" Q- y/ C$ n# b: }
  7.         {0 ]! ^4 L/ F; G" s- N
  8.                   recv_total_size = s_uart_dev[uart_id].dmarx_buf_size - bsp_uart1_get_dmarx_buf_remain_size();
    - [% B1 r7 ]  h9 L
  9.         }8 Y, D1 B) ^3 g" D3 d; U' R6 E
  10.         else if (uart_id == 1)
    9 E" D  W' [: A# U
  11.         {
    & X# z5 b( w! x; c
  12.                 recv_total_size = s_uart_dev[uart_id].dmarx_buf_size - bsp_uart2_get_dmarx_buf_remain_size();
    . R" O2 }! o# q$ g8 N5 Y
  13.         }
    + v5 p! D8 f0 k" e1 O& t' R- q& N
  14.         recv_size = recv_total_size - s_uart_dev[uart_id].last_dmarx_size;$ i8 f, d) ~" D7 F
  15.         % j; W8 l: u6 }
  16.         fifo_write(&s_uart_dev[uart_id].rx_fifo,
    & k3 t4 h+ b; x3 H0 y
  17.                                    (const uint8_t *)&(s_uart_dev[uart_id].dmarx_buf[s_uart_dev[uart_id].last_dmarx_size]), recv_size);9 G% z: T5 V7 P1 S0 _
  18.         s_uart_dev[uart_id].last_dmarx_size = recv_total_size;/* 记录接收总数据大小 */
    4 N" s, }) G* o5 k5 U
  19. }
复制代码

/ K% e8 l- x4 U" i6 f2 i9 _8 A; f$ Q& ]% y* Q

串口空闲中断场景计算

$ v# K; ^2 F  f+ T; O

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

串口空闲中断处理函数:

注:9 M! S2 |. a* e7 _/ g
串口空闲中断处理函数,除了将数据拷贝到串口接收fifo中,还可以增加特殊处理,如作为串口数据传输完成标识、不定长度数据处理等等。


( {2 A& l" |+ A7 G 5.3.2 接收数据偏移地址

- I: J" |8 r+ i$ R- g

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

  i$ M8 K* H  [0 y+ I

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


7 e3 F  e% J2 v4 @

  1. void uart_dmarx_done_isr(uint8_t uart_id)
    ) h6 G5 T; }8 p% @9 J
  2. {
    + R* M( A4 j4 J" X* {) S/ D% {
  3.         /* todo */
      |7 ~+ e& n6 c9 Q7 F
  4.         s_uart_dev[uart_id].last_dmarx_size = 0;
    $ Z6 l! g4 s+ G' F  \1 y$ H
  5. }
复制代码

0 B/ W& `  W  ]; O9 j/ g! |! T* S
. E4 C5 D& q+ a/ Z. s5.4 应用读取串口数据方法

0 B5 N& Q2 ^) I' L  U$ D8 @

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

# j5 L3 {8 x$ k8 p0 B( F
6 串口DMA发送

, P: T7 [. Y" s$ [" {; y; }' W9 ~9 Q1 @  Q$ y5 u
5.1 基本流程
# d, q, D8 l& d) l- d4 h6 Y7 S

6.png


2 f; C5 }/ v* P& [8 I1 e# K: ]5 ?- w0 U$ o* C
串口发送流程图
- \- u4 Z! M% M) `9 t3 F

6 S8 R0 a* z3 z# y- T. o$ ]0 Q9 {  p9 U# ?9 X- n2 o8 n# l. a
5.2 相关配置
1 S1 h) z5 M5 |- B. S3 D9 V

关键步骤


6 y# S- e# A# x, `9 A$ T

【1】初始化串口

# }1 ~( \! e$ l9 q

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

* k+ c! v1 v6 f* n, q

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

6 F; }( }" S3 P9 K+ Y- `0 C

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

, P2 T/ D# g' m2 R: i. Q

  • 串口发送是,DMA通道工作模式设为单次模式(正常模式),每次需要发送数据时重新配置DMA
  • 使能DMA通道传输完成中断,利用该中断信息处理一些必要的任务,如清空发送状态、启动下一次传输
  • 启动DMA通道前清空相关状态标识,防止首次传输错乱数据) Q% m5 N+ a' A$ \4 m- b) i
    & ^; x8 _  `, W$ I7 j% P
  • 2 x3 f$ f2 h3 r" P

    " S5 J$ d( ?; [' p, K% d. N

* @, v7 S( `- j) G* D5.3 发送处理

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

串口发送处理函数:


' D+ ]+ B) B7 d4 r/ b5 N

  1. void uart_poll_dma_tx(uint8_t uart_id)1 s4 t$ }4 c1 t, P+ A
  2. {
    , O" C6 X% `. n5 l
  3.           uint16_t size = 0;8 {4 c# J1 ?9 l
  4.        
    ( j: A- K0 ^/ x
  5.         if (0x01 == s_uart_dev[uart_id].status)! z( _0 \5 C  Y1 q( ^. p; ~
  6.     {
    * V2 w# H9 c  W5 ^+ s; s' n5 f
  7.         return;
    9 l+ |. J# s+ F" \8 o0 ?/ R3 T
  8.     }! r, ]) a/ L8 H; D$ [  b3 X9 s
  9.         size = fifo_read(&s_uart_dev[uart_id].tx_fifo, s_uart_dev[uart_id].dmatx_buf,
    6 M; [+ @( X6 n' `+ L: |
  10.                                          s_uart_dev[uart_id].dmatx_buf_size);1 P1 Q1 v8 q8 H. w
  11.         if (size != 0)! K/ k! Y! T- X2 w  `
  12.         {( p1 R+ o& k6 O  Q& b2 v* K/ \
  13.         s_UartTxRxCount[uart_id*2+0] += size;
    8 F- H& n2 V( G) h7 U8 H
  14.                   if (uart_id == 0)" n# S1 ^+ p3 H* G- ~
  15.                 {
    9 v, a9 I9 M1 G& D" K: S- ^
  16.             s_uart_dev[uart_id].status = 0x01;        /* DMA发送状态 */$ S5 o: {' |2 g# O
  17.                           bsp_uart1_dmatx_config(s_uart_dev[uart_id].dmatx_buf, size);2 g6 b# ~. P- q" a1 `& n3 I
  18.                 }2 ^! j8 `: L6 \4 E" M. g
  19.                 else if (uart_id == 1)
    . o0 r6 N, D9 R. N( p: I0 e* \
  20.                 {  ]- V+ ?& J6 U8 l
  21.             s_uart_dev[uart_id].status = 0x01;        /* DMA发送状态,必须在使能DMA传输前置位,否则有可能DMA已经传输并进入中断 */3 v& h2 i- n1 l$ E
  22.                         bsp_uart2_dmatx_config(s_uart_dev[uart_id].dmatx_buf, size);0 q8 c) k& `8 u% p0 R3 D' i
  23.                 }
    / b" l6 {1 f3 H2 O. C+ c+ B
  24.         }: \1 [& ?& v8 c
  25. }
复制代码

# P+ C# t9 v9 o- _! ]# o0 f, ]

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


; ?' w: r2 J, G/ i  y- l0 i: z

# |& U. \/ `; v* V3 [, z! l

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

0 x3 N! T& D! i

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

  1. void uart_dmatx_done_isr(uint8_t uart_id)
    ! C, p- Q  ?8 o! Z+ i
  2. {! a( j2 }- @$ J, O" O
  3.         s_uart_dev[uart_id].status = 0;        /* 清空DMA发送状态标识 */
    / O" J; ^8 [' R7 C$ J2 D
  4. }
复制代码
0 E7 c5 C$ {  k! g, W0 P! z0 h; W0 [/ }2 @

: v" A. D0 W" E* G

  }" |$ `7 U  f) Y

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


9 a$ s% h) H9 ?3 m# [


2 t8 O# X; I+ Q. ]" y$ m0 Q  i

  • 主线程任务调用,前提是线程不能被其他任务阻塞,否则导致fifo溢出" l% |0 R: n# z+ u& J
      Q% v2 V- I1 G& g- E
  1. void thread(void)
    / h. `0 G/ r5 Q# c
  2. {$ Y& Z9 p% O1 t+ o  \- d. M# q( z3 e
  3.     uart_dmatx_done_isr(DEV_UART1);, P- i% n0 @! Z- N% Q+ Z0 U4 W. Z
  4.     uart_dmatx_done_isr(DEV_UART2);$ O8 u0 r$ P- t; u
  5. }
复制代码

( t% i3 X" w+ U. t/ n2 _, x* @2 V
6 ~5 [$ }9 [- t# e1 O2 J
  • 定时器中断中调用' x8 m. _6 W+ t' n9 _* Q2 g6 p
  1. void TIMx_IRQHandler(void)& r4 s" g5 e6 g- ~7 w% x
  2. {
    : h, h% G5 W& B; S4 ]
  3.     uart_dmatx_done_isr(DEV_UART1);
    2 ]* E9 R, }0 W. h
  4.     uart_dmatx_done_isr(DEV_UART2);- O6 Y8 Y" J. B) @- C) m
  5. }
复制代码

. r& n$ L" V0 \0 N" G4 I* W9 K2 z$ [/ H3 A6 X
  • DMA通道传输完成中断中调用, c* k: y* T3 D3 }# W

      O8 {$ k  i! p
  1. void DMA1_Channel4_5_IRQHandler(void)5 E4 j( t' ^, Y6 k, T8 B6 x0 i
  2. {
    7 S; w1 O  _; N1 s' T2 y: g  W
  3.         if(DMA_GetITStatus(DMA1_IT_TC4))
    - E8 U  O& K0 E: a) c! D, j
  4.         {
    3 L: n' N5 R1 [& C( X- q
  5.                 UartDmaSendDoneIsr(UART_2);: z( W' A& d( q6 U
  6.                 DMA_ClearFlag(DMA1_FLAG_TC4);1 V) R1 H  p) ?
  7.                 uart_dmatx_done_isr(DEV_UART2);
    * G9 \3 I0 R- {6 }3 \3 c
  8.         }/ l. |  V) k7 ~4 B( D
  9. }
复制代码

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

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

  • 周期查询发送fifo数据,启动DMA传输,充分利用DMA发送效率,但可能降低串口数据流实时性
  • 实时查询发送fifo数据,加上超时处理,理想的方法
  • 在DMA传输完成中断中处理,保证实时连续数据流
    5 O- G1 b" Z% f# F/ w8 b

    # w9 F+ j6 B( H( ]

5 Z$ y+ I! ~/ {3 a5 b4 }6 串口设备
$ P  N5 `. B9 P# k8 Z, A

5 v! H. J, [8 p7 V1 q6.1 数据结构. B6 ]& R3 t" d$ \. {! r
  1. /* 串口设备数据结构 */
    0 P+ Y! {# j5 x; a5 q+ f
  2. typedef struct' z1 W2 X* z. r
  3. {/ z1 @3 O" `' G# ?
  4.         uint8_t status;                        /* 发送状态 */
    & G# ]* V5 b# P6 k1 j
  5.         _fifo_t tx_fifo;                /* 发送fifo */* q1 _5 |0 W; ~" \
  6.         _fifo_t rx_fifo;                /* 接收fifo */
    ' \- B& i3 g: R. e
  7.         uint8_t *dmarx_buf;                /* dma接收缓存 */8 Y7 |& S" L5 W. q0 K
  8.         uint16_t dmarx_buf_size;/* dma接收缓存大小*/! |) U& ~. L' L8 N  L$ ?& e
  9.         uint8_t *dmatx_buf;                /* dma发送缓存 */
    + @7 i# r- S' H) F* f. p
  10.         uint16_t dmatx_buf_size;/* dma发送缓存大小 */8 U/ U$ a3 ]9 n3 b' A7 a
  11.         uint16_t last_dmarx_size;/* dma上一次接收数据大小 */
    $ A) p0 w" u) P, O+ y/ N% A
  12. }uart_device_t;
复制代码
, T" i! `' T' e1 j+ [* }4 _2 a
4 _" j5 |% g( _5 g0 R
6.2 对外接口

1 W) _0 W$ b- b& i  j* w, c! n2 c0 \. [) E3 V& l5 V
  1. /* 串口注册初始化函数 */
    " s2 h3 z, ], @, l8 v
  2. void uart_device_init(uint8_t uart_id)) N. ~. Q" V% p0 }
  3. {' Q; W2 m% T  g0 K  B5 w
  4.           if (uart_id == 1)
      _- Y# ?$ P4 E/ w. F
  5.         {
    1 u+ j1 v; _: {3 I$ ~: R( O3 q+ I
  6.                 /* 配置串口2收发fifo */+ {: j# W/ e$ K" T- d+ V4 l
  7.                 fifo_register(&s_uart_dev[uart_id].tx_fifo, &s_uart2_tx_buf[0],
    " ~) h7 ~: j) R, ]  M1 g
  8.                       sizeof(s_uart2_tx_buf), fifo_lock, fifo_unlock);9 p+ c$ m/ Z3 u. W" }4 _1 f
  9.                 fifo_register(&s_uart_dev[uart_id].rx_fifo, &s_uart2_rx_buf[0], $ J9 p. r7 D/ b
  10.                       sizeof(s_uart2_rx_buf), fifo_lock, fifo_unlock);
    7 Z2 w6 Y9 G) P
  11.                 8 {2 m# u7 v" ~+ T* S! @: x
  12.                 /* 配置串口2 DMA收发buf */# p% |/ U5 x' |' p
  13.                 s_uart_dev[uart_id].dmarx_buf = &s_uart2_dmarx_buf[0];
    $ g/ T7 l3 _* O. D
  14.                 s_uart_dev[uart_id].dmarx_buf_size = sizeof(s_uart2_dmarx_buf);8 A8 b2 m9 {! d( m! K6 \
  15.                 s_uart_dev[uart_id].dmatx_buf = &s_uart2_dmatx_buf[0];
    / S& P4 r( `  g- K% [0 j
  16.                 s_uart_dev[uart_id].dmatx_buf_size = sizeof(s_uart2_dmatx_buf);
    : [+ e  |3 b6 Q
  17.                 bsp_uart2_dmarx_config(s_uart_dev[uart_id].dmarx_buf,
    8 ^5 \/ \/ R) U4 M8 N2 M
  18.                                                            sizeof(s_uart2_dmarx_buf));
    " c/ a* n8 c2 v  _0 ?1 I/ K
  19.                 s_uart_dev[uart_id].status  = 0;
    7 G; W. m1 s# V4 e$ T( O
  20.         }8 Q- ]4 r" D6 f7 L& s7 F; {; q
  21. }
    * v0 ^# u% v8 s  {7 Q- `: Q! T

  22. 3 ]1 E2 _- D7 A' [( Y1 x
  23. /* 串口发送函数 */: W7 d# w' k- _1 T
  24. uint16_t uart_write(uint8_t uart_id, const uint8_t *buf, uint16_t size)' g0 j9 k  B3 o+ r1 e2 e
  25. {, V2 {$ A& v0 ]( B' V8 U9 ^
  26.         return fifo_write(&s_uart_dev[uart_id].tx_fifo, buf, size);
    , Y+ |8 k' w# v( r6 C7 ~: ]) B
  27. }* a" S! D) P7 e: X9 e% V
  28. 4 G; f9 z  B# A* \7 i
  29. /* 串口读取函数 */4 [# H/ H& @1 G( S2 f
  30. uint16_t uart_read(uint8_t uart_id, uint8_t *buf, uint16_t size): @, q9 k# @8 [- u
  31. {) b5 i, s0 @0 c9 _) Y# \; }( |# L. R; {
  32.         return fifo_read(&s_uart_dev[uart_id].rx_fifo, buf, size);
    & s7 V3 I! k! m' }% I
  33. }
复制代码

5 m0 L- Q. e( ?1 K# H8 完整源码6 L2 K7 }. ]0 ^; D9 t

  V* |  |9 g- M: D( W+ Y& }9 ~$ U: w
" D/ s. Z4 p- e

串口&DMA底层配置:

) x( E  I- |; t( Z7 a3 o% [4 F6 l

  1. #include <stddef.h>1 L" T% O2 ]  @0 e! V  ]6 E
  2. #include <stdint.h>8 |6 c& F8 F1 W
  3. #include <stdbool.h>5 Y# c9 |& B' i. u( w
  4. #include "stm32f0xx.h"* w" `4 q, `' g3 \8 c6 U( T
  5. #include "bsp_uart.h"+ _6 K0 }/ s6 @  ~; @8 M
  6. ( s8 V' C4 O# A% ]( ]
  7. /**
    ( g" x7 w' ]" s* w" N% i
  8. * @brief  
    5 O3 {8 l. R8 ]
  9. * @param  
    9 Q( z  J, x6 V: s* _- I0 o
  10. * @retval
    5 p6 A4 o! @  Z2 \$ J/ Y
  11. */
    % o1 v# i  ^# o; A3 o8 S
  12. static void bsp_uart1_gpio_init(void)6 N7 r6 m- ?  ?5 B
  13. {9 B! M  B# r. Y( _/ t, r  q
  14.     GPIO_InitTypeDef    GPIO_InitStructure;
    # P+ G& E' x3 u# F
  15. #if 0
    # N$ R: K& Y+ o# I
  16.         RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOB, ENABLE);4 Q( g& A* _; _: R
  17.        
    7 l" P+ m; A& [% C' @, q  B5 w
  18.     GPIO_PinAFConfig(GPIOB, GPIO_PinSource6, GPIO_AF_0);+ v: _* [; j7 e- Y- ~4 m
  19.     GPIO_PinAFConfig(GPIOB, GPIO_PinSource7, GPIO_AF_0); , P; H+ u8 O9 _! X) q- r0 N# h
  20.        
    * x8 Y5 i# [! K$ K# V
  21.         GPIO_InitStructure.GPIO_Pin         = GPIO_Pin_6 | GPIO_Pin_7;
      U) B2 u% n7 a+ y+ i/ }
  22.     GPIO_InitStructure.GPIO_Mode         = GPIO_Mode_AF;
    # g: @% |7 X- n" u9 l% M& p
  23.         GPIO_InitStructure.GPIO_OType         = GPIO_OType_PP;8 j# U$ x( r' k6 @
  24.     GPIO_InitStructure.GPIO_Speed          = GPIO_Speed_Level_3;# ?# w0 e* @2 F' \7 I
  25.     GPIO_InitStructure.GPIO_PuPd         = GPIO_PuPd_UP;
    ! ?( r1 ^& Y3 R) q) {  `
  26.     GPIO_Init(GPIOB, &GPIO_InitStructure);
    # a/ V5 `, g0 b6 e; K
  27. #else3 t/ S$ F3 v: N; L3 {. f
  28.         RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOA, ENABLE);' O7 R3 N. q4 m8 E
  29.        
    + S+ N2 R8 d. V, u3 S
  30.     GPIO_PinAFConfig(GPIOB, GPIO_PinSource9, GPIO_AF_1);
    ( N* n4 G: N" |9 c; ^
  31.     GPIO_PinAFConfig(GPIOB, GPIO_PinSource10, GPIO_AF_1); ' z) E4 v) m/ e) B& Q
  32.         : a# }* C4 c! N* _6 d: M4 |
  33.         GPIO_InitStructure.GPIO_Pin         = GPIO_Pin_9 | GPIO_Pin_10;, |( l/ ~3 f, P6 g2 L
  34.     GPIO_InitStructure.GPIO_Mode         = GPIO_Mode_AF;
    $ d8 ^& r# p1 X& O
  35.         GPIO_InitStructure.GPIO_OType         = GPIO_OType_PP;2 r; Z4 B  j" I4 x; z9 I5 _! f, c  h
  36.     GPIO_InitStructure.GPIO_Speed          = GPIO_Speed_Level_3;. m. J7 z' ~  l/ f: |
  37.     GPIO_InitStructure.GPIO_PuPd         = GPIO_PuPd_UP;
    6 p8 d0 S! _+ K+ g; ~
  38.     GPIO_Init(GPIOA, &GPIO_InitStructure);
      c/ ^. W) s  {8 `$ [- g
  39. #endif
    % t$ l- D5 [% c
  40. }7 X& q9 ~& K' J' \

  41. / x" O9 H( N# r5 V
  42. /**/ f# U2 L: T5 f2 f
  43. * @brief  
    ' ~9 I4 ?+ p' n! W  a3 F. ?
  44. * @param  2 P$ Z# C4 ]- p* C) s8 n
  45. * @retval
    8 W. u! I" o! Z( u* \) g% H) g: t7 l
  46. */1 c/ z. p# S1 K  V3 J5 K, G
  47. static void bsp_uart2_gpio_init(void), Y. ^9 w) l+ Q7 C7 w' P1 v. j  W3 h: J9 e
  48. {
    3 C4 p( s- u( t# V/ ~, ]
  49.         GPIO_InitTypeDef GPIO_InitStructure;
    7 k  D! z# t# P* u3 t) n+ t8 A
  50.        
    8 F6 {7 h. G0 I6 z- U
  51.         RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOB, ENABLE);
    8 \7 a6 U. b- {! X4 n6 Q
  52.         9 [. L% t1 M2 |% I) \( q6 j* ^
  53.         GPIO_PinAFConfig(GPIOA, GPIO_PinSource2, GPIO_AF_1);6 n# b7 g9 Z  u$ _  M
  54.         GPIO_PinAFConfig(GPIOA, GPIO_PinSource3, GPIO_AF_1);5 R& c0 b0 x/ ^% y2 v, @1 n
  55.         % U- q) W3 L( `5 d
  56.         GPIO_InitStructure.GPIO_Pin   = GPIO_Pin_2 | GPIO_Pin_3;# Z+ s6 R0 V1 g1 W  Z3 S
  57.         GPIO_InitStructure.GPIO_Mode  = GPIO_Mode_AF;
    1 r$ E3 l; F7 J& X% [- Q
  58.         GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;5 `8 ~  R8 v7 L% F) Y
  59.         GPIO_InitStructure.GPIO_Speed = GPIO_Speed_10MHz;9 n6 {# M* n% ]! i* N
  60.         GPIO_InitStructure.GPIO_PuPd  = GPIO_PuPd_UP;/ O, T# l* |; A* y6 U, K
  61.         GPIO_Init(GPIOA, &GPIO_InitStructure);' N, s. @+ s' v8 U/ k5 \
  62. }
    5 W, [8 l- f, p4 W) [8 u) P9 f

  63. * J9 w! ?7 x$ B" k1 q
  64. /**
    , a- o) a0 L7 X0 t+ e, S& x' _
  65. * @brief  1 n: ~( F+ W7 {/ {* S0 k1 ]" p
  66. * @param  : s9 O, P! J/ X/ d
  67. * @retval
    . Y  n( Y. l6 |% F# j% G: g8 R( b
  68. */
    ( v% ?3 }1 n1 ^0 y  M" s3 v( M
  69. void bsp_uart1_init(void)
    8 x8 m" Q$ k* v- j
  70. {3 F, n& q2 L8 o! j9 j* o# ]/ @% a
  71.         USART_InitTypeDef USART_InitStructure;& B7 {# y/ H6 A6 `3 P' [8 }
  72.         NVIC_InitTypeDef NVIC_InitStructure;' x! K' N, H! S
  73.         : R; f' u+ Z3 k" [, h
  74.         bsp_uart1_gpio_init();
    & W, B3 h3 r" i5 D& v6 `
  75.         ( N. W% ~* V- L; b6 `! M7 ?
  76.         /* 使能串口和DMA时钟 */
    ; ?7 g; e! L+ }  d1 ]! x- Y5 C
  77.         RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);
    % Y" l* H! T6 [2 n8 e
  78.         RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);5 y0 c; ?8 |% B
  79.        
    ) ^& z1 F& ^4 m/ J4 c$ l4 D
  80.         USART_InitStructure.USART_BaudRate            = 57600;2 ]# |. v1 H4 q: z" z$ t3 _% p
  81.         USART_InitStructure.USART_WordLength          = USART_WordLength_8b;# V9 o0 }7 Z% O8 h) K( U2 A: \
  82.         USART_InitStructure.USART_StopBits            = USART_StopBits_1;5 D7 J) j5 p9 x; [6 Q2 H
  83.         USART_InitStructure.USART_Parity              = USART_Parity_No;# G& f$ j& h- w- z
  84.         USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
    $ S& a3 J' C8 f, _
  85.         USART_InitStructure.USART_Mode                = USART_Mode_Rx | USART_Mode_Tx;
    1 @6 b" l! `  M9 F$ g' m8 s
  86.         USART_Init(USART1, &USART_InitStructure);
    ! c2 _$ Y, q8 y; ?) G
  87.        
    : M/ y# A8 A, x8 i2 w
  88.         USART_ITConfig(USART1, USART_IT_IDLE, ENABLE);        /* 使能空闲中断 */
    . X$ Y9 e' Q! e; \: _: j+ `
  89.         USART_OverrunDetectionConfig(USART1, USART_OVRDetection_Disable);
    $ O- B7 N3 u* o0 S& |
  90.         - b7 E: p4 ]* t5 J1 R
  91.         USART_Cmd(USART1, ENABLE);
    " t& Q1 i* n8 Y. ?: k' h: j
  92.         USART_DMACmd(USART1, USART_DMAReq_Rx|USART_DMAReq_Tx, ENABLE); /* 使能DMA收发 */4 u( j- N6 H: q

  93. . O* v% R, A) Z: B: Y4 S! @6 j
  94.         /* 串口中断 */- A! Z# N4 A2 w
  95.         NVIC_InitStructure.NVIC_IRQChannel         = USART1_IRQn;+ ~3 n+ _' q& W# z/ @7 ^6 H" ?
  96.         NVIC_InitStructure.NVIC_IRQChannelPriority = 2;( E' o* v7 S0 Y" j
  97.         NVIC_InitStructure.NVIC_IRQChannelCmd      = ENABLE;4 h' R5 o- c( f& a
  98.         NVIC_Init(&NVIC_InitStructure);
    5 [8 |2 v) u9 R

  99. : f6 ]2 A1 W# s8 b7 v
  100.         /* DMA中断 */0 m4 H' F7 H2 E
  101.           NVIC_InitStructure.NVIC_IRQChannel                    = DMA1_Channel2_3_IRQn;       ; N# W7 P, U0 B/ E5 S8 l
  102.           NVIC_InitStructure.NVIC_IRQChannelPriority = 0;
    / D0 }7 m) _" L) j% s' a* V
  103.         NVIC_InitStructure.NVIC_IRQChannelCmd      = ENABLE;
    & f) }/ x+ I1 m
  104.           NVIC_Init(&NVIC_InitStructure);4 T* `6 G6 a6 Z4 }, |
  105. }
    1 a2 [2 @) E" n. [# [' U

  106.   C$ f6 v# c& c8 d% y+ n8 X& G3 X& C
  107. /**  r" V  w4 D3 [+ Q. d
  108. * @brief  
    9 C0 W" G" f2 C. \( b9 F( }
  109. * @param  
    4 g; e5 I( R  G
  110. * @retval + [  p! x: d  c4 m4 [7 W' ~( t8 p
  111. */$ T% {2 }2 D2 R& h  Q4 p/ q
  112. void bsp_uart2_init(void)2 w6 {1 |2 [( R9 `
  113. {  ^: b- q% F  n1 |* D( C' w
  114.         USART_InitTypeDef USART_InitStructure;
    / \0 |  N0 C+ z7 ?3 G
  115.         NVIC_InitTypeDef NVIC_InitStructure;
    , H$ x% `/ I' R
  116.        
    2 O) Y! z% |9 k  s
  117.         bsp_uart2_gpio_init();
    , ]" ^6 ~2 J# b7 F$ w5 l& a
  118.           ?+ c" \- z7 N
  119.         /* 使能串口和DMA时钟 */
    / L" q: M) }2 G0 M! H9 o+ n
  120.         RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);6 p% `4 J# K( ~
  121.         RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2, ENABLE);
    $ W) i0 @5 O, L
  122. 7 e5 V5 j) C; m/ _' ]
  123.         USART_InitStructure.USART_BaudRate            = 57600;
    4 X& V4 v# j/ `) ~! _" a
  124.         USART_InitStructure.USART_WordLength          = USART_WordLength_8b;( @. D7 L# u( ?
  125.         USART_InitStructure.USART_StopBits            = USART_StopBits_1;
    8 p( N  X3 i7 D. Z8 u+ F+ D
  126.         USART_InitStructure.USART_Parity              = USART_Parity_No;
    : `. r1 C& O8 t8 z3 a$ r
  127.         USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
    " j- O# K! B+ r9 L
  128.         USART_InitStructure.USART_Mode                = USART_Mode_Rx | USART_Mode_Tx;
    . ~8 G! `/ u7 }7 Y" u
  129.         USART_Init(USART2, &USART_InitStructure);# h0 u3 Q$ K  v( j! H
  130.         & x( |' b  ^1 n; W
  131.         USART_ITConfig(USART2, USART_IT_IDLE, ENABLE);        /* 使能空闲中断 */' \8 S) R# k4 F7 x4 y
  132.         USART_OverrunDetectionConfig(USART2, USART_OVRDetection_Disable);
    " s' ]# f& |, g" @6 ^+ ?0 X
  133.        
    7 h: O, k8 Y9 c2 T) y6 l
  134.         USART_Cmd(USART2, ENABLE);( M- f& T1 K$ o( a# l, J
  135.         USART_DMACmd(USART2, USART_DMAReq_Rx|USART_DMAReq_Tx, ENABLE);         /* 使能DMA收发 */% c% J$ n; u. Q5 c; H) r
  136. / u. U/ C" f" o
  137.         /* 串口中断 */
    / w; J& j% s& G: K  x
  138.         NVIC_InitStructure.NVIC_IRQChannel         = USART2_IRQn;# d+ @0 g" T4 v& J, Z: s. d" r1 s$ t
  139.         NVIC_InitStructure.NVIC_IRQChannelPriority = 2;$ T% c# p5 W/ W' _3 |8 B
  140.         NVIC_InitStructure.NVIC_IRQChannelCmd      = ENABLE;
    + y( K* T# N8 R2 f
  141.         NVIC_Init(&NVIC_InitStructure);
    3 i9 X; `! E5 [+ g

  142. 4 s' f5 A. a! P3 U; a
  143.         /* DMA中断 */! K( f( i7 j* D  N- r+ o
  144.         NVIC_InitStructure.NVIC_IRQChannel         = DMA1_Channel4_5_IRQn;       : W  M, ~9 J" r
  145.           NVIC_InitStructure.NVIC_IRQChannelPriority = 0; / M4 r5 c! ^& `  L: E( |
  146.         NVIC_InitStructure.NVIC_IRQChannelCmd      = ENABLE;
    0 S" T7 ?. I. {4 s# @1 `* @
  147.           NVIC_Init(&NVIC_InitStructure);1 R. M# A* J, I3 k  e2 b
  148. }
    $ B, i" o. E( @4 K" @
  149.   S9 i1 s' q# C6 P3 r
  150. void bsp_uart1_dmatx_config(uint8_t *mem_addr, uint32_t mem_size)1 d+ m) p8 o& E. |
  151. {. e; F% n# S, N( ~: z# u. a2 H% f7 |6 c
  152.           DMA_InitTypeDef DMA_InitStructure;, w0 G; ]1 Q/ R$ t" p3 A
  153.         ; |" d" e/ J* I0 X" e, t
  154.         DMA_DeInit(DMA1_Channel2);
    4 T8 s2 a5 c" \8 s
  155.         DMA_Cmd(DMA1_Channel2, DISABLE);% m, h  n& p$ Y0 ^  ]
  156.         DMA_InitStructure.DMA_PeripheralBaseAddr         = (uint32_t)&(USART1->TDR);& |% |6 G3 o% W$ ^4 H& F- r
  157.         DMA_InitStructure.DMA_MemoryBaseAddr                 = (uint32_t)mem_addr;
    1 {) N. R& g  y" a! S4 Z
  158.         DMA_InitStructure.DMA_DIR                                         = DMA_DIR_PeripheralDST;         /* 传输方向:内存->外设 */4 X  j, {! U$ I5 [, F) J7 L/ g
  159.         DMA_InitStructure.DMA_BufferSize                         = mem_size; ( f3 Y9 f; `! z. B" x% {6 ?5 d6 S! q
  160.         DMA_InitStructure.DMA_PeripheralInc                 = DMA_PeripheralInc_Disable;
    ; Z0 U, R( y* z. f% z% h7 n7 P- {
  161.         DMA_InitStructure.DMA_MemoryInc                         = DMA_MemoryInc_Enable; ' X7 c! L8 H9 J/ [5 Z) y
  162.         DMA_InitStructure.DMA_PeripheralDataSize         = DMA_PeripheralDataSize_Byte;
      |7 b: W7 K. _8 ~9 c6 p; n
  163.         DMA_InitStructure.DMA_MemoryDataSize                 = DMA_MemoryDataSize_Byte;
    5 f' A3 L7 i3 y6 s
  164.         DMA_InitStructure.DMA_Mode                                         = DMA_Mode_Normal; * @& c2 X) L# O/ o6 K; r' e
  165.         DMA_InitStructure.DMA_Priority                                 = DMA_Priority_High; 6 G6 K/ W5 l+ ^5 [( B$ l
  166.         DMA_InitStructure.DMA_M2M                                         = DMA_M2M_Disable;
    5 ~: \1 e/ x2 R! t' h- C5 ?
  167.         DMA_Init(DMA1_Channel2, &DMA_InitStructure);  
    1 |) l/ ]  ~$ e2 i
  168.         DMA_ITConfig(DMA1_Channel2, DMA_IT_TC|DMA_IT_TE, ENABLE); 7 ~7 X# L7 G1 f/ B! s8 c! s0 [
  169.         DMA_ClearFlag(DMA1_IT_TC2);        /* 清除发送完成标识 */, U8 m  p- c9 V! O8 |2 f
  170.         DMA_Cmd(DMA1_Channel2, ENABLE);
    0 V5 L; T% G  L3 J0 W2 b- ^, E  T" A
  171. }
    5 ]; c6 @- \/ d9 N+ p% U: U0 w

  172. 6 P, ^; \! H/ Z# x
  173. void bsp_uart1_dmarx_config(uint8_t *mem_addr, uint32_t mem_size)7 Q- M9 Y' _0 Q( z9 H6 _# d& a
  174. {& r  _4 F! h9 H0 W+ ^
  175.           DMA_InitTypeDef DMA_InitStructure;
    : B% F  o/ W; M, d
  176.        
    ! ^2 q4 i6 y$ Q* t& e* J
  177.         DMA_DeInit(DMA1_Channel3);
    & R" }+ Y0 o5 P: q7 y, m# V" _9 R+ j, d8 |
  178.         DMA_Cmd(DMA1_Channel3, DISABLE);! N! m$ J) r! f; m& g$ @) {
  179.         DMA_InitStructure.DMA_PeripheralBaseAddr         = (uint32_t)&(USART1->RDR);
    1 I  t# Y) V3 N  B7 a
  180.         DMA_InitStructure.DMA_MemoryBaseAddr                 = (uint32_t)mem_addr; - k+ L" M0 Z/ q  g2 G
  181.         DMA_InitStructure.DMA_DIR                                         = DMA_DIR_PeripheralSRC;         /* 传输方向:外设->内存 */
    ) t2 t4 l/ h% k" ^5 U+ q
  182.         DMA_InitStructure.DMA_BufferSize                         = mem_size;
    7 c# a. U9 \) X0 p7 @" y
  183.         DMA_InitStructure.DMA_PeripheralInc                 = DMA_PeripheralInc_Disable; * t- M1 Y1 T& \4 w0 N, q
  184.         DMA_InitStructure.DMA_MemoryInc                         = DMA_MemoryInc_Enable;
    * C8 Q: j* o# n! ]; T$ D' p3 H
  185.         DMA_InitStructure.DMA_PeripheralDataSize         = DMA_PeripheralDataSize_Byte;
    3 z! b! L" D! x; l" k$ I
  186.         DMA_InitStructure.DMA_MemoryDataSize                 = DMA_MemoryDataSize_Byte;6 Z/ F) @6 Y: T: Z; ~
  187.         DMA_InitStructure.DMA_Mode                                         = DMA_Mode_Circular; + Q6 C  V2 ~/ [$ f9 g
  188.         DMA_InitStructure.DMA_Priority                                 = DMA_Priority_VeryHigh; - [0 Z/ o0 e# H9 {  R
  189.         DMA_InitStructure.DMA_M2M                                         = DMA_M2M_Disable; 1 L2 I+ V, [+ S0 S* R6 ?, Q
  190.         DMA_Init(DMA1_Channel3, &DMA_InitStructure);
    - A+ h6 i( v: M. s) W
  191.         DMA_ITConfig(DMA1_Channel3, DMA_IT_TC|DMA_IT_HT|DMA_IT_TE, ENABLE);/* 使能DMA半满、全满、错误中断 */" Y  r6 C/ b0 `2 v- g7 I6 u- k
  192.         DMA_ClearFlag(DMA1_IT_TC3);
    ! N# U2 d/ @. _. M6 N
  193.         DMA_ClearFlag(DMA1_IT_HT3);
    * `7 N0 p' Y# f, N6 K8 }9 U6 n8 Y
  194.         DMA_Cmd(DMA1_Channel3, ENABLE); 5 v* ]; @7 ~2 X1 a# z/ }
  195. }
    7 h7 z9 K- a- k" i9 i& [+ }; `

  196. / S5 r( _6 T% b% P
  197. uint16_t bsp_uart1_get_dmarx_buf_remain_size(void)  \4 V0 B* E+ ~8 C5 c% e
  198. {
    2 r: }0 p* ~. P, O* v3 X) e
  199.         return DMA_GetCurrDataCounter(DMA1_Channel3);        /* 获取DMA接收buf剩余空间 */
    . F' E. x# @, w2 K& E0 |
  200. }
    ' Z6 i6 t0 ]. }! P, l8 k  X
  201. 0 {& n+ h1 f% c2 s4 ~
  202. void bsp_uart2_dmatx_config(uint8_t *mem_addr, uint32_t mem_size)
    5 W& d: J+ S# g9 x; T8 V  x
  203. {
    ) z1 }! {0 U& A% l/ v, p
  204.           DMA_InitTypeDef DMA_InitStructure;& A8 v( G. Z9 k" {  F1 Q8 I
  205.        
    * Z# K) a6 t# W* ~* h8 }
  206.         DMA_DeInit(DMA1_Channel4);
    $ u- F3 R, u7 m% s
  207.         DMA_Cmd(DMA1_Channel4, DISABLE);+ a& b9 `9 h" g$ r: l
  208.         DMA_InitStructure.DMA_PeripheralBaseAddr         = (uint32_t)&(USART2->TDR);: i( W/ Z& \( ^" x, c8 E- t! B' |; g
  209.         DMA_InitStructure.DMA_MemoryBaseAddr                 = (uint32_t)mem_addr; ' L! {% _- l: i  x1 o' |' e
  210.         DMA_InitStructure.DMA_DIR                                         = DMA_DIR_PeripheralDST;         /* 传输方向:内存->外设 */
    ' E2 i' g) i- }; l! x3 v
  211.         DMA_InitStructure.DMA_BufferSize                         = mem_size; 7 ]1 N: Q/ g/ o0 K+ v
  212.         DMA_InitStructure.DMA_PeripheralInc                 = DMA_PeripheralInc_Disable; $ N, z5 w( k, w& `  y8 j  Z& Y
  213.         DMA_InitStructure.DMA_MemoryInc                         = DMA_MemoryInc_Enable;
    / Y/ D- k( Z' m5 X. r5 g/ o9 ?
  214.         DMA_InitStructure.DMA_PeripheralDataSize         = DMA_PeripheralDataSize_Byte;
    4 r. f+ q- o8 s9 q, N/ B
  215.         DMA_InitStructure.DMA_MemoryDataSize                 = DMA_MemoryDataSize_Byte;
    ! E: R- w* R# u! H* u1 e5 v
  216.         DMA_InitStructure.DMA_Mode                                         = DMA_Mode_Normal;
    ( r/ M0 F' H* l. s$ _  Z
  217.         DMA_InitStructure.DMA_Priority                                 = DMA_Priority_High;   r! k" y5 k* u/ f6 v. A7 y+ `, d
  218.         DMA_InitStructure.DMA_M2M                                         = DMA_M2M_Disable;
    & v# s+ e- _% [  V4 k. e/ `+ o
  219.         DMA_Init(DMA1_Channel4, &DMA_InitStructure);  
    2 L% g# P1 G" l3 k
  220.         DMA_ITConfig(DMA1_Channel4, DMA_IT_TC|DMA_IT_TE, ENABLE); - t$ t8 m6 O$ Z1 f3 b. x6 p
  221.         DMA_ClearFlag(DMA1_IT_TC4);        /* 清除发送完成标识 */
    ' L0 P2 w$ Q  H( ?8 Q  _: X
  222.         DMA_Cmd(DMA1_Channel4, ENABLE);
    % N! i: r+ f! E5 y9 T
  223. }
    1 f; i; o0 Z, n# c
  224. 0 o# ]) P5 r2 R
  225. void bsp_uart2_dmarx_config(uint8_t *mem_addr, uint32_t mem_size)
    & Q; [) d1 a6 V
  226. {/ ]4 l  W( \1 T' e
  227.           DMA_InitTypeDef DMA_InitStructure;
    5 Q6 r$ x  S) q4 g
  228.         1 _6 V5 x6 p3 F! r. Z, A/ D! U
  229.         DMA_DeInit(DMA1_Channel5); 8 C8 h9 g* j3 [- |- q
  230.         DMA_Cmd(DMA1_Channel5, DISABLE);
    ) o$ z* ?+ ~( {5 L
  231.         DMA_InitStructure.DMA_PeripheralBaseAddr         = (uint32_t)&(USART2->RDR);9 Z% U7 a3 F& F+ ]& _3 G: F* r& K
  232.         DMA_InitStructure.DMA_MemoryBaseAddr                 = (uint32_t)mem_addr;
      d7 ?( E+ A, ^% f$ E7 E6 Y
  233.         DMA_InitStructure.DMA_DIR                                         = DMA_DIR_PeripheralSRC;         /* 传输方向:外设->内存 */
    ( i6 S4 p8 r5 h7 [- q) p+ C- R
  234.         DMA_InitStructure.DMA_BufferSize                         = mem_size;
    6 y3 `& c0 k& x: p9 T0 e& ^" F
  235.         DMA_InitStructure.DMA_PeripheralInc                 = DMA_PeripheralInc_Disable; ( c9 G9 T0 k: s: a! W5 W9 K  y
  236.         DMA_InitStructure.DMA_MemoryInc                         = DMA_MemoryInc_Enable;
    * j. e/ E( D  C# R( U
  237.         DMA_InitStructure.DMA_PeripheralDataSize         = DMA_PeripheralDataSize_Byte;
    1 K' C5 ^7 k" Y, y
  238.         DMA_InitStructure.DMA_MemoryDataSize                 = DMA_MemoryDataSize_Byte;
    ) a" u/ g# _5 {5 f
  239.         DMA_InitStructure.DMA_Mode                                         = DMA_Mode_Circular;
    + l" z" t" `9 P/ `9 c* c
  240.         DMA_InitStructure.DMA_Priority                                 = DMA_Priority_VeryHigh;
    - ?. o- ]- @0 u
  241.         DMA_InitStructure.DMA_M2M                                         = DMA_M2M_Disable;
    + n1 Q& N7 j7 m& c. G) P
  242.         DMA_Init(DMA1_Channel5, &DMA_InitStructure); : z2 H) C) z! D  o
  243.         DMA_ITConfig(DMA1_Channel5, DMA_IT_TC|DMA_IT_HT|DMA_IT_TE, ENABLE);/* 使能DMA半满、全满、错误中断 */
    7 a. C7 ~& t" D' P7 N. b
  244.         DMA_ClearFlag(DMA1_IT_TC5);
    3 q: N5 ^+ d+ f2 c, J
  245.         DMA_ClearFlag(DMA1_IT_HT5);2 g: [3 g( X7 q+ F0 L+ R
  246.         DMA_Cmd(DMA1_Channel5, ENABLE); # a/ l% s. r- X0 |! l4 D0 j3 d
  247. }" _8 `1 w% V2 w$ z) {

  248. - W3 R; G4 a7 i% W- d3 f
  249. uint16_t bsp_uart2_get_dmarx_buf_remain_size(void)! ^; ~7 h+ L. d  G* E* @; S
  250. {; C7 j5 Z( n) u7 P6 R) `8 ~* T0 a4 I
  251.         return DMA_GetCurrDataCounter(DMA1_Channel5);        /* 获取DMA接收buf剩余空间 */- E" f6 _6 [9 K- {* n! Z
  252. }
复制代码
9 Q& _; |7 r$ Y, P' }4 Q


, x: |. J7 E1 U2 P

压力测试:

  • 1.5Mbps波特率,串口助手每毫秒发送1k字节数据,stm32f0 DMA接收数据,再通过DMA发送回串口助手,毫无压力。
  • 1.5Mbps波特率,可传输大文件测试,将接受数据保存为文件,与源文件比较。
  • 串口高波特率测试需要USB转TLL工具及串口助手都支持才可行,推荐CP2102、FT232芯片的USB转TTL工具。3 R; K) y# q. t- c$ K0 I
    4 w( K! J* x3 \+ I- @
/ W( y* T$ S. ]" U- L9 W" b# [

7.png

+ Q  w1 M. j1 l" F6 c8 O
1.5Mbps串口回环压力测试

- S% S* o; e$ K" |3 Z$ b. w; @
  X+ w& E0 X  _6 [* w. N' `1 u% f
# E5 ]7 U& `0 }! [5 |
收藏 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 手机版