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