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