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

【经验分享】STM32串口DMA收发机制

[复制链接]
STMCU小助手 发布时间:2022-1-5 21:11
1 前言

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

  • 内存—>内存,内存间拷贝
  • 外设—>内存,如uart、spi、i2c等总线接收数据过程
  • 内存—>外设,如uart、spi、i2c等总线发送数据过程
    ( }8 u) ?, [& Q' Z9 I
    + p2 T  B$ U6 }1 ^9 S

    ' _7 A1 h5 ^, w) s6 P, E9 Z
2 串口有必要使用DMA吗

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

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

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

    / l3 K2 P% `2 T: f
    2 T: |  |0 V, K7 h) ^; F; i; S& f* T

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

3 实现方式
9 L# O3 A4 j! V0 q7 V
[29J8H4IWN~W5SX]S96SO.png

4 Y' t5 M9 M) ^( w3 w$ a" t* U% X1 k! i/ h/ s
4 STM32串口使用DMA

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

测试平台:

  • STM32F030C8T6
  • UART1/UART2
  • DMA1 Channel2—Channel5
  • ST标准库
  • 主频48MHz(外部12MHz晶振)
    ; \8 t1 o+ M: d+ E

    & D" c& k' S/ z

    6 V8 s% }, W. V" e
    3 `! t/ h6 N6 X- l
VQ)B5)48I]XV2S0[)JIAZPS.png
4 K* D8 Z; W4 h6 Z8 S0 b
; a& [6 ]  ~9 `7 v1 i0 ?: y0 ?
5 串口DMA接收
5.1 基本流程
6 d$ f6 P. ?8 j5 M
$)5TD]C}DU`]XB0G@_3DIP9.png

* k4 @  U7 \' m# l& Z- k. K  L
5.2 相关配置

关键步骤

【1】初始化串口

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

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

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

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

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

【1】第一步,DMA先将数据搬运到buf1,搬运完成通知CPU来拷贝buf1数据 【2】第二步,DMA将数据搬运到buf2,与CPU拷贝buf1数据不会冲突 【3】第三步,buf2数据搬运完成,通知CPU来拷贝buf2数据 【4】执行完第三步,DMA返回执行第一步,一直循环

) q" Z3 N2 A9 O& s0 S
Y[GW(8F02}1LW]A7N[Z8{~X.png

& x. ]0 A( K; r1 x1 j5 U

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

【1】第一步,DMA将数据搬运完成buf的前一半时,产生“半满中断”,CPU来拷贝buf前半部分数据 【2】第二步,DMA继续将数据搬运到buf的后半部分,与CPU拷贝buf前半部数据不会冲突 【3】第三步,buf后半部分数据搬运完成,触发“溢满中断”,CPU来拷贝buf后半部分数据 【4】执行完第三步,DMA返回执行第一步,一直循环

  X& l) r/ t& b# I% x7 D7 F
0]0H$SQN9V)T8MMN65WF0(8.png

2 r* Z! q1 p4 W, ]/ g8 y# R

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

  • 串口接收,DMA通道工作模式设为连续模式
  • 使能DMA通道接收buf半满中断、溢满(传输完成)中断
  • 启动DMA通道前清空相关状态标识,防止首次传输错乱数据
    4 w; @/ G& S9 b, S
  1. void bsp_uart2_dmarx_config(uint8_t *mem_addr, uint32_t mem_size)/ F" ~* K, ^1 j5 T
  2. {
    / W" G! x9 f! T6 _, M  |
  3.    DMA_InitTypeDef DMA_InitStructure;
    ; T0 c+ Z! v; A8 c! T5 A6 {
  4. # V1 B: ^8 Q6 G, ~, M8 _" S
  5. DMA_DeInit(DMA1_Channel5); 6 I4 H+ o0 s; H% C: @7 z: k' y
  6. DMA_Cmd(DMA1_Channel5, DISABLE);
    4 Z/ n, o1 d' |
  7. DMA_InitStructure.DMA_PeripheralBaseAddr  = (uint32_t)&(USART2->RDR);/* UART2接收数据地址 */% V( _# q7 u1 H
  8. DMA_InitStructure.DMA_MemoryBaseAddr   = (uint32_t)mem_addr; /* 接收buf */! q: Q) ?" s7 {: |2 D
  9. DMA_InitStructure.DMA_DIR      = DMA_DIR_PeripheralSRC;  /* 传输方向:外设->内存 */
    ) ~/ C. m" S% m& H
  10. DMA_InitStructure.DMA_BufferSize    = mem_size; /* 接收buf大小 */9 x, j& c) b& n
  11. DMA_InitStructure.DMA_PeripheralInc   = DMA_PeripheralInc_Disable;
    ; h! g5 b9 l  L% I1 L0 K/ {7 j
  12. DMA_InitStructure.DMA_MemoryInc    = DMA_MemoryInc_Enable;
    6 V, `8 J  c+ G# R
  13. DMA_InitStructure.DMA_PeripheralDataSize  = DMA_PeripheralDataSize_Byte; 6 i5 B: ?+ C9 X. D- z& W
  14. DMA_InitStructure.DMA_MemoryDataSize   = DMA_MemoryDataSize_Byte;3 a# J( l+ i* ~6 E3 b$ e
  15. DMA_InitStructure.DMA_Mode      = DMA_Mode_Circular; /* 连续模式 */
    : K) p. l! [5 v- q! V8 U
  16. DMA_InitStructure.DMA_Priority     = DMA_Priority_VeryHigh; ( M- e6 F! O& v7 x
  17. DMA_InitStructure.DMA_M2M      = DMA_M2M_Disable;
    ' m; w5 \0 v/ W$ E; H0 k
  18. DMA_Init(DMA1_Channel5, &DMA_InitStructure);
    # ]: n" ?  Q' L4 ^9 ~4 C
  19. DMA_ITConfig(DMA1_Channel5, DMA_IT_TC|DMA_IT_HT|DMA_IT_TE, ENABLE);/* 使能DMA半满、溢满、错误中断 */( ?, [/ K% v! b
  20. DMA_ClearFlag(DMA1_IT_TC5); /* 清除相关状态标识 */# E6 _* Q, o5 Q% W/ A
  21. DMA_ClearFlag(DMA1_IT_HT5);
      V' g% w  R: I7 f9 C9 O3 h# d
  22. DMA_Cmd(DMA1_Channel5, ENABLE); 6 N+ Y1 g" Z" o% i
  23. }
复制代码
5 T/ Q. Y# T1 M, K7 V, F# s


$ U1 p5 @( O2 @. A& W

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


+ Y) c# @- `" N1 G$ u: m

5.3 接收处理
3 i$ O% z- E" Y

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

  • DMA通道buf溢满(传输完成)场景
  • DMA通道buf半满场景
  • 串口空闲中断场景

    + ]/ |. G2 z  e; V! C' b- l- w  _" N

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


