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