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