9 l$ p4 x+ [# k/ e+ `  P. E8 T% M

5.3 .1 接收数据大小

; s+ S0 f4 P7 r5 M2 k, [) C

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

  • 数据刚好是DMA接收buf的整数倍,这是理想的状态
  • 数据量小于DMA接收buf或者小于接收buf的一半,此时会触发串口空闲中断

    5 ?: L$ w. [3 @7 E
    ; N4 W0 o( X' C" ^1 f

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

  1. /* 获取DMA通道接收buf剩余空间大小 */9 d+ _$ l# K* U9 ~# }  L8 t
  2. uint16_t DMA_GetCurrDataCounter(DMA_Channel_TypeDef* DMAy_Channelx);
复制代码

: x/ l4 `3 }3 R0 U+ q

DMA通道buf溢满场景计算

  1. 接收数据大小 = DMA通道buf大小 - 上一次接收的总数据大小
复制代码
+ y% b' i7 a3 L! h! p/ i3 \1 d

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

  1. void uart_dmarx_done_isr(uint8_t uart_id)" [# E4 j2 ]3 b0 q$ |" B
  2. {8 z; X8 X( @+ s4 M1 l/ c8 S
  3.    uint16_t recv_size;
    2 x3 }) @. `% U! {! X2 T
  4. 2 D* f  v' k+ u  t: g' b* j  r
  5. recv_size = s_uart_dev[uart_id].dmarx_buf_size - s_uart_dev[uart_id].last_dmarx_size;! T& q* O$ [" Z! E

  6. 4 C$ ~6 Q: S9 C. `3 Z0 l: X
  7. fifo_write(&s_uart_dev[uart_id].rx_fifo, $ _' E7 L% v: z* A
  8.        (const uint8_t *)&(s_uart_dev[uart_id].dmarx_buf[s_uart_dev[uart_id].last_dmarx_size]), recv_size);
    ! O0 D. Y8 [4 w( M

  9. + z, i" l% ]. q; o: }$ n
  10. s_uart_dev[uart_id].last_dmarx_size = 0;
    - Y; C2 G$ x' w$ W0 L& N" A( ~
  11. }
复制代码

& E; x3 v3 R9 J

DMA通道buf半满场景计算

  1. 接收数据大小 = DMA通道接收总数据大小 - 上一次接收的总数据大小
    % w# \0 B/ I& H5 S! w9 x
  2. DMA通道接收总数据大小 = DMA通道buf大小 - DMA通道buf剩余空间大小
复制代码


: ]2 B6 m( m0 {, N- `

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

  1. void uart_dmarx_half_done_isr(uint8_t uart_id)( O( H2 b0 v7 V, R
  2. {
    2 B0 D8 i- k) d$ ~" v0 k
  3.    uint16_t recv_total_size;. C& G$ ~% g5 v( L5 m, |
  4.    uint16_t recv_size;
    # x0 v* R# }- Y1 B" k
  5. / L! e* p2 U9 x, r) K4 H1 H# q
  6. if(uart_id == 0)
    - \" b1 c5 K' I
  7. {( M4 r( j; G4 z6 S. o
  8.     recv_total_size = s_uart_dev[uart_id].dmarx_buf_size - bsp_uart1_get_dmarx_buf_remain_size();7 J  f# p5 L; L7 I
  9. }6 R' L) c1 q8 ?( p* d! \- p
  10. else if (uart_id == 1)  m' ?' S9 R5 z# B  W. K
  11. {$ h6 O' |, o9 S# T6 N# B
  12.   recv_total_size = s_uart_dev[uart_id].dmarx_buf_size - bsp_uart2_get_dmarx_buf_remain_size();
    ; ]# U; m+ o1 }5 Z6 Z
  13. }
    5 [% s: N( k2 C+ q+ S+ k( n% H# y
  14. recv_size = recv_total_size - s_uart_dev[uart_id].last_dmarx_size;
    1 o4 j) m( A1 a+ B' W- x2 Q

  15. ) E" q8 {5 A& ~7 Q7 B4 ~8 \
  16. fifo_write(&s_uart_dev[uart_id].rx_fifo,
    * _1 q5 N, V- N% C
  17.        (const uint8_t *)&(s_uart_dev[uart_id].dmarx_buf[s_uart_dev[uart_id].last_dmarx_size]), recv_size);' }2 k3 [8 C; g" n9 d
  18. s_uart_dev[uart_id].last_dmarx_size = recv_total_size;/* 记录接收总数据大小 */, i1 V" A; k/ e5 f4 O
  19. }
复制代码

! e9 N/ I  A& H. i4 b9 M* {

串口空闲中断场景计算

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

串口空闲中断处理函数:

  1. void uart_dmarx_idle_isr(uint8_t uart_id): p/ e+ m, D% N# [/ q/ t" g
  2. {; Q/ C, Z% i( K# U. C5 E& j
  3.    uint16_t recv_total_size;# N- W2 }( b9 d7 X/ t
  4.    uint16_t recv_size;
    9 t' a( j7 `7 d4 K: W$ {

  5. ! i$ v7 n) n% E" T# f
  6. if(uart_id == 0)/ _& |" S5 |7 C8 A" H- G
  7. {
    # A! S  K9 K8 x: q
  8.     recv_total_size = s_uart_dev[uart_id].dmarx_buf_size - bsp_uart1_get_dmarx_buf_remain_size();# c: Q" k; u7 K
  9. }
    8 |  Y$ }5 e7 L% T
  10. else if (uart_id == 1)
    ( n! w/ X, R- ?$ ?( b
  11. {
    1 ^+ u& ^6 F0 `. d1 u& |+ B$ W
  12.   recv_total_size = s_uart_dev[uart_id].dmarx_buf_size - bsp_uart2_get_dmarx_buf_remain_size();5 I4 L; Z% M" j
  13. }
    ) F4 s+ I5 V  ^9 e6 f
  14. recv_size = recv_total_size - s_uart_dev[uart_id].last_dmarx_size;. c, y, |: N# h0 z
  15. s_UartTxRxCount[uart_id*2+1] += recv_size;, r9 D, H% v3 {: Z+ K1 ^! H' e
  16. fifo_write(&s_uart_dev[uart_id].rx_fifo, + O; G9 _: I5 r9 p* k1 W4 f7 E
  17.        (const uint8_t *)&(s_uart_dev[uart_id].dmarx_buf[s_uart_dev[uart_id].last_dmarx_size]), recv_size);
    2 O4 N  o* Y, L
  18. s_uart_dev[uart_id].last_dmarx_size = recv_total_size;! g) {# y- r( q! x; {9 g& `; D
  19. }
复制代码

. T/ a4 k1 O$ y2 |

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

2 J' X& f# P. U7 K) C; l

5.3.2 接收数据偏移地址
& Q. L( q: ^: s8 I0 g( N

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

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


  1. " A. C# L% E* i6 i
  2. void uart_dmarx_done_isr(uint8_t uart_id)
    9 n; O$ O- y9 w# \) Y% l/ q: ^
  3. {
    4 r, O4 u; @/ l2 o
  4.   /* todo */
    % G0 V- w6 s* S) l  u, R, h
  5. s_uart_dev[uart_id].last_dmarx_size = 0;& V- y% ], T4 T& Q. X
  6. }
复制代码

* ?, T7 `# @! ?# r
5.4 应用读取串口数据方法

) O' E: k+ R" y0 `6 `

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

! F$ `: u2 a1 H3 b- }# j

6 串口DMA发送
5.1 基本流程

( }7 s5 n1 ]6 s" ?
3GZD_)OPJ_TWP(YL80Q5ZPB.png

5 |0 Y( m( i/ `' T, G% z
5.2 相关配置

' T" K" ?' n: x1 j

关键步骤

【1】初始化串口

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

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

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

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

    , d1 c  s! o2 f( r
  1. void bsp_uart2_dmatx_config(uint8_t *mem_addr, uint32_t mem_size)& ^+ Z  Y- z0 n7 H: H, x" R
  2. {
    # b. l5 W1 q! E. f8 v3 G
  3.    DMA_InitTypeDef DMA_InitStructure;: K) r9 g2 S: `& w, z( n
  4. " Y5 D4 b" X& I6 i0 \* t( e% M
  5. DMA_DeInit(DMA1_Channel4);( S& a/ c! L1 s  F
  6. DMA_Cmd(DMA1_Channel4, DISABLE);) o2 b0 F! o% J9 S6 f# e# G
  7. DMA_InitStructure.DMA_PeripheralBaseAddr  = (uint32_t)&(USART2->TDR);/* UART2发送数据地址 */; C/ n4 [- i$ `" _5 K) q
  8. DMA_InitStructure.DMA_MemoryBaseAddr   = (uint32_t)mem_addr;  /* 发送数据buf */
    5 }8 s* t1 M, c- ?1 T$ S! N
  9. DMA_InitStructure.DMA_DIR      = DMA_DIR_PeripheralDST;  /* 传输方向:内存->外设 */7 ?$ U8 O6 h0 }$ \$ r
  10. DMA_InitStructure.DMA_BufferSize    = mem_size;    /* 发送数据buf大小 */; |5 T7 V! F0 e- W% f- K
  11. DMA_InitStructure.DMA_PeripheralInc   = DMA_PeripheralInc_Disable;
    0 l% p; D0 X& y. ~. ^3 A
  12. DMA_InitStructure.DMA_MemoryInc    = DMA_MemoryInc_Enable;
    6 R* O) q. I- W2 W: o; e
  13. DMA_InitStructure.DMA_PeripheralDataSize  = DMA_PeripheralDataSize_Byte; 0 t7 r7 M' R8 ?3 a# z
  14. DMA_InitStructure.DMA_MemoryDataSize   = DMA_MemoryDataSize_Byte;
    6 G- \, o: {# y& F: F
  15. DMA_InitStructure.DMA_Mode      = DMA_Mode_Normal;   /* 单次模式 */# n9 t% }( a& n1 D
  16. DMA_InitStructure.DMA_Priority     = DMA_Priority_High;  $ @* F- ^6 l9 l- y
  17. DMA_InitStructure.DMA_M2M      = DMA_M2M_Disable;
    3 Z7 L, g6 N* ^6 Q( d& X
  18. DMA_Init(DMA1_Channel4, &DMA_InitStructure);  
    : t. a; F5 @2 \( V* Q2 c- `  W2 `
  19. DMA_ITConfig(DMA1_Channel4, DMA_IT_TC|DMA_IT_TE, ENABLE); /* 使能传输完成中断、错误中断 */- k9 t3 d2 O' J. I  p8 a# D
  20. DMA_ClearFlag(DMA1_IT_TC4); /* 清除发送完成标识 */
    9 l- L0 S3 K2 ^% q
  21. DMA_Cmd(DMA1_Channel4, ENABLE); /* 启动DMA发送 */
    4 _. c: o! k2 H& l9 v; s! q- o
  22. }
复制代码

0 o4 }  H* ?/ `7 r7 l! [
+ B) Y! s) }4 [$ ~. j% g; i# S/ L
5.3 发送处理
" S, i& G0 x  H; }+ [, ]* l& R0 |

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

串口发送处理函数:

  1. void uart_poll_dma_tx(uint8_t uart_id): @3 S  k" Y' `$ y
  2. {
    $ y. F9 s7 P7 v5 u# }
  3.    uint16_t size = 0;
    6 M0 v0 d+ U4 O* K2 L; d0 C' \

  4. , B) [9 n0 F! Y4 ]% i$ ?
  5. if (0x01 == s_uart_dev[uart_id].status)
    % b+ b8 I6 r, ]6 |  ~* T6 r. w; S
  6.     {
    " m. Y* I1 E% l2 O! ~2 @3 C7 s/ {
  7.         return;$ i3 P3 [4 ]( U, K. I0 R0 q0 K
  8.     }
    4 X: Q9 O% n. M5 s  H* b/ L  p7 u' x
  9. size = fifo_read(&s_uart_dev[uart_id].tx_fifo, s_uart_dev[uart_id].dmatx_buf,5 D3 y/ c! v5 g
  10.       s_uart_dev[uart_id].dmatx_buf_size);$ g# ~7 A  C) d
  11. if (size != 0)
    ! u  _0 E' \5 X( G- c' I1 I
  12. {2 N$ P% t" Q6 V  P( W* X2 i
  13.         s_UartTxRxCount[uart_id*2+0] += size;: ^! W- j( `# C$ l% w; J6 N8 r
  14.     if (uart_id == 0)/ u$ a# T4 I  K, T. N$ J9 j- x
  15.   {
    - B" U+ ]' f8 b5 |/ C8 v
  16.             s_uart_dev[uart_id].status = 0x01; /* DMA发送状态 */% `0 G6 [* F- e" R( V! ~" t7 ~
  17.      bsp_uart1_dmatx_config(s_uart_dev[uart_id].dmatx_buf, size);! T1 {, z. Z+ I6 s% F; G
  18.   }
    ) v" J9 `& a+ d( J
  19.   else if (uart_id == 1)
    8 q) ]: N3 P- s* t- e$ L
  20.   {
    , K: Z4 ^2 y9 l- G1 e1 ^+ n% k
  21.             s_uart_dev[uart_id].status = 0x01; /* DMA发送状态,必须在使能DMA传输前置位,否则有可能DMA已经传输并进入中断 */' ~- N8 g( Q0 j$ F) }
  22.    bsp_uart2_dmatx_config(s_uart_dev[uart_id].dmatx_buf, size);
    5 r* M6 c+ Z# X  U
  23.   }
    3 A- j7 C- C$ E- |+ g5 D; U" A/ x
  24. }: P0 t1 A7 ?) a0 B. R
  25. }
复制代码
2 _) l+ c5 w! ~- m; v$ O3 W4 Q
  • 注意发送状态标识,必须先置为“发送状态”,然后启动DMA 传输。如果步骤反过来,在传输数据量少时,DMA传输时间短,“DMA_IT_TC”中断可能比“发送状态标识置位”先执行,导致程序误判DMA一直处理发送状态(发送标识无法被清除)。
    # D5 p+ a* K1 H3 `, v* A0 ]" I6 b

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


4 }, E( S; I/ Y* u! ?1 L2 `, I: `) |& r

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

  1. void uart_dmatx_done_isr(uint8_t uart_id)
    4 P6 p8 g: K; S0 G  u9 ^& @: D
  2. {
    8 `6 W& K% w3 M  M5 |
  3.   s_uart_dev[uart_id].status = 0; /* 清空DMA发送状态标识 */5 H8 d- ?: i5 {
  4. }
复制代码
. d$ B8 `3 o: y+ ]) d/ E5 {

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

  • 主线程任务调用,前提是线程不能被其他任务阻塞,否则导致fifo溢出

    . S1 `7 _2 L" T+ b( q
  1. void thread(void)
    + \0 L8 H! O! n7 ~" e& }1 q6 |
  2. {7 m( h5 b* P# R
  3.     uart_poll_dma_tx(DEV_UART1);
    4 o$ x% I& G$ g$ J
  4.     uart_poll_dma_tx(DEV_UART2);8 L  C, @# w" j& e/ @0 R
  5. }
复制代码
/ c! v) q9 T7 t; q5 a5 O
  • 定时器中断中调用

    / a$ n' C* N! }' u5 A
  1. void TIMx_IRQHandler(void)
    6 J( G5 A; U4 {) r
  2. {3 y! D7 D3 l0 O( t4 ~0 i' ]. d7 {
  3.     uart_poll_dma_tx(DEV_UART1);  \2 \% U) u6 S
  4.     uart_poll_dma_tx(DEV_UART2);4 u* b2 B' E, Y9 D' w( W
  5. }
复制代码

( O4 {+ o  w4 J9 c+ v
  • DMA通道传输完成中断中调用

    % I' a3 Y4 N. l
  1. void DMA1_Channel4_5_IRQHandler(void)& d) ?1 A" K- J, I
  2. {
    5 Q5 x9 \/ j& E6 I7 d& I$ g
  3. if(DMA_GetITStatus(DMA1_IT_TC4))0 m! \0 O/ Y8 W' t
  4. {" N1 }9 w, C' `" N1 k; @4 m
  5.   UartDmaSendDoneIsr(UART_2);$ I/ i) I: l' x' C5 x. v3 a; E
  6.   DMA_ClearFlag(DMA1_FLAG_TC4);. T9 Q+ [0 m" s8 [/ {, D4 z! }7 Z
  7.   uart_poll_dma_tx(DEV_UART2);+ o+ t$ Z! Q) t
  8. }
    ( ~  _! ~* G  J. e3 h% b1 R# F
  9. }
复制代码

( {9 W7 L9 z! X3 V

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

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

  • 周期查询发送fifo数据,启动DMA传输,充分利用DMA发送效率,但可能降低串口数据流实时性
  • 实时查询发送fifo数据,加上超时处理,理想的方法
  • 在DMA传输完成中断中处理,保证实时连续数据流
    ( M% T4 M0 p1 U+ Y; I% ~: M
    , Y* _: S" z  ?. A; a& m8 H
6 串口设备
6.1 数据结构
  1. <div align="left">/* 串口设备数据结构 */</div><div align="left">typedef struct</div><div align="left">{</div> <div align="left">uint8_t status;   /* 发送状态 */</div><div align="left">_fifo_t tx_fifo;  /* 发送fifo */<font style="background-color:rgb(255, 255, 255)">
    2 s- Y: ]6 r0 d% m
  2. </font></div><div align="left">_fifo_t rx_fifo;  /* 接收fifo */</div><div align="left">uint8_t *dmarx_buf;  /* dma接收缓存 */<font style="background-color:rgb(255, 255, 255)">
    : p2 K4 T+ W! D9 R
  3. </font></div><div align="left">uint16_t dmarx_buf_size;/* dma接收缓存大小*/</div><div align="left">uint8_t *dmatx_buf;  /* dma发送缓存 */<font style="background-color:rgb(255, 255, 255)">
    0 j; V. S: b) J3 o
  4. </font></div><div align="left">uint16_t dmatx_buf_size;/* dma发送缓存大小 */</div><div align="left">uint16_t last_dmarx_size;/* dma上一次接收数据大小 */</div> <div align="left">}uart_device_t;</div>
复制代码

' J8 G# b# Q- Y
6.2 对外接口
  1. /* 串口注册初始化函数 */
    * z" \8 e/ j; B; z# b- H+ h/ k
  2. void uart_device_init(uint8_t uart_id)
    9 c/ K) W! m4 y& f; P9 X
  3. {: p3 v+ {8 Q) ]- B1 \0 S0 [
  4.    if (uart_id == 1)9 o4 _- n. C; y
  5. {$ j) l6 F. {* H6 z6 @
  6.   /* 配置串口2收发fifo */1 j4 x+ x9 g) O* x
  7.   fifo_register(&s_uart_dev[uart_id].tx_fifo, &s_uart2_tx_buf[0],
    6 Z7 [: z6 R! t2 g
  8.                       sizeof(s_uart2_tx_buf), fifo_lock, fifo_unlock);3 W, H' f  f. Q- Q6 s
  9.   fifo_register(&s_uart_dev[uart_id].rx_fifo, &s_uart2_rx_buf[0],
    - C- e" Q5 f# B' u. v4 Y7 `% B8 V
  10.                       sizeof(s_uart2_rx_buf), fifo_lock, fifo_unlock);
    ) A& o) D. m% Z3 u8 `6 v2 _; Q0 \
  11.   " U; _; c1 |: \& o! k" [
  12.   /* 配置串口2 DMA收发buf */; p' `4 l- H3 i. s2 B
  13.   s_uart_dev[uart_id].dmarx_buf = &s_uart2_dmarx_buf[0];% U6 A- T* q2 g$ a+ i. o
  14.   s_uart_dev[uart_id].dmarx_buf_size = sizeof(s_uart2_dmarx_buf);
    4 l' M+ X) a) W
  15.   s_uart_dev[uart_id].dmatx_buf = &s_uart2_dmatx_buf[0];
    + O3 o! x( Y) ~
  16.   s_uart_dev[uart_id].dmatx_buf_size = sizeof(s_uart2_dmatx_buf);
    - c7 N  `" x! Y/ P1 p1 A5 t& k
  17.   bsp_uart2_dmarx_config(s_uart_dev[uart_id].dmarx_buf,
    & H! v( V  K5 P3 D8 N6 q: F' \
  18.           sizeof(s_uart2_dmarx_buf));
    , J% }+ G/ b8 D* N; Z8 n
  19.   s_uart_dev[uart_id].status  = 0;' s" y4 Q$ s, N5 B  S% k% U8 [% Z
  20. }) N; I# r' a. q$ W2 \
  21. }* q0 z/ F8 n+ ?7 p. s6 p

  22. 1 N3 w9 K9 D: _4 Q5 V7 Z
  23. /* 串口发送函数 */" p3 o' ]  j0 m( Q0 q' n9 B
  24. uint16_t uart_write(uint8_t uart_id, const uint8_t *buf, uint16_t size)
    . E& s- r, y* A( n
  25. {
    4 W1 h- n4 r& f7 e
  26. return fifo_write(&s_uart_dev[uart_id].tx_fifo, buf, size);
    ) ?) ~$ F7 P( Q1 `9 S; T
  27. }
    . n7 p9 T+ c' s. C
  28. 8 x/ _6 O9 v: b4 ~6 [8 J' v
  29. /* 串口读取函数 */
    ' o2 r( g; L! v! ?9 R$ F& y
  30. uint16_t uart_read(uint8_t uart_id, uint8_t *buf, uint16_t size)$ P% h+ |; d4 S
  31. {* y+ m6 r( n/ C; P" u; g
  32. return fifo_read(&s_uart_dev[uart_id].rx_fifo, buf, size);2 h8 l/ |5 m2 X
  33. }
复制代码

# l* v5 o% s$ }2 J( ]6 b& L4 O& s3 Y
) H6 I1 w3 O+ f# s; y
# ^+ `, Y, Y5 s1 h0 @% j3 m
7 相关文章

依赖的fifo参考该文章:

【1】?通用环形缓冲区模块

8 完整源码
4 R9 D, Z# J8 ?0 P

串口&DMA底层配置:

  1. <div align="left"><font color="#000"><font size="3">#include <stddef.h></font></font></div><font size="3"><div align="left"><font color="#000">#include <stdint.h></font></div><font color="#000000"><div align="left">#include <stdbool.h></div><div align="left">#include "stm32f0xx.h"</div><div align="left">#include "bsp_uart.h"</div><div align="left">/**</div><div align="left">* @brief  </div><div align="left">* @param  </div><div align="left">* @retval</div><div align="left">*/</div><div align="left">static void bsp_uart1_gpio_init(void)</div><div align="left">{</div><div align="left">    GPIO_InitTypeDef    GPIO_InitStructure;</div><div align="left">#if 0</div><div align="left">RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOB, ENABLE);</div> 6 a1 m% {- q+ v1 @) j( K
  2. <div align="left">    GPIO_PinAFConfig(GPIOB, GPIO_PinSource6, GPIO_AF_0);</div><div align="left">    GPIO_PinAFConfig(GPIOB, GPIO_PinSource7, GPIO_AF_0); </div>
    $ u# N; ?# N/ D0 m- V
  3. , P% B: G8 }8 R! x0 L
  4. <div align="left">GPIO_InitStructure.GPIO_Pin  = GPIO_Pin_6 | GPIO_Pin_7;</div><div align="left">    GPIO_InitStructure.GPIO_Mode  = GPIO_Mode_AF;</div><div align="left">GPIO_InitStructure.GPIO_OType  = GPIO_OType_PP;</div><div align="left">    GPIO_InitStructure.GPIO_Speed   = GPIO_Speed_Level_3;</div><div align="left">    GPIO_InitStructure.GPIO_PuPd  = GPIO_PuPd_UP;</div><div align="left">    GPIO_Init(GPIOB, &GPIO_InitStructure);</div><div align="left">#else</div><div align="left">RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOA, ENABLE);</div>. O/ m, e; `* Q7 u7 e5 U
  5. <div align="left">    GPIO_PinAFConfig(GPIOB, GPIO_PinSource9, GPIO_AF_1);</div><div align="left">    GPIO_PinAFConfig(GPIOB, GPIO_PinSource10, GPIO_AF_1); </div>. V2 X' o6 G1 i" l* z) k7 t
  6. <div align="left">GPIO_InitStructure.GPIO_Pin  = GPIO_Pin_9 | GPIO_Pin_10;</div><div align="left">    GPIO_InitStructure.GPIO_Mode  = GPIO_Mode_AF;</div><div align="left">GPIO_InitStructure.GPIO_OType  = GPIO_OType_PP;</div><div align="left">    GPIO_InitStructure.GPIO_Speed   = GPIO_Speed_Level_3;</div><div align="left">    GPIO_InitStructure.GPIO_PuPd  = GPIO_PuPd_UP;</div><div align="left">    GPIO_Init(GPIOA, &GPIO_InitStructure);</div><div align="left">#endif</div><div align="left">}</div><div align="left">/**</div><div align="left">* @brief  </div><div align="left">* @param  </div><div align="left">* @retval</div><div align="left">*/</div><div align="left">static void bsp_uart2_gpio_init(void)</div><div align="left">{</div><div align="left">GPIO_InitTypeDef GPIO_InitStructure;</div><div align="left">
    7 m. ~5 K, \/ d
  7. </div><div align="left">RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOB, ENABLE);</div>
    & A* t4 y2 ^$ Y. B+ H3 ^6 O! G; V
  8. <div align="left">GPIO_PinAFConfig(GPIOA, GPIO_PinSource2, GPIO_AF_1);</div><div align="left">GPIO_PinAFConfig(GPIOA, GPIO_PinSource3, GPIO_AF_1);</div>) Z2 q7 W9 D% E  i$ j  I- W. G
  9. 8 l) Y7 m& @( f4 z0 a0 X5 V4 u- d# ^% V
  10. <div align="left">GPIO_InitStructure.GPIO_Pin   = GPIO_Pin_2 | GPIO_Pin_3;</div><div align="left">GPIO_InitStructure.GPIO_Mode  = GPIO_Mode_AF;</div><div align="left">GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;</div><div align="left">GPIO_InitStructure.GPIO_Speed = GPIO_Speed_10MHz;</div><div align="left">GPIO_InitStructure.GPIO_PuPd  = GPIO_PuPd_UP;</div><div align="left">GPIO_Init(GPIOA, &GPIO_InitStructure);</div><div align="left">}</div>
      ~) {8 W  k5 }& t
  11. <div align="left">/**</div><div align="left">* @brief  </div><div align="left">* @param  </div><div align="left">* @retval</div><div align="left">*/</div><div align="left">void bsp_uart1_init(void)</div><div align="left">{</div><div align="left">USART_InitTypeDef USART_InitStructure;</div><div align="left">NVIC_InitTypeDef NVIC_InitStructure;</div>/ H/ `4 s! z1 Y5 `7 @" h' s% z
  12. <div align="left">bsp_uart1_gpio_init();</div>0 P5 A5 M% l$ w" B) Q' l
  13. <div align="left">/* 使能串口和DMA时钟 */</div>; e6 Y' b/ W7 J3 g
  14. <div align="left">RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);</div><div align="left">RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);</div>
    : J  d* J5 J7 ]5 x2 n
  15. <div align="left">USART_InitStructure.USART_BaudRate            = 57600;</div><div align="left">USART_InitStructure.USART_WordLength          = USART_WordLength_8b;</div><div align="left">USART_InitStructure.USART_StopBits            = USART_StopBits_1;</div><div align="left">USART_InitStructure.USART_Parity              = USART_Parity_No;</div><div align="left">USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;</div><div align="left">USART_InitStructure.USART_Mode                = USART_Mode_Rx | USART_Mode_Tx;</div><div align="left">USART_Init(USART1, &USART_InitStructure);</div>( R7 _# N  m- |
  16. % m3 |9 a2 U! C! C. [2 ^
  17. <div align="left">USART_ITConfig(USART1, USART_IT_IDLE, ENABLE); /* 使能空闲中断 */</div><div align="left">USART_OverrunDetectionConfig(USART1, USART_OVRDetection_Disable);</div>
    9 p* O/ C) s. j8 j
  18. <div align="left">USART_Cmd(USART1, ENABLE);</div><div align="left">USART_DMACmd(USART1, USART_DMAReq_Rx|USART_DMAReq_Tx, ENABLE); /* 使能DMA收发 */</div>
    # e8 T  c1 M: o% K/ S
  19. <div align="left">/* 串口中断 */</div><div align="left">NVIC_InitStructure.NVIC_IRQChannel         = USART1_IRQn;</div><div align="left">NVIC_InitStructure.NVIC_IRQChannelPriority = 2;</div><div align="left">NVIC_InitStructure.NVIC_IRQChannelCmd      = ENABLE;</div><div align="left">NVIC_Init(&NVIC_InitStructure);</div>
    1 x: h/ o2 m# z7 w7 `9 K# }
  20. <div align="left">/* DMA中断 */</div>+ l7 `9 y8 ?  I
  21. <div align="left">   NVIC_InitStructure.NVIC_IRQChannel      = DMA1_Channel2_3_IRQn;       </div>( e" \6 N7 D6 s3 |+ G4 O' Z) j
  22. <div align="left">   NVIC_InitStructure.NVIC_IRQChannelPriority = 0; </div>2 V! h! j5 R* S5 R0 ?3 u% }( g8 t& P
  23. <div align="left">NVIC_InitStructure.NVIC_IRQChannelCmd      = ENABLE;</div>
    5 H" K# v: s! m4 i  L& k$ F8 G
  24. <div align="left">   NVIC_Init(&NVIC_InitStructure);</div>
    . }2 i, j8 n6 i$ E- {+ @
  25. <div align="left">}</div>% E- y6 G9 o' J
  26. " E( a+ R! @/ }" C. _
  27. <div align="left">/**</div>5 l2 l1 W, p' N* K( H* v
  28. <div align="left">* @brief  </div>
    / j" [' H8 |1 k; y, B  J  s2 ]
  29. <div align="left">* @param  </div>3 Q' l1 {" ^( f! [3 J% L2 f( p
  30. <div align="left">* @retval </div>
    $ L" t) M. _8 g' t- i
  31. <div align="left">*/</div>
    * v1 l' m# r* l* @' k7 c
  32. <div align="left">void bsp_uart2_init(void)</div>6 @4 m5 w6 M* y. p
  33. <div align="left">{</div>7 T$ j1 {+ U/ l3 J- d& Y3 @" P
  34. <div align="left">USART_InitTypeDef USART_InitStructure;</div>& Z  M2 q4 h1 _% y! v/ w* I
  35. <div align="left">NVIC_InitTypeDef NVIC_InitStructure;</div>
    5 @# w3 N$ X* o% a9 P  O

  36. 5 g: c3 p* g/ r' a
  37. <div align="left">bsp_uart2_gpio_init();</div>
    . N% s  o. Q# r

  38. - R, @4 B7 D3 F# R6 t1 k
  39. <div align="left">/* 使能串口和DMA时钟 */</div>9 @3 T% i0 c* s0 p6 P: Y3 ^
  40. <div align="left">RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);</div>% |# [" e0 e6 R) Q6 ~7 P/ G) J
  41. <div align="left">RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2, ENABLE);</div>
    $ L- z1 c& h9 r  s& F

  42. ' c/ J6 l( Y( P  C
  43. <div align="left">USART_InitStructure.USART_BaudRate            = 57600;</div>0 S) ~2 y3 s% x; h3 e- y5 r
  44. <div align="left">USART_InitStructure.USART_WordLength          = USART_WordLength_8b;</div>* t8 I$ G2 l  k( _5 ^
  45. <div align="left">USART_InitStructure.USART_StopBits            = USART_StopBits_1;</div>
    ( V, l$ f3 m2 G% `# E7 V1 C: g
  46. <div align="left">USART_InitStructure.USART_Parity              = USART_Parity_No;</div>
    ! `+ s5 g; K; R3 D4 S7 \. L* j! Q
  47. <div align="left">USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;</div>
    3 |+ X" w$ ~0 E
  48. <div align="left">USART_InitStructure.USART_Mode                = USART_Mode_Rx | USART_Mode_Tx;</div>2 a* t) a2 ~6 y" w& z
  49. <div align="left">USART_Init(USART2, &USART_InitStructure);</div>
    3 |  {( |, X# F
  50. 9 A! z  E: R4 S) q
  51. <div align="left">USART_ITConfig(USART2, USART_IT_IDLE, ENABLE); /* 使能空闲中断 */</div>
    / ~, i2 C, x. t. n
  52. <div align="left">USART_OverrunDetectionConfig(USART2, USART_OVRDetection_Disable);</div>2 S: W4 {$ W  z3 o: E6 P3 \
  53. / F" L. P+ W* z2 L
  54. <div align="left">USART_Cmd(USART2, ENABLE);</div>. Y8 W, l( a7 d3 M' `, C
  55. <div align="left">USART_DMACmd(USART2, USART_DMAReq_Rx|USART_DMAReq_Tx, ENABLE);  /* 使能DMA收发 */</div># O5 P2 J' }7 h9 V/ g% U- C; e/ J

  56. 8 P# A, t( l3 A% I
  57. <div align="left">/* 串口中断 */</div>
    1 w% L9 B7 I, o! p8 _
  58. <div align="left">NVIC_InitStructure.NVIC_IRQChannel         = USART2_IRQn;</div>" H8 ~$ \! U1 z" h' _- n
  59. <div align="left">NVIC_InitStructure.NVIC_IRQChannelPriority = 2;</div>
    5 h% j% j( y$ Y) R
  60. <div align="left">NVIC_InitStructure.NVIC_IRQChannelCmd      = ENABLE;</div>3 G8 Z2 _1 P9 F# i
  61. <div align="left">NVIC_Init(&NVIC_InitStructure);</div>
    - E0 f2 l3 L  C0 v: E6 M

  62. 9 F8 {$ |" U  H& j  T" j
  63. <div align="left">/* DMA中断 */</div>* e9 M  {* o2 ?; p. n
  64. <div align="left">NVIC_InitStructure.NVIC_IRQChannel         = DMA1_Channel4_5_IRQn;       </div>
    ) q5 b5 Y) |, Q( X" H5 M. {
  65. <div align="left">   NVIC_InitStructure.NVIC_IRQChannelPriority = 0; </div>* d, T" J7 D1 I6 o% n' O8 G
  66. <div align="left">NVIC_InitStructure.NVIC_IRQChannelCmd      = ENABLE;</div>
    / g; f" \* g2 a! X  U. t: @
  67. <div align="left">   NVIC_Init(&NVIC_InitStructure);</div>
    - m- N+ `4 d( R2 q1 q2 x
  68. <div align="left">}</div>8 o- h, _# J. K) B' j# \
  69. + i+ B* x# ^# M% \  U
  70. <div align="left">void bsp_uart1_dmatx_config(uint8_t *mem_addr, uint32_t mem_size)</div>4 ^' X9 ?+ ?  \5 i. `5 g4 R
  71. <div align="left">{</div>) h2 q7 r6 B+ \% w/ I3 N
  72. <div align="left">   DMA_InitTypeDef DMA_InitStructure;</div>2 H: z+ C9 e% ~7 {" w. c. p# _

  73. 0 h# o: s3 O/ I' s* Q, M
  74. <div align="left">DMA_DeInit(DMA1_Channel2);</div>  M* @  S! k. v5 S
  75. <div align="left">DMA_Cmd(DMA1_Channel2, DISABLE);</div>/ ^( j/ O/ n5 |# S  b  I8 D; |7 A
  76. <div align="left">DMA_InitStructure.DMA_PeripheralBaseAddr  = (<b>uint32_t</b>)&(USART1->TDR);</div>" R6 z  S4 m& H7 }5 d
  77. <div align="left">DMA_InitStructure.DMA_MemoryBaseAddr   = (<b>uint32_t</b>)mem_addr; </div>& x& J% j; F  H" R  H7 R
  78. <div align="left">DMA_InitStructure.DMA_DIR      = DMA_DIR_PeripheralDST;  /* 传输方向:内存->外设 */</div>
    0 k( t$ {0 W  x; g# F5 F- _
  79. <div align="left">DMA_InitStructure.DMA_BufferSize    = mem_size; </div>* |6 w; H% l, K4 J
  80. <div align="left">DMA_InitStructure.DMA_PeripheralInc   = DMA_PeripheralInc_Disable; </div>( |( y7 c& j# ^
  81. <div align="left">DMA_InitStructure.DMA_MemoryInc    = DMA_MemoryInc_Enable; </div># L: \7 w1 z* O$ s3 k% E+ I) _
  82. <div align="left">DMA_InitStructure.DMA_PeripheralDataSize  = DMA_PeripheralDataSize_Byte; </div>
    . Y- y* h; k5 s
  83. <div align="left">DMA_InitStructure.DMA_MemoryDataSize   = DMA_MemoryDataSize_Byte;</div>* T) [! ^5 e  s/ _3 W7 a4 E
  84. <div align="left">DMA_InitStructure.DMA_Mode      = DMA_Mode_Normal; </div>* ]' U- v5 f  Y$ W) B
  85. <div align="left">DMA_InitStructure.DMA_Priority     = DMA_Priority_High; </div>
    8 y1 k) ^* ?% L; n2 J
  86. <div align="left">DMA_InitStructure.DMA_M2M      = DMA_M2M_Disable; </div>7 N+ N2 z$ s5 Z4 u2 W& E
  87. <div align="left">DMA_Init(DMA1_Channel2, &DMA_InitStructure);  </div>" y6 _* \% h' ]- N
  88. <div align="left">DMA_ITConfig(DMA1_Channel2, DMA_IT_TC|DMA_IT_TE, ENABLE); </div>
    7 e, D! f7 l* _3 ]$ P5 |/ f3 t
  89. <div align="left">DMA_ClearFlag(DMA1_IT_TC2); /* 清除发送完成标识 */</div>( Q( `. S8 |+ L; Z
  90. <div align="left">DMA_Cmd(DMA1_Channel2, ENABLE); </div>( u. C, O0 q1 ?# g
  91. <div align="left">}</div>, }  @2 U/ v, h! t: {
  92. ; e) q; G4 D! T% h' S
  93. <div align="left">void bsp_uart1_dmarx_config(uint8_t *mem_addr, uint32_t mem_size)</div>
    8 v" j. a$ h% O* u+ a; q, Y; K
  94. <div align="left">{</div>3 c& M' U+ d/ R
  95. <div align="left">   DMA_InitTypeDef DMA_InitStructure;</div>
    0 I, m& k  `8 G
  96. 0 _) a! U5 j; F( q( f* g6 v" B
  97. <div align="left">DMA_DeInit(DMA1_Channel3); </div>' v+ }1 }5 E7 ?9 ?, \+ @9 J$ j# O
  98. <div align="left">DMA_Cmd(DMA1_Channel3, DISABLE);</div>
    8 b2 H# `5 r; }* L/ W
  99. <div align="left">DMA_InitStructure.DMA_PeripheralBaseAddr  = (<b>uint32_t</b>)&(USART1->RDR);</div>
    & P3 s2 p( ]# t! L8 K1 K' v2 {
  100. <div align="left">DMA_InitStructure.DMA_MemoryBaseAddr   = (<b>uint32_t</b>)mem_addr; </div>
    1 D; Z  g1 N; P8 h
  101. <div align="left">DMA_InitStructure.DMA_DIR      = DMA_DIR_PeripheralSRC;  /* 传输方向:外设->内存 */</div>2 N6 P' }5 R5 K. P+ E0 a
  102. <div align="left">DMA_InitStructure.DMA_BufferSize    = mem_size; </div>
    - c3 ]* x+ q6 w' N* Y' [
  103. <div align="left">DMA_InitStructure.DMA_PeripheralInc   = DMA_PeripheralInc_Disable; </div>9 M  C, r7 U1 C) M$ \' w
  104. <div align="left">DMA_InitStructure.DMA_MemoryInc    = DMA_MemoryInc_Enable; </div>( ^% V5 v- {+ n) w* y7 L
  105. <div align="left">DMA_InitStructure.DMA_PeripheralDataSize  = DMA_PeripheralDataSize_Byte; </div>9 G0 [7 E! n% v% J! L
  106. <div align="left">DMA_InitStructure.DMA_MemoryDataSize   = DMA_MemoryDataSize_Byte;</div>
    0 I, @5 `. _, a. R' A  \" ?
  107. <div align="left">DMA_InitStructure.DMA_Mode      = DMA_Mode_Circular; </div>2 @  O# `* L2 P
  108. <div align="left">DMA_InitStructure.DMA_Priority     = DMA_Priority_VeryHigh; </div>
    " I* O2 ~! M, m2 E  V
  109. <div align="left">DMA_InitStructure.DMA_M2M      = DMA_M2M_Disable; </div>) V* Y! q" d9 S, P
  110. <div align="left">DMA_Init(DMA1_Channel3, &DMA_InitStructure); </div>
    , M' \( j0 |2 h2 B6 x. u1 C
  111. <div align="left">DMA_ITConfig(DMA1_Channel3, DMA_IT_TC|DMA_IT_HT|DMA_IT_TE, ENABLE);/* 使能DMA半满、全满、错误中断 */</div>% a* g# [0 X6 K6 w. Q
  112. <div align="left">DMA_ClearFlag(DMA1_IT_TC3);</div>' i  j/ y/ K( p8 D+ e. ?. U
  113. <div align="left">DMA_ClearFlag(DMA1_IT_HT3);</div>
    2 K5 O9 b% j8 r) B+ Y
  114. <div align="left">DMA_Cmd(DMA1_Channel3, ENABLE); </div>
    4 r/ X- b: j: I+ @5 e
  115. <div align="left">}</div>
    , a3 C* D6 X  ^! s- h( n( J* N+ E

  116. ; k. [/ |  m2 S- _) e0 b  ]
  117. <div align="left">uint16_t bsp_uart1_get_dmarx_buf_remain_size(void)</div>
    1 U1 X1 Z8 u$ L  j4 T7 o( v
  118. <div align="left">{</div>
    * D  d0 U  O5 ^/ x% _8 N8 l1 b
  119. <b><div align="left"><b>return</b> DMA_GetCurrDataCounter(DMA1_Channel3); /* 获取DMA接收buf剩余空间 */</div>
    # f- S1 C& f/ E0 R# a
  120. </b><div align="left">}</div>  U' Y% w5 I# M) X4 u" N$ O
  121. 6 i, X' d. K& Z
  122. <div align="left">void bsp_uart2_dmatx_config(uint8_t *mem_addr, uint32_t mem_size)</div>
    & z0 c( ?4 r% i
  123. <div align="left">{</div>
    6 u+ l& K4 |% p' ?  D+ V
  124. <div align="left">   DMA_InitTypeDef DMA_InitStructure;</div>5 N( A& J" s, b/ v' ]. [/ ?; H0 x% M

  125. ; l* E% x" Q0 P, T' y% V$ q
  126. <div align="left">DMA_DeInit(DMA1_Channel4);</div>
    & s% \8 a' h: Y, O9 F9 _
  127. <div align="left">DMA_Cmd(DMA1_Channel4, DISABLE);</div>
    0 d' G  V/ h* n- T$ h
  128. <div align="left">DMA_InitStructure.DMA_PeripheralBaseAddr  = (<b>uint32_t</b>)&(USART2->TDR);</div>' a+ g) I+ T% X, G
  129. <div align="left">DMA_InitStructure.DMA_MemoryBaseAddr   = (<b>uint32_t</b>)mem_addr; </div>
      {+ b2 H5 Y: i3 y+ ^* @9 H$ L$ |
  130. <div align="left">DMA_InitStructure.DMA_DIR      = DMA_DIR_PeripheralDST;  /* 传输方向:内存->外设 */</div>$ @* R# l1 u7 U9 {+ E( z- `  p
  131. <div align="left">DMA_InitStructure.DMA_BufferSize    = mem_size; </div>. a$ n& h7 `* X4 n; T% P) {3 i
  132. <div align="left">DMA_InitStructure.DMA_PeripheralInc   = DMA_PeripheralInc_Disable; </div>/ U. l# B5 M# Z" U
  133. <div align="left">DMA_InitStructure.DMA_MemoryInc    = DMA_MemoryInc_Enable; </div>+ ^' ?1 `% A" F2 Y5 {; k
  134. <div align="left">DMA_InitStructure.DMA_PeripheralDataSize  = DMA_PeripheralDataSize_Byte; </div>
    8 o- ]; A* q1 {8 @" ^3 h$ B
  135. <div align="left">DMA_InitStructure.DMA_MemoryDataSize   = DMA_MemoryDataSize_Byte;</div>
    7 |6 [1 r+ H3 \' H4 ^- o6 p8 y* o
  136. <div align="left">DMA_InitStructure.DMA_Mode      = DMA_Mode_Normal; </div>0 ]$ h5 U% y' D  C- ]
  137. <div align="left">DMA_InitStructure.DMA_Priority     = DMA_Priority_High; </div>
    & C) o" a& q8 _* W& m1 i) V2 v' v. t
  138. <div align="left">DMA_InitStructure.DMA_M2M      = DMA_M2M_Disable; </div>% F% ^! B6 l* U+ z3 o( _" r
  139. <div align="left">DMA_Init(DMA1_Channel4, &DMA_InitStructure);  </div>
    8 _  k9 y5 |0 K) y# x
  140. <div align="left">DMA_ITConfig(DMA1_Channel4, DMA_IT_TC|DMA_IT_TE, ENABLE); </div>
    / u7 }; y- \% f$ U# P) a
  141. <div align="left">DMA_ClearFlag(DMA1_IT_TC4); /* 清除发送完成标识 */</div>/ E1 p# f* T, C% S+ b; `9 @
  142. <div align="left">DMA_Cmd(DMA1_Channel4, ENABLE); </div>
    , d( \0 ^& ~, Q8 G( [, w
  143. <div align="left">}</div>
    ; D! g# M7 X9 u
  144. 5 K+ T0 e5 B! j+ p  O$ C# J
  145. <div align="left">void bsp_uart2_dmarx_config(uint8_t *mem_addr, uint32_t mem_size)</div>3 R; S7 N1 R& w( _' _
  146. <div align="left">{</div>, K. e/ T1 a6 J2 l, W* N" d
  147. <div align="left">   DMA_InitTypeDef DMA_InitStructure;</div>. N8 @. s2 }* F, J7 E

  148. : s& t  l6 a1 e9 j. J
  149. <div align="left">DMA_DeInit(DMA1_Channel5); </div>
    , n3 z1 v0 y* |; k% ]
  150. <div align="left">DMA_Cmd(DMA1_Channel5, DISABLE);</div>
    7 Y( H0 s4 W( A4 A
  151. <div align="left">DMA_InitStructure.DMA_PeripheralBaseAddr  = (<b>uint32_t</b>)&(USART2->RDR);</div>& G. p) A! \/ w0 D, G1 S. \
  152. <div align="left">DMA_InitStructure.DMA_MemoryBaseAddr   = (<b>uint32_t</b>)mem_addr; </div>
      |/ I3 f3 W& n+ y; ]* t0 B5 g
  153. <div align="left">DMA_InitStructure.DMA_DIR      = DMA_DIR_PeripheralSRC;  /* 传输方向:外设->内存 */</div># L: g9 {- `8 \
  154. <div align="left">DMA_InitStructure.DMA_BufferSize    = mem_size; </div>
    & r6 G3 p: N! v5 n
  155. <div align="left">DMA_InitStructure.DMA_PeripheralInc   = DMA_PeripheralInc_Disable; </div>- @& C  U; ^) M. X. M" [6 T. z
  156. <div align="left">DMA_InitStructure.DMA_MemoryInc    = DMA_MemoryInc_Enable; </div>
    0 I2 M& C' v& V+ g+ X. K
  157. <div align="left">DMA_InitStructure.DMA_PeripheralDataSize  = DMA_PeripheralDataSize_Byte; </div>/ `; G, P& A$ j. O
  158. <div align="left">DMA_InitStructure.DMA_MemoryDataSize   = DMA_MemoryDataSize_Byte;</div>
    & a" M/ P: A$ G) K9 C- z. I; k
  159. <div align="left">DMA_InitStructure.DMA_Mode      = DMA_Mode_Circular; </div>5 a! j3 K: _" A
  160. <div align="left">DMA_InitStructure.DMA_Priority     = DMA_Priority_VeryHigh; </div>- K& ^* f) P0 T0 D% p! p0 j& F1 W5 ]
  161. <div align="left">DMA_InitStructure.DMA_M2M      = DMA_M2M_Disable; </div>
    ' y% w" c& W( e5 ]( a8 g1 c
  162. <div align="left">DMA_Init(DMA1_Channel5, &DMA_InitStructure); </div>
    8 h9 [4 u! J/ G& i6 K
  163. <div align="left">DMA_ITConfig(DMA1_Channel5, DMA_IT_TC|DMA_IT_HT|DMA_IT_TE, ENABLE);/* 使能DMA半满、全满、错误中断 */</div>7 ]. K$ G6 a9 w$ J
  164. <div align="left">DMA_ClearFlag(DMA1_IT_TC5);</div>8 {# G% \% \4 M  D( v: a  n
  165. <div align="left">DMA_ClearFlag(DMA1_IT_HT5);</div>% Y6 a; D; v. V3 k3 S8 w# o* H
  166. <div align="left">DMA_Cmd(DMA1_Channel5, ENABLE); </div>( W& v2 R4 o# @2 A
  167. <div align="left">}</div>$ R5 H# L3 `5 ^" O/ |: R

  168. + {8 U8 ?2 k6 Q0 z
  169. <div align="left">uint16_t bsp_uart2_get_dmarx_buf_remain_size(void)</div>3 \3 J/ k* i" K' g( m  M& @! [
  170. <div align="left">{</div>, ]/ V" v6 C. a
  171. <b><div align="left"><b>return</b> DMA_GetCurrDataCounter(DMA1_Channel5); /* 获取DMA接收buf剩余空间 */</div>0 R8 w& x! [0 j$ I4 c
  172. </b><div align="left">}</div></font></font>
复制代码
5 m: ~3 K) d5 w

压力测试:

  • 1.5Mbps波特率,串口助手每毫秒发送1k字节数据,stm32f0 DMA接收数据,再通过DMA发送回串口助手,毫无压力。
  • 1.5Mbps波特率,可传输大文件测试,将接收数据保存为文件,与源文件比较。
  • 串口高波特率测试需要USB转TLL工具及串口助手都支持才可行,推荐CP2102、FT232芯片的USB转TTL工具。
    & x/ i  c; _3 e/ ]2 k* J

    / Q& X( N! w* s$ ^

    , c) o+ O5 X, |  P
LU3Y[[@P3WWX551XNDGQY5X.png

4 j' g, ^  _1 a

2 E3 A0 \9 b$ u* {6 S
收藏 评论0 发布时间:2022-1-5 21:11

举报

0个回答

所属标签

相似分享

官网相关资源

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