1 前言. o9 N ?# {1 b& D8 M
M+ _- T- n3 q5 H$ v 直接存储器访问(Direct Memory Access),简称DMA。DMA是CPU一个用于数据从一个地址空间到另一地址空间“搬运”(拷贝)的组件,数据拷贝过程不需CPU干预,数据拷贝结束则通知CPU处理。因此,大量数据拷贝时,使用DMA可以释放CPU资源。DMA数据拷贝过程,典型的有: - 内存—>内存,内存间拷贝
- 外设—>内存,如uart、spi、i2c等总线接收数据过程
- 内存—>外设,如uart、spi、i2c等总线发送数据过程. h# w+ [) s. J; U
5 b7 g$ B/ [' m2 |/ r8 P. z' E
1 C- {- \( Q6 Q+ l. A( ]- j2 串口有必要使用DMA吗* o, ?2 j. N! b, w
8 ~) K+ V' G! _. D
{2 E y( O3 d0 I9 F% v X( Q8 r 串口(uart)是一种低速的串行异步通信,适用于低速通信场景,通常使用的波特率小于或等于115200bps。对于小于或者等于115200bps波特率的,而且数据量不大的通信场景,一般没必要使用DMA,或者说使用DMA并未能充分发挥出DMA的作用。 0 A B/ p6 V+ X
对于数量大,或者波特率提高时,必须使用DMA以释放CPU资源,因为高波特率可能带来这样的问题: - 对于发送,使用循环发送,可能阻塞线程,需要消耗大量CPU资源“搬运”数据,浪费CPU
- 对于发送,使用中断发送,不会阻塞线程,但需浪费大量中断资源,CPU频繁响应中断;以115200bps波特率,1s传输11520字节,大约69us需响应一次中断,如波特率再提高,将消耗更多CPU资源
- 对于接收,如仍采用传统的中断模式接收,同样会因为频繁中断导致消耗大量CPU资源
9 w n4 q; y) G& x, f# k
& p( o+ _% g: r
( q1 g; B/ \6 p; V g 因此,高波特率场景下,串口非常有必要使用DMA。 7 u* B; f* S9 p! U$ J1 ^& K. G7 U# o. F
3 实现方式) p* \8 _; g0 I* G1 C a
/ R1 \) T) m2 C8 @4 |
R% R/ t1 [5 Q. e$ {4 F6 R" n4 [" k" A
/ [* V: R3 q2 B p" C4 STM32串口使用DMA! e0 F$ K5 e( z* ^; e$ K( Z, H
- ~! m. B% {/ n' w$ v3 N8 ~
( m7 C; Z+ C: g; S/ d 关于STM32串口使用DMA,不乏一些开发板例程及网络上一些博主的使用教程。使用步骤、流程、配置基本大同小异,正确性也没什么毛病,但都是一些基本的Demo例子,作为学习过程没问题;实际项目使用缺乏严谨性,数据量大时可能导致数据异常。 * I2 c5 U! B h0 _; N/ [& u& A
测试平台: - STM32F030C8T6
- UART1/UART2
- DMA1 Channel2—Channel5
- ST标准库
- 主频48MHz(外部12MHz晶振)
1 v3 D/ t' o5 [9 h6 _4 U " M2 ^$ e7 b( J4 ~! h3 b
+ v. F$ r% ^7 C
B( j+ u0 [/ q8 q3 L* t' t" p8 z- N9 M8 S9 e4 }, k3 B
5 串口DMA接收
* f3 Z; u% V' g( U
; b2 B: f# ?" ^, ]! j5.1 基本流程% G# l/ R& ^' r$ W) \; T
, i/ ^; N; s. u5 F* ~( l l
串口接收流程图 5 W- n- @% ~* d* o
: h# l8 q) O" C3 k# s" J. {; n6 g+ t
5.2 相关配置
3 y' F0 B4 s% Z$ ?! M& g* a1 Z
8 X$ k* y- k5 A2 i7 F关键步骤 【1】初始化串口 【2】使能串口DMA接收模式,使能串口空闲中断 【3】配置DMA参数,使能DMA通道buf半满(传输一半数据)中断、buf溢满(传输数据完成)中断
! Y7 X' v. V; x; V/ X为什么需要使用DMA 通道buf半满中断? , D" D* q* \* {9 D
很多串口DMA模式接收的教程、例子,基本是使用了“空间中断”+“DMA传输完成中断”来接收数据。实质上这是存在风险的,当DMA传输数据完成,CPU介入开始拷贝DMA通道buf数据,如果此时串口继续有数据进来,DMA继续搬运数据到buf,就有可能将数据覆盖,因为DMA数据搬运是不受CPU控制的,即使你关闭了CPU中断。 % E" I3 Q, T* L* B' M
严谨的做法需要做双buf,CPU和DMA各自一块内存交替访问,即是"乒乓缓存” ,处理流程步骤应该是这样:
& r% W7 ?4 T! b; b
【1】第一步,DMA先将数据搬运到buf1,搬运完成通知CPU来拷贝buf1数据# Z) s+ z" u% B+ z+ K' w/ U
【2】第二步,DMA将数据搬运到buf2,与CPU拷贝buf1数据不会冲突5 _5 S! G. D2 \/ `
【3】第三步,buf2数据搬运完成,通知CPU来拷贝buf2数据
' t6 E5 }8 a4 m/ N9 p" x) X" d. l【4】执行完第三步,DMA返回执行第一步,一直循环
8 ]9 m# b' ?$ _; u2 [/ X
. x- Y! U- h1 }# p
( D0 S2 C& J) I* C! H# j/ o1 b* K
双缓存DMA数据搬运过程
, S& W( f6 t% E/ y0 g3 r
/ M. m0 M4 @) G6 _7 S# b3 G1 ? STM32F0系列DMA不支持双缓存(以具体型号为准)机制,但提供了一个buf"半满中断",即是数据搬运到buf大小的一半时,可以产生一个中断信号。基于这个机制,我们可以实现双缓存功能,只需将buf空间开辟大一点即可。
: n$ i3 J8 C4 z* S" ^' h0 {【1】第一步,DMA将数据搬运完成buf的前一半时,产生“半满中断”,CPU来拷贝buf前半部分数据" q; G c1 Y& Y+ z
【2】第二步,DMA继续将数据搬运到buf的后半部分,与CPU拷贝buf前半部数据不会冲突# \# i% @9 {6 n0 c1 o$ G) z9 o/ J
【3】第三步,buf后半部分数据搬运完成,触发“溢满中断”,CPU来拷贝buf后半部分数据9 y8 l7 V6 s5 ^1 h2 P0 `
【4】执行完第三步,DMA返回执行第一步,一直循环 * y( U# L( \/ Q: @
9 B$ _+ I. u9 j+ w9 l* q: l7 j
0 G- G1 d- a& t- k4 q
; @# s6 y& n/ C$ n( ?5 v0 J, q( T$ x9 A4 z7 V6 [! Q" j
使用半满中断DMA数据搬运过程 + L5 f2 o- s5 ^
1 R* P/ f+ S! g4 E
UART2 DMA模式接收配置代码如下,与其他外设使用DMA的配置基本一致,留意关键配置: - 串口接收,DMA通道工作模式设为连续模式
- 使能DMA通道接收buf半满中断、溢满(传输完成)中断
- 启动DMA通道前清空相关状态标识,防止首次传输错乱数据+ L/ W0 N# R8 j! M- u1 s* _
- [- G! {2 e3 s. r7 `( D# }
6 ~) g+ u7 m% `) a
, ^( Y" M; R1 p. J) ^" Q% }/ J' k' p
6 p; @2 A* K w4 Y- Q7 W6 Q# o- void bsp_uart2_dmarx_config(uint8_t *mem_addr, uint32_t mem_size)7 `+ s6 T+ K& Z5 i+ y) `4 O, b3 g* O; z
- {
: W, }# R, V8 I" A& G2 c - DMA_InitTypeDef DMA_InitStructure;. B. P4 O( u% ~8 U6 R
- ; Y3 Y. r& z: w4 z: E7 F
- DMA_DeInit(DMA1_Channel5);
; V( } ]& R1 E8 b - DMA_Cmd(DMA1_Channel5, DISABLE);$ I: D* _+ P: Q2 l0 J* `: y
- DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&(USART2->RDR);/* UART2接收数据地址 */4 |3 I6 I a8 G5 {
- DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)mem_addr; /* 接收buf */8 p* f3 e; e; p7 ?8 w! z
- DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC; /* 传输方向:外设->内存 */; y9 p7 v( [: E9 s2 J) x
- DMA_InitStructure.DMA_BufferSize = mem_size; /* 接收buf大小 */7 g- @3 h" e) e% D7 v0 l
- DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable; 3 _0 d5 S% V6 X8 R
- DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable; " ^( R: d# J) i0 q
- DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;
8 C3 h4 r$ R4 D! r* a' A - DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;
( o$ G2 c+ p6 l$ t) _# t { - DMA_InitStructure.DMA_Mode = DMA_Mode_Circular; /* 连续模式 */
* r7 J, ?$ s! v6 G8 k# h6 W0 R" n - DMA_InitStructure.DMA_Priority = DMA_Priority_VeryHigh;
) Z, U. y: X) Q3 v- ^) r& N - DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;
: s6 X& [+ I4 n4 _8 D - DMA_Init(DMA1_Channel5, &DMA_InitStructure); $ V6 L' Y& ^8 p0 ]0 \" a. ?0 r0 o
- DMA_ITConfig(DMA1_Channel5, DMA_IT_TC|DMA_IT_HT|DMA_IT_TE, ENABLE);/* 使能DMA半满、溢满、错误中断 */
4 r/ H( h3 }+ h9 I! ?: q2 v/ H - DMA_ClearFlag(DMA1_IT_TC5); /* 清除相关状态标识 */
, ^+ Y0 s8 {# { - DMA_ClearFlag(DMA1_IT_HT5);
- k3 y0 f* O4 s- S - DMA_Cmd(DMA1_Channel5, ENABLE);
3 k. @) a3 K( ?# K+ ~9 _ - }
复制代码 1 G: R* n- G4 o/ h8 P4 [
3 c& `5 O: s/ |! o2 A! U/ E5 p
DMA 错误中断“DMA_IT_TE”,一般用于前期调试使用,用于检查DMA出现错误的次数,发布软件可以不使能该中断。 0 u0 E9 e# n; {# W5 c5 e
5.3 接收处理
& S5 W6 e; }* D. U# u; { 基于上述描述机制,DMA方式接收串口数据,有三种中断场景需要CPU去将buf数据拷贝到fifo中,分别是: - DMA通道buf溢满(传输完成)场景
- DMA通道buf半满场景
- 串口空闲中断场景5 b, x) n+ I4 Z6 O' |2 d- d+ `
$ x# Z& o' [+ o# M6 J) ?. R: X) ], B( E
前两者场景,前面文章已经描述。串口空闲中断指的是,数据传输完成后,串口监测到一段时间内没有数据进来,则触发产生的中断信号。
& o8 c2 x1 ~! r! b 5.3 .1 接收数据大小/ [' [( u, [& D
数据传输过程是随机的,数据大小也是不定的,存在几类情况: - 数据刚好是DMA接收buf的整数倍,这是理想的状态
- 数据量小于DMA接收buf或者小于接收buf的一半,此时会触发串口空闲中断 r. I, _) B: g7 `, y4 r; D
9 i' y7 b0 u- w; a4 l, S+ C
因此,我们需根据“DMA通道buf大小”、“DMA通道buf剩余空间大小”、“上一次接收的总数据大小”来计算当前接收的数据大小。 - /* 获取DMA通道接收buf剩余空间大小 */, y+ i- A. g( {3 p! ?$ o* t# m
- uint16_t DMA_GetCurrDataCounter(DMA_Channel_TypeDef* DMAy_Channelx);
复制代码 % J2 [# _! f+ E
DMA通道buf溢满场景计算
1 n' A, M, t' f* r* c
- 接收数据大小 = DMA通道buf大小 - 上一次接收的总数据大小
复制代码
( e# A! H! e0 u: i! t3 P- x1 z3 d6 X. [4 l% E6 p0 I
DMA通道buf溢满中断处理函数: - void uart_dmarx_done_isr(uint8_t uart_id)2 ^6 ]; r5 T. a( F/ ]- W7 S
- {
7 Q1 \! G7 |6 n! T - uint16_t recv_size;
( @& J! } ^) G6 M0 X2 ~ - 9 Y9 Y. c+ B5 ]" W
- recv_size = s_uart_dev[uart_id].dmarx_buf_size - s_uart_dev[uart_id].last_dmarx_size;
1 D$ a) W. H) T6 Q2 C4 U( F - - F) r* {% w* k1 F1 A# E) P9 D3 f4 n
- fifo_write(&s_uart_dev[uart_id].rx_fifo, : N% s: i1 J% C/ h4 u" B
- (const uint8_t *)&(s_uart_dev[uart_id].dmarx_buf[s_uart_dev[uart_id].last_dmarx_size]), recv_size);# ^; R3 \( k) w
- % ^3 s. k6 P- @ }
- s_uart_dev[uart_id].last_dmarx_size = 0;
! D* _+ }% D, c b5 s ?/ { - }
复制代码 # M2 _/ j1 i; M2 m- {/ h
DMA通道buf半满场景计算 ! t: N+ V5 j4 w
- 接收数据大小 = DMA通道接收总数据大小 - 上一次接收的总数据大小, v B! ~4 }; i- o
- DMA通道接收总数据大小 = DMA通道buf大小 - DMA通道buf剩余空间大小
复制代码
( ^# Y1 w% B0 V+ \, V/ pDMA通道buf半满中断处理函数:
$ w7 Y4 }& | H% X5 K
- void uart_dmarx_half_done_isr(uint8_t uart_id)1 b$ B8 d, ~8 y8 r+ X K- a
- {
! A( i" {; R; } - uint16_t recv_total_size;
! v! u. f1 ^6 h0 Q2 X - uint16_t recv_size;
+ z. c# I2 T- {6 a) a- C6 ~, l# [ - 3 J) m7 J* C8 J0 D6 k7 M' s
- if(uart_id == 0)/ V7 s4 G, Q& ?% N3 c$ p6 x
- {
6 W/ U" C9 w) `; `' A+ r - recv_total_size = s_uart_dev[uart_id].dmarx_buf_size - bsp_uart1_get_dmarx_buf_remain_size();
0 U/ m3 h3 x+ b( r - }7 N% B* `1 }, r8 `
- else if (uart_id == 1)0 O6 c( k# x* i" h
- {+ R/ t/ y) e) T5 N5 y) n) e( D' x
- recv_total_size = s_uart_dev[uart_id].dmarx_buf_size - bsp_uart2_get_dmarx_buf_remain_size();* b( v( z0 m; P$ ?+ U% y
- }) _0 a, I* y" Y9 C$ b h y
- recv_size = recv_total_size - s_uart_dev[uart_id].last_dmarx_size;
! y N6 x8 L( K# O/ c H -
; F$ g; U) K# g* f7 w - fifo_write(&s_uart_dev[uart_id].rx_fifo, 8 H/ z9 |5 k* B1 P
- (const uint8_t *)&(s_uart_dev[uart_id].dmarx_buf[s_uart_dev[uart_id].last_dmarx_size]), recv_size);
2 y3 n3 i; f' E - s_uart_dev[uart_id].last_dmarx_size = recv_total_size;/* 记录接收总数据大小 */
) d- r6 i& O( y: z - }
复制代码 4 v5 ?) i p+ c% R
+ Q/ n, G' `, K5 v串口空闲中断场景计算
- |/ ~- x5 _3 h9 Q* I
串口空闲中断场景的接收数据计算与“DMA通道buf半满场景”计算方式是一样的。 串口空闲中断处理函数: 注:
! |/ t) v4 e9 J# l* g3 {9 E串口空闲中断处理函数,除了将数据拷贝到串口接收fifo中,还可以增加特殊处理,如作为串口数据传输完成标识、不定长度数据处理等等。 $ D7 x# c& z. _. J
5.3.2 接收数据偏移地址1 a! i5 H5 M6 w* `/ [6 l) v
将有效数据拷贝到fifo中,除了需知道有效数据大小外,还需知道数据存储于DMA 接收buf的偏移地址。有效数据偏移地址只需记录上一次接收的总大小即,可,在DMA通道buf全满中断处理函数将该值清零,因为下一次数据将从buf的开头存储。
9 A/ K: n) Y( d9 t5 |在DMA通道buf溢满中断处理函数中将数据偏移地址清零:
9 ?- Y$ N9 G6 q( c5 f
- void uart_dmarx_done_isr(uint8_t uart_id)
7 B8 e" O+ g1 F# r; S - {7 h2 G8 c! ?/ r; A' g. E! I
- /* todo *// E6 g8 z& r# b/ s0 r8 z2 ~9 g; |
- s_uart_dev[uart_id].last_dmarx_size = 0;
. d6 H- J9 Q- N V( n) A( }2 V - }
复制代码
) k# c3 |, b" c5 z3 o" a
; O+ E Q! r$ I }6 ~# w! Y5.4 应用读取串口数据方法
5 Z3 m: E# t& o" _2 u 经过前面的处理步骤,已将串口数据拷贝至接收fifo,应用程序任务只需从fifo获取数据进行处理。前提是,处理效率必须大于DAM接收搬运数据的效率,否则导致数据丢失或者被覆盖处理。
5 n7 m* S# _' d: s$ g6 串口DMA发送; H2 e+ J; H# z# L( p( Q
5 l5 i: s2 X6 J5.1 基本流程
3 N, K# L: o7 _5 J! Q" x/ u
5 q! ?6 |0 J4 Q+ z8 B3 w- u
* {* c: [3 k+ ?. _# s' y- Z串口发送流程图 % X) X) X3 u$ _. r" L9 Q5 i
5 b6 y3 P9 K0 i# ?9 q w/ ^
* B1 {5 S/ A T; X2 F' s3 I n2 j( ^
5.2 相关配置# Y$ V+ K- O, B, i5 s
关键步骤
9 C, [; |* }. g8 M1 `
【1】初始化串口 4 {- F' u" Q* }/ Q
【2】使能串口DMA发送模式 6 N0 b9 z0 O9 ^1 o J& y+ ?
【3】配置DMA发送通道,这一步无需在初始化设置,有数据需要发送时才配置使能DMA发送通道 / @* o+ y4 w# h( W
UART2 DMA模式发送配置代码如下,与其他外设使用DMA的配置基本一致,留意关键配置:
$ X b- G' v1 r' f6 m
- 串口发送是,DMA通道工作模式设为单次模式(正常模式),每次需要发送数据时重新配置DMA
- 使能DMA通道传输完成中断,利用该中断信息处理一些必要的任务,如清空发送状态、启动下一次传输
- 启动DMA通道前清空相关状态标识,防止首次传输错乱数据
j9 h9 m* n2 A) ^$ P% L7 e0 F$ v7 x& U3 ^! M/ M
% _1 E7 R, V" |. r" t0 P% d. X! H/ M: s( W6 u1 {2 O2 n- o# q1 `
7 x2 L) |0 I$ }% g# ?' ?
5.3 发送处理 串口待发送数据存于发送fifo中,发送处理函数需要做的的任务就是循环查询发送fifo是否存在数据,如存在则将该数据拷贝到DMA发送buf中,然后启动DMA传输。前提是需要等待上一次DMA传输完毕,即是DMA接收到DMA传输完成中断信号"DMA_IT_TC"。 串口发送处理函数: " d7 D2 K& ^. ^- L
- void uart_poll_dma_tx(uint8_t uart_id)
$ C/ m1 c( \$ }3 x - {' ~) S5 y$ r" r7 A5 r
- uint16_t size = 0;" W% a3 ]3 ]& C* v6 \) b
-
. L5 E. y6 ]6 v - if (0x01 == s_uart_dev[uart_id].status)
7 X5 Z1 J6 C1 p1 Q& p4 a, T3 ] - {3 I0 K' Z, i j8 t9 l0 ?
- return;! r9 l* P& P4 a0 }( Z
- }& H. v% {9 p/ c0 R
- size = fifo_read(&s_uart_dev[uart_id].tx_fifo, s_uart_dev[uart_id].dmatx_buf,0 U( d9 f9 q8 q6 @ M
- s_uart_dev[uart_id].dmatx_buf_size);% m8 k i/ U: P( m0 i' Z
- if (size != 0): d6 n+ a' p" G# u: x8 m
- {
, O& l- ~9 O% D0 [" L - s_UartTxRxCount[uart_id*2+0] += size;1 m) u* n5 b8 L7 t9 d
- if (uart_id == 0)
: b0 R& t% ?- U8 b, |4 i( E, U1 ^* w% v - {
( U; L: y/ m3 |7 G9 y8 e - s_uart_dev[uart_id].status = 0x01; /* DMA发送状态 */
7 q) M+ n) |$ d; I - bsp_uart1_dmatx_config(s_uart_dev[uart_id].dmatx_buf, size);1 x8 }. p/ r2 c' \; ~. h L9 z* L$ {
- }* C) e$ i0 U7 r1 R9 k% h
- else if (uart_id == 1)
. \' E$ Z* e9 ?% e - {
5 z H/ e- b8 R8 k - s_uart_dev[uart_id].status = 0x01; /* DMA发送状态,必须在使能DMA传输前置位,否则有可能DMA已经传输并进入中断 */
' Y0 q/ V5 l% u( d* ^$ c - bsp_uart2_dmatx_config(s_uart_dev[uart_id].dmatx_buf, size);4 P+ a6 ~- I$ _6 n Q! r
- }
8 f; s! {- y5 D' j - }) e1 W* |9 {- k" ?' p+ a+ S
- }
复制代码
3 |+ o- C9 r2 ^" i" q" Z6 V注意发送状态标识,必须先置为“发送状态”,然后启动DMA 传输。如果步骤反过来,在传输数据量少时,DMA传输时间短,“DMA_IT_TC”中断可能比“发送状态标识置位”先执行,导致程序误判DMA一直处理发送状态(发送标识无法被清除) 1 k+ n% ]7 E) Z3 J' n
$ E. J. Q& Z- n3 w& k1 U; E
注:
; ]; d# J8 k1 T, j, a; M关于DMA发送数据启动函数,有些博客文章描述只需改变DMA发送buf的大小即可;经过测试发现,该方法在发送数据量较小时可行,数据量大后,导致发送失败,而且不会触发DMA发送完成中断。因此,可靠办法是:每次启动DMA发送,重新配置DMA通道所有参数。该步骤只是配置寄存器过程,实质上不会占用很多CPU执行时间。 6 X" I0 B0 c0 V6 U1 r
DMA传输完成中断处理函数: - void uart_dmatx_done_isr(uint8_t uart_id)" a0 q }7 ?/ @' x# W" o, H
- {" O x7 g" _8 Q+ r- I7 X3 [
- s_uart_dev[uart_id].status = 0; /* 清空DMA发送状态标识 */
6 ?* I/ l% B3 C4 ?5 a: u5 d; Y# p# k: V - }
复制代码 8 \6 Y; P( H- g: o' {, E5 R. O6 n
+ k3 U( o2 F9 I. D1 ~. W! y
/ u, [: e$ x4 q U- k0 w' @4 h 上述串口发送处理函数可以在几种情况调用:
/ M! r* y- m: S5 {; B( O+ V
$ L* B( E4 W% a
- 主线程任务调用,前提是线程不能被其他任务阻塞,否则导致fifo溢出5 z" y0 T3 n1 ]: k
: q. B: c5 L# }6 P# o& b- b
- void thread(void)
: S; Y# I3 P) R W! F - {
* q* K6 \7 F8 v1 _ - uart_dmatx_done_isr(DEV_UART1);/ \. f) k3 L* q( u) e( p
- uart_dmatx_done_isr(DEV_UART2);; K6 c3 Z: F$ x! U9 q9 z j
- }
复制代码 + p Z0 O, P! p0 f9 x/ L
' C" a+ @; e- e- s8 Q- 定时器中断中调用+ Y3 E3 Q: c' H; M# @3 Z
- void TIMx_IRQHandler(void)2 q- L# W3 U2 @5 ?8 q" ?% q
- {
; G% p: y; N8 v2 j1 u/ `+ q4 M - uart_dmatx_done_isr(DEV_UART1);% ^- h2 ?) c4 F# U, ~7 s( @2 E+ P
- uart_dmatx_done_isr(DEV_UART2);" b3 K+ Q0 [' V" k
- }
复制代码 0 S+ v& ]* {* L/ R" h7 ~
* u# Q9 U, h3 p# c$ U. B/ N- E I
- DMA通道传输完成中断中调用& n" y, |( F {" j
- D5 v/ B- q) w. T3 t: P* J, p% s
- void DMA1_Channel4_5_IRQHandler(void)
3 X- z3 J( h) a6 Q& x - {
6 b3 R, y6 U7 z( o/ b3 y - if(DMA_GetITStatus(DMA1_IT_TC4))" Z5 ~; ?( X" I/ `- O( ^
- {
# I8 I+ g/ j; R9 o8 W* @3 Y2 w - UartDmaSendDoneIsr(UART_2);. T, z) [) W# _& R; \. L
- DMA_ClearFlag(DMA1_FLAG_TC4);. {; p/ y# O& G
- uart_dmatx_done_isr(DEV_UART2);
6 D" U' Z i- X - }
, d; U' @, u1 |) E8 F( N - }
复制代码每次拷贝多少数据量到DMA发送buf: 关于这个问题,与具体应用场景有关,遵循的原则就是:只要发送fifo的数据量大于等于DMA发送buf的大小,就应该填满DMA发送buf,然后启动DMA传输,这样才能充分发挥会DMA性能。因此,需兼顾每次DMA传输的效率和串口数据流实时性,参考着几类实现: - 周期查询发送fifo数据,启动DMA传输,充分利用DMA发送效率,但可能降低串口数据流实时性
- 实时查询发送fifo数据,加上超时处理,理想的方法
- 在DMA传输完成中断中处理,保证实时连续数据流/ N2 v6 P/ {6 ~: n* I
1 `( w0 ^* B0 f* |7 d9 Q' d
; y2 \% W, H3 A m/ y0 B8 C/ R6 串口设备, B% w* w. Y. w; P% Z. [' N
1 u. W! A l9 G" d6 U6.1 数据结构! M0 b" d+ j1 ~- G2 u0 k
- /* 串口设备数据结构 */( A$ c2 [8 B, B; o$ v
- typedef struct7 B& W: h# K8 U( C O8 a2 w9 |
- {0 I; ^- a5 `( I0 q
- uint8_t status; /* 发送状态 */$ x/ g9 n( H/ @* A
- _fifo_t tx_fifo; /* 发送fifo */
6 }4 m/ ?. b0 f! |# k! G+ D! T - _fifo_t rx_fifo; /* 接收fifo */$ c% E2 _* d/ M5 V
- uint8_t *dmarx_buf; /* dma接收缓存 */
+ r. f3 R9 R( q6 X9 r: f8 s - uint16_t dmarx_buf_size;/* dma接收缓存大小*/
& f9 W# B2 Y* S N3 q9 v/ G - uint8_t *dmatx_buf; /* dma发送缓存 */2 ]$ t7 p) e; \2 s- I4 ?# A( p
- uint16_t dmatx_buf_size;/* dma发送缓存大小 */
. Q# P( L% |! d+ J# e4 ^ - uint16_t last_dmarx_size;/* dma上一次接收数据大小 */
9 W7 m+ h' @6 z - }uart_device_t;
复制代码
2 p1 T/ Z B" d# e/ [( t6 g' @6 _: N6 o
6.2 对外接口. A) q, b5 ?, @0 \1 H
3 N n {7 ?" Y- /* 串口注册初始化函数 */
% A9 P5 Q3 t! E2 v - void uart_device_init(uint8_t uart_id)
( S `9 w% U3 A7 r$ T% u* O) D' z - {
7 y$ D J9 B% \5 e& z! r/ A - if (uart_id == 1)3 n4 @7 g+ ^1 B6 e7 i4 M; v. ]
- {
+ L" C6 K4 t' u: ?* c - /* 配置串口2收发fifo */
1 f ] Z" c1 Q- b6 I9 _4 f. X - fifo_register(&s_uart_dev[uart_id].tx_fifo, &s_uart2_tx_buf[0],
8 E0 ]" a% B% ^ - sizeof(s_uart2_tx_buf), fifo_lock, fifo_unlock);$ n3 v1 @3 ^) T6 Y' L& ^
- fifo_register(&s_uart_dev[uart_id].rx_fifo, &s_uart2_rx_buf[0],
3 p" C& @0 {4 z7 K - sizeof(s_uart2_rx_buf), fifo_lock, fifo_unlock);% p4 L' I4 [* B- n; ?/ [, ~
- ) b7 p4 U4 j" f7 [8 o/ `8 B
- /* 配置串口2 DMA收发buf */
; k0 n- {& ~1 D4 d0 Z - s_uart_dev[uart_id].dmarx_buf = &s_uart2_dmarx_buf[0];: {; _2 Z& L: _( i1 L" M; h
- s_uart_dev[uart_id].dmarx_buf_size = sizeof(s_uart2_dmarx_buf);
* i# k5 a+ F8 h# s - s_uart_dev[uart_id].dmatx_buf = &s_uart2_dmatx_buf[0];* k1 E' w9 z7 b- a
- s_uart_dev[uart_id].dmatx_buf_size = sizeof(s_uart2_dmatx_buf);
6 T4 P0 D( g2 S# P! T - bsp_uart2_dmarx_config(s_uart_dev[uart_id].dmarx_buf, " X, W5 ?! J4 q: \* d- x
- sizeof(s_uart2_dmarx_buf));7 X( n# c6 r1 T. o5 {. @
- s_uart_dev[uart_id].status = 0;
+ b' ]8 i! {/ x8 e( @1 ] - }: B4 T4 j% {- ~9 b4 G
- } e+ R0 \2 u6 Z9 E( n y8 k
- 4 K h" N6 n, X7 }* H$ ?4 f
- /* 串口发送函数 */
# I* z/ S7 k/ u& Y6 H+ w( C \ - uint16_t uart_write(uint8_t uart_id, const uint8_t *buf, uint16_t size)
7 i7 w, H% {6 d: D - {/ u! N- t2 ~% F0 @8 q; [4 U# t
- return fifo_write(&s_uart_dev[uart_id].tx_fifo, buf, size);
1 Q1 I p2 x; m4 O - }
: [& H$ Q. ?! x; R( A+ i - 9 D$ l5 W1 f! E+ H
- /* 串口读取函数 */3 A) V1 d! f0 `! \
- uint16_t uart_read(uint8_t uart_id, uint8_t *buf, uint16_t size)
0 o$ ?/ o3 M" ^0 H% p% y4 m3 s - {- F7 A9 P/ M# u+ c4 g
- return fifo_read(&s_uart_dev[uart_id].rx_fifo, buf, size);2 q5 W% Z5 z. m; j# a
- }
复制代码
: A6 m9 J2 |7 E/ p" S6 G8 完整源码3 m4 V2 W7 ?& G0 F- s5 [# @- Y
; M9 [0 h+ K' H5 w
+ l3 B5 w# n0 J8 f3 I5 @串口&DMA底层配置:
9 `, O+ e) L8 j7 B5 s4 f
- #include <stddef.h>+ a* V z- d+ @( A
- #include <stdint.h>
$ I3 q' j" N6 ?5 X' [ - #include <stdbool.h>
5 G3 C$ U# o. s( a0 R - #include "stm32f0xx.h"% g; C3 }( \, H; Q5 ^* M2 { v1 _% |
- #include "bsp_uart.h"* E/ \2 u8 a0 g& l) u
- ~: |) _3 u( e9 g, _. [0 w
- /**
$ f1 T* t1 K% k' F - * @brief
5 `( T- o% J, Y; r" h$ S/ C - * @param
9 c( r" j9 w& v2 M# M. c - * @retval / J/ D8 _, Q: |5 O5 C# G
- */
! a* E9 M% e) Q& O) i6 Z: Q# Z- k7 U - static void bsp_uart1_gpio_init(void)! w- @+ ]& c2 ^
- {
$ ?0 o* D( t7 V6 l* Z! C1 @7 T5 V - GPIO_InitTypeDef GPIO_InitStructure;1 @, V6 C, { e3 I* B
- #if 0
0 R3 O: Q5 i- M+ c8 t - RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOB, ENABLE);4 h) n" [, d2 x% G
- ! b' z( x1 x5 }: P+ E8 Y. m" P3 E9 y
- GPIO_PinAFConfig(GPIOB, GPIO_PinSource6, GPIO_AF_0);1 x \+ T n7 A5 B% P
- GPIO_PinAFConfig(GPIOB, GPIO_PinSource7, GPIO_AF_0); i1 ^ o( d6 h5 f* c3 r
- % p3 [8 V' p7 D" e- I; ^0 c
- GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6 | GPIO_Pin_7;, n! F1 M( P% W
- GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
7 a1 C# ~+ A6 H/ w/ h7 B6 h% @ - GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
+ j' F2 ~/ h! E3 r4 f$ r - GPIO_InitStructure.GPIO_Speed = GPIO_Speed_Level_3;
, \5 A" s! \+ M e! o5 |8 p W - GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;
* y+ o6 a+ w1 H4 T - GPIO_Init(GPIOB, &GPIO_InitStructure);0 m, x4 e$ h$ G- n$ W; a3 b8 T! Q+ W
- #else
7 T1 N D; s/ _- {1 ^% d - RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOA, ENABLE);3 h! N" \/ L, u& W0 g) h
-
7 w; B/ j- B' p2 V- g1 n' a - GPIO_PinAFConfig(GPIOB, GPIO_PinSource9, GPIO_AF_1);( X4 ]0 C) V5 Y
- GPIO_PinAFConfig(GPIOB, GPIO_PinSource10, GPIO_AF_1);
5 t) H- Y# T3 ?* U) n) B* {/ @ -
9 r0 n2 R1 _0 v- o9 I - GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9 | GPIO_Pin_10;1 r% s" S* P" V- j: Q: D% M
- GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;5 Z9 U9 \6 v" ~# C) F
- GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
" x* g8 ?# a# I! D6 _ - GPIO_InitStructure.GPIO_Speed = GPIO_Speed_Level_3;
1 I) z" a3 ~( Z2 t5 q4 y - GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;2 N# E" R/ U. L/ Z2 o9 y; Y
- GPIO_Init(GPIOA, &GPIO_InitStructure);% f4 v2 p P, N) W3 Z& W. ~
- #endif$ q( Q* u. S4 _! M& `% ]
- }
2 Z0 A6 O; P* I, y
, [. s/ ~( ]' H/ _- p- /**
, n) n' Z4 h1 G9 u4 S2 T - * @brief # g/ o0 J7 B1 S6 S: l6 w! s
- * @param
# \8 h% Z- U, x - * @retval 1 b! C/ g1 F# D; V% B
- */$ X4 B* f8 w0 d+ G* d3 h, p! T/ e2 Z
- static void bsp_uart2_gpio_init(void)8 O) n5 S$ T# J' r% G( ]
- {
1 V: X; F& Q F+ Y/ u. t - GPIO_InitTypeDef GPIO_InitStructure;
7 s j- a8 X3 D -
" P1 D* ]5 U5 R& B- `0 s - RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOB, ENABLE);
) Y: H" e/ P: _- v4 B% l -
1 s' ^5 W( G& K8 _8 L% A - GPIO_PinAFConfig(GPIOA, GPIO_PinSource2, GPIO_AF_1);; }# S6 l1 c9 V" C; {
- GPIO_PinAFConfig(GPIOA, GPIO_PinSource3, GPIO_AF_1);- C! V6 i5 W- m# n# t
- T% ~. Y d: k+ v+ J/ `
- GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2 | GPIO_Pin_3;
! O6 a( g/ [$ F4 h+ i7 s - GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
! I3 m( Y+ S' b9 @ - GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;5 A9 P* ^4 s! m2 S1 i* {
- GPIO_InitStructure.GPIO_Speed = GPIO_Speed_10MHz;) |8 B% r. [: V) [
- GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;4 \0 m- `2 D. K+ t% {/ }) K( i" w
- GPIO_Init(GPIOA, &GPIO_InitStructure);- |. Q1 P! h# ~- M8 N
- }
3 Q+ b; P0 `2 P4 x - 4 {8 y+ s, I3 @6 S/ f
- /**
' T+ R; [/ G( H - * @brief : @" o# S. v2 z
- * @param - |2 a' k9 q) M1 q9 @: v6 s
- * @retval
T' _* U2 Z( N - */
: f. n! D3 R. f$ t" d- l l - void bsp_uart1_init(void)1 {6 f$ W; Q+ E$ r
- {
6 A1 c3 U+ [+ d* {+ z - USART_InitTypeDef USART_InitStructure;' y) y3 U; ~8 N5 _, m
- NVIC_InitTypeDef NVIC_InitStructure;
/ d9 e6 I ~% ?3 N% Y/ _ -
, O! i7 s, z3 R- Q - bsp_uart1_gpio_init();
( b' G9 x1 S5 x1 E - $ n; {1 i+ z* {1 d8 }) S# O* b
- /* 使能串口和DMA时钟 */
3 M: u6 {0 A5 E. r; k# T - RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);! v8 v0 G+ A4 v" p' _6 W+ ^
- RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);
8 I) B: Y. l$ ~5 G3 r9 p8 U -
0 G- S: p9 {+ p - USART_InitStructure.USART_BaudRate = 57600;0 O/ A1 d: ?. Z+ U0 F* D
- USART_InitStructure.USART_WordLength = USART_WordLength_8b;, p$ R2 e/ |# L5 K( Z# e
- USART_InitStructure.USART_StopBits = USART_StopBits_1;# Q2 g" D7 E- w L9 h }+ w/ p) D
- USART_InitStructure.USART_Parity = USART_Parity_No;0 ~" c( [$ c2 O' c0 E! T1 B9 f. u
- USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
! L' B# j) O) t; |7 S - USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;. E; ]- p9 p- I7 ? x+ k) A" [
- USART_Init(USART1, &USART_InitStructure);; X4 Q9 [3 s- ]. }2 e( E. U& ^
- 1 f* h M* j1 c
- USART_ITConfig(USART1, USART_IT_IDLE, ENABLE); /* 使能空闲中断 */
$ _: V( t* e, }1 N - USART_OverrunDetectionConfig(USART1, USART_OVRDetection_Disable);
, y" M8 ? d9 ]" J! M9 |, ` -
: q. P6 R' d1 ?9 v - USART_Cmd(USART1, ENABLE);
6 A; N3 c$ I7 X' i9 K% n a - USART_DMACmd(USART1, USART_DMAReq_Rx|USART_DMAReq_Tx, ENABLE); /* 使能DMA收发 */5 z4 V# a; l0 l# [3 Q2 A; y/ w
- ' x7 L: N! I8 p7 ^, e$ g
- /* 串口中断 */8 C- r _, M/ _- k, } {( ~! ?2 y
- NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;
' F& w" m' q- U) V& i- }8 A4 R, n; N - NVIC_InitStructure.NVIC_IRQChannelPriority = 2;
) ~4 i! t5 [% Y4 u - NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
: ~3 {8 ^0 r0 U4 U) z - NVIC_Init(&NVIC_InitStructure);
; D0 a" l& i# v/ F# q: p - ) k0 B8 b& F7 \3 G& f6 h, N3 z
- /* DMA中断 */
* f9 s+ _; G/ _, y+ d$ U* @3 ~ - NVIC_InitStructure.NVIC_IRQChannel = DMA1_Channel2_3_IRQn;
# H3 h. R/ j# }- P - NVIC_InitStructure.NVIC_IRQChannelPriority = 0;
* c- t" a+ f4 X, |* \" l" {! V - NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
* O) f* [8 g3 f {$ S/ z1 m9 b - NVIC_Init(&NVIC_InitStructure);
! ]& F* i4 E! G8 f - }4 m/ O0 H+ P6 x' c
" V9 i9 {: T) v5 a- /**# K) G. r, Y, z
- * @brief
: c2 j$ x1 c: t& I& S# Z& B2 h5 J - * @param 6 N5 n+ f; V. }# `; E1 z8 I
- * @retval ' z6 ]. L0 N4 O7 \, q! L
- */
- v( E4 ~; _* D$ E - void bsp_uart2_init(void)8 Y6 @2 D% ]: d& T J
- {- i" O$ C1 v1 j0 j" h3 }" a
- USART_InitTypeDef USART_InitStructure;
' h+ @( w% d& I# K- \ - NVIC_InitTypeDef NVIC_InitStructure;
8 ? Q8 R; r" D0 f3 m* b - % N) h& w& }4 r5 d' b
- bsp_uart2_gpio_init();
3 n8 x* {6 v/ P* e( K0 a -
7 e8 t. Z9 s2 Q: p8 S - /* 使能串口和DMA时钟 */3 g9 n- {" c1 d% D
- RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);
. a e& @2 F9 \ - RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2, ENABLE);& r5 u2 w0 F5 A( F' t$ X& O
- # w& ^' w$ h" u3 E0 F. W7 N
- USART_InitStructure.USART_BaudRate = 57600;
. s6 c/ `1 `) l3 W8 R# ~" n - USART_InitStructure.USART_WordLength = USART_WordLength_8b;
# l4 U& z8 Z: K5 m( ~/ F8 l - USART_InitStructure.USART_StopBits = USART_StopBits_1;& v4 _7 b) ~% g
- USART_InitStructure.USART_Parity = USART_Parity_No;/ `" U! N/ }; [! H. |* {
- USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
9 O& L0 g- ~( X8 k- T - USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;0 f3 K# L: f4 I" v1 h, A
- USART_Init(USART2, &USART_InitStructure);5 p a, B+ V ?
- ( _3 u6 m. n; S b& d: C3 c
- USART_ITConfig(USART2, USART_IT_IDLE, ENABLE); /* 使能空闲中断 */
2 T9 Y$ ^* h7 {( B4 l- G# I' ~# C - USART_OverrunDetectionConfig(USART2, USART_OVRDetection_Disable);1 d9 M% B5 ?+ a6 p
- ' u. u6 [, z) A8 P( o4 G* O. F
- USART_Cmd(USART2, ENABLE);- L+ P- E5 _/ S8 j2 |
- USART_DMACmd(USART2, USART_DMAReq_Rx|USART_DMAReq_Tx, ENABLE); /* 使能DMA收发 */+ _0 n3 K; g% x- q3 L" f
- 0 W M5 T/ M( W5 D" z
- /* 串口中断 */
' R0 d; O! S( n4 I Q- s. y6 d: B - NVIC_InitStructure.NVIC_IRQChannel = USART2_IRQn;6 n6 l4 W& V1 i0 |/ K# K0 b8 ]: u# e m
- NVIC_InitStructure.NVIC_IRQChannelPriority = 2;
- b7 S( `8 `& M3 g - NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
$ y. D9 e! B3 G4 N7 C x2 Q - NVIC_Init(&NVIC_InitStructure);0 t0 ]' f, B& d
% b" k2 v4 m$ N$ r5 p% @ ?9 w! x- /* DMA中断 */
2 z- X8 t& ]& a; | - NVIC_InitStructure.NVIC_IRQChannel = DMA1_Channel4_5_IRQn; / j/ S$ x4 e& d. M |% j5 n! h
- NVIC_InitStructure.NVIC_IRQChannelPriority = 0; ' _: e- S$ H F- \) w' o
- NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
2 s6 r! N$ _1 \* T; u! @ - NVIC_Init(&NVIC_InitStructure);
& Z; a& ^. p* u1 u, t7 B2 C - }6 H& `$ U% q& P- j7 P5 g4 K
, j0 x( d* d0 `& c# q8 J! s- void bsp_uart1_dmatx_config(uint8_t *mem_addr, uint32_t mem_size)% J0 \# ` o5 X8 K+ M
- {4 P( \9 U7 e6 y/ T
- DMA_InitTypeDef DMA_InitStructure;- ?! S: M: w# p* J0 R
- " u$ _; ^$ m5 G. M2 o
- DMA_DeInit(DMA1_Channel2);5 O% l6 x7 H: D9 V( {
- DMA_Cmd(DMA1_Channel2, DISABLE);- }2 X# W" w* \& H
- DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&(USART1->TDR);
) r9 q8 y- z4 L. N7 W5 U - DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)mem_addr;
: D3 R# f' K- v, K% n, U5 J - DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST; /* 传输方向:内存->外设 */
0 ]1 I' s j! y9 x% h9 H# g0 E0 v - DMA_InitStructure.DMA_BufferSize = mem_size;
- ~5 r" n9 S1 I' M - DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable; - t4 m; w9 J4 s7 }# ~7 R* ~, {3 Z, i
- DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable; 9 v0 l" U& g# E; Y, l
- DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte; ( v S5 M9 i8 q. @
- DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;
' ^0 M" b( e7 c" A6 Z1 N- k - DMA_InitStructure.DMA_Mode = DMA_Mode_Normal; ! U0 F& K+ ^4 }' R, Z8 V
- DMA_InitStructure.DMA_Priority = DMA_Priority_High; 5 @0 s( V+ E* ]- j1 [& Y
- DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;
' m# b1 ?8 l& U+ K) P - DMA_Init(DMA1_Channel2, &DMA_InitStructure);
8 H- S. ^9 S' \; x. h, }4 c' M1 c - DMA_ITConfig(DMA1_Channel2, DMA_IT_TC|DMA_IT_TE, ENABLE);
( a! Z- A) |7 v - DMA_ClearFlag(DMA1_IT_TC2); /* 清除发送完成标识 */$ o! T% k+ r% `0 S3 x1 k; p8 s
- DMA_Cmd(DMA1_Channel2, ENABLE); $ @$ r, [& @4 G8 C# r& T2 w# S
- }
5 `9 T9 a" {* y1 p8 B5 s
1 j* s. Q i. W5 {- Y7 T- void bsp_uart1_dmarx_config(uint8_t *mem_addr, uint32_t mem_size)7 Q9 S8 ]7 H; l0 Q; A3 x; C
- {: d9 ^" G" L1 y" y* ~5 D* q$ f9 e
- DMA_InitTypeDef DMA_InitStructure;
7 m/ |" {* E1 o -
( x, m- X- p" [* k' _/ ^; o+ Y- x - DMA_DeInit(DMA1_Channel3);
# n5 C4 y4 \0 t/ N% f/ J - DMA_Cmd(DMA1_Channel3, DISABLE);
% k( P# J; @* k% T% A; S - DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&(USART1->RDR);
8 ^4 `% P. m+ E- `3 _* |0 H$ { - DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)mem_addr; " ?7 |0 n2 J. L& K% n" {
- DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC; /* 传输方向:外设->内存 */+ X5 _+ R4 i5 ]
- DMA_InitStructure.DMA_BufferSize = mem_size;
1 ]* ?' Y9 K1 q$ [9 R7 b9 M# S - DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable; - ~" s; |% t5 G1 l
- DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
( h3 w4 R6 P) D- q - DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;
% _' y1 X% m u7 a) Y* I0 i - DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;- a4 l, e( |: M. {' j1 \: g# I
- DMA_InitStructure.DMA_Mode = DMA_Mode_Circular; 5 ]( b2 |/ G' r# l
- DMA_InitStructure.DMA_Priority = DMA_Priority_VeryHigh; + T( `" M/ E' i" v2 g% a
- DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;
+ m. o) }0 o3 V9 t- `! `8 f - DMA_Init(DMA1_Channel3, &DMA_InitStructure);
( }3 A+ S: ~( \1 ?+ F/ s, |& Z E- L - DMA_ITConfig(DMA1_Channel3, DMA_IT_TC|DMA_IT_HT|DMA_IT_TE, ENABLE);/* 使能DMA半满、全满、错误中断 */
" p# u% S7 O: b - DMA_ClearFlag(DMA1_IT_TC3);; j1 U0 N( i, G+ ^7 P
- DMA_ClearFlag(DMA1_IT_HT3);
' H( c5 c* `) ^6 J$ T - DMA_Cmd(DMA1_Channel3, ENABLE); ( z! R# u V6 z2 {* | i7 D
- }
9 X$ p& Y5 e7 e - 3 g+ F. ?) v+ J8 b
- uint16_t bsp_uart1_get_dmarx_buf_remain_size(void)
, j$ b) i- T. i% N6 \3 g' j - {( K5 L& f( @+ t1 N& l, d
- return DMA_GetCurrDataCounter(DMA1_Channel3); /* 获取DMA接收buf剩余空间 */
0 N1 n* F' T. I0 C7 b8 \. r - }" k; @. a) N$ r9 w) h
- . C. L8 A3 Q7 E
- void bsp_uart2_dmatx_config(uint8_t *mem_addr, uint32_t mem_size); I. d* N6 N% p3 [1 u5 R
- {0 o) [, T% y6 r1 N
- DMA_InitTypeDef DMA_InitStructure;
& j2 x! ~( x" y7 n- {' Y, c - ; Q3 ^, m- I9 K8 I8 }" O
- DMA_DeInit(DMA1_Channel4);( M5 \6 {& O1 `2 o7 T
- DMA_Cmd(DMA1_Channel4, DISABLE);7 a: `0 s8 u% l0 U1 N) r" `
- DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&(USART2->TDR);
( H1 T8 q9 p; M0 z# x0 x7 M - DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)mem_addr; * }' g/ H7 d, i+ G
- DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST; /* 传输方向:内存->外设 */ d% K$ Z0 T( g" T* u
- DMA_InitStructure.DMA_BufferSize = mem_size; ; Z5 [% l1 W% e
- DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable; 9 N& m2 f+ q& X) r) E
- DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
1 y4 D( R6 B. u0 q8 k' Y - DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte; ' @ x' u. |" b% L: O c8 L
- DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;
% }, v' M! J# p - DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;
6 [( m8 G# n& P' G: L - DMA_InitStructure.DMA_Priority = DMA_Priority_High; / o& H, e3 p5 l
- DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;
4 N# ] [! A& ~7 c: L$ a9 m - DMA_Init(DMA1_Channel4, &DMA_InitStructure); 0 f" j# e$ g+ [8 T. [. i
- DMA_ITConfig(DMA1_Channel4, DMA_IT_TC|DMA_IT_TE, ENABLE);
8 m: C D8 J2 y s2 e) |* Z7 {; v; | - DMA_ClearFlag(DMA1_IT_TC4); /* 清除发送完成标识 */
* x# j# D& i" ?" n8 i+ v% T6 ?9 M# u, S - DMA_Cmd(DMA1_Channel4, ENABLE);
+ B' {; q/ l1 C x1 ^2 b - }
* Y$ I) `7 }; z2 y# H$ Z3 T. C
, P2 y$ k# T7 Z7 }- void bsp_uart2_dmarx_config(uint8_t *mem_addr, uint32_t mem_size)
v. q7 H4 I3 s6 S$ ^5 [ - {$ l$ }3 y# C& I( K
- DMA_InitTypeDef DMA_InitStructure;
, F0 {' X) W8 N3 K/ C! l - 0 s+ Y+ B! s; d+ ?( a! [
- DMA_DeInit(DMA1_Channel5);
% Y8 F5 ~3 f+ c, }$ g/ y - DMA_Cmd(DMA1_Channel5, DISABLE);
, w& e4 s! s1 |; O8 a5 A - DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&(USART2->RDR);
7 n5 P' K4 l: A- @ I- f - DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)mem_addr;
, M. ^- H) s @$ E+ i/ l3 R* X8 D - DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC; /* 传输方向:外设->内存 */
# H/ C* ~5 S% L% E - DMA_InitStructure.DMA_BufferSize = mem_size; - `2 \4 y" c- c7 \- `8 P- w
- DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
! D% s+ [0 _: ~' `5 H; I - DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
2 p% \. \1 E9 @4 l( ] - DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;
9 Z9 g; U9 ^# w& \$ n- ? - DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;( E8 C8 D5 t3 P0 u0 Q% j
- DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;
; P: G D3 C; _8 |3 v - DMA_InitStructure.DMA_Priority = DMA_Priority_VeryHigh; 0 m# e$ f& C( ?( `7 S, V# e1 |
- DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;
/ v! K, s" H2 j9 b) r" x7 k5 C - DMA_Init(DMA1_Channel5, &DMA_InitStructure); 5 t3 J1 p" u' S( C0 z/ ^7 j+ S
- DMA_ITConfig(DMA1_Channel5, DMA_IT_TC|DMA_IT_HT|DMA_IT_TE, ENABLE);/* 使能DMA半满、全满、错误中断 */
$ V, m& D, g4 {" I - DMA_ClearFlag(DMA1_IT_TC5);
" U! Q, U# V# Y" s5 W4 K9 f( } U( T - DMA_ClearFlag(DMA1_IT_HT5);0 E3 a+ {- I' l q/ O3 g3 D l
- DMA_Cmd(DMA1_Channel5, ENABLE);
+ p+ c* O) b& g! \* A8 T9 n - }
! X# J/ @# c9 V- E
& {- @+ J2 ] e* Y- uint16_t bsp_uart2_get_dmarx_buf_remain_size(void)2 Y( g" S. D4 E, O
- {* J& N* E) K4 T- o
- return DMA_GetCurrDataCounter(DMA1_Channel5); /* 获取DMA接收buf剩余空间 */0 J* S; H/ }& n' S s3 |# j
- }
复制代码 , ] D8 V# ]9 }6 u$ c% a) P
3 n3 F# z" b. s$ J" p4 `. ]# ?
压力测试: - 1.5Mbps波特率,串口助手每毫秒发送1k字节数据,stm32f0 DMA接收数据,再通过DMA发送回串口助手,毫无压力。
- 1.5Mbps波特率,可传输大文件测试,将接受数据保存为文件,与源文件比较。
- 串口高波特率测试需要USB转TLL工具及串口助手都支持才可行,推荐CP2102、FT232芯片的USB转TTL工具。
0 Q' G! V+ P' V2 w3 _ U; z7 E: E3 I+ V8 w
* k4 c$ ?' Y+ G$ p
, {/ V3 [1 T+ c+ s! G& G
1.5Mbps串口回环压力测试 0 D5 j7 _& o% K7 Z4 d+ H; n) \
- e8 j4 A: V7 _
O9 p# P7 t0 C |
大牛能不能放一个代码示例出来学** 这里的fifo函数如何实现的?谢谢了
半满和全满中断函数是不是没有添加清中断的代码。