问题描述: 我有一个需求,AD采得一定数目的数据之后,由串口DMA发出,由于AD使用双缓冲,所以每次开始DMA的时候都需要重新设置开始的内存地址以及传输的数目(这些都是理所当然的),但是在开始调试的时候,遇到了一些问题,问题如下:当第一次DMA传输完毕,关闭DMA以设置内存地址等,再开启DMA,发现不启动了。 开始是参考了《STM32中文参考手册REV10》,里面的发送步骤如下: - 1. 在DMA控制寄存器上将USART_DR寄存器地址配置成DMA传输的目的地址。在每个TXE事件后,数据将被传送到这个地址。' K' l; k1 r2 n9 b) c
- 2. 在DMA控制寄存器上将存储器地址配置成DMA传输的源地址。在每个TXE事件后,将从此存储器区读出数据并传送到USART_DR寄存器。4 B1 M/ G8 G5 d0 s. ~
- 3. 在DMA控制寄存器中配置要传输的总的字节数。3 I; Y7 w! R4 |- y
- 4. 在DMA寄存器上配置通道优先级。
, }& j- S4 F6 O2 b% ]+ T - 5. 根据应用程序的要求,配置在传输完成一半还是全部完成时产生DMA中断。# H; [' V0 `2 L5 m' x( Q8 N
- 6. 在DMA寄存器上激活该通道。
复制代码
3 r t, |$ H Z% }% T3 f检查代码,发现没问题,但是问题还是解决不了,就找了英文的参考手册(REV14),发现上面的步骤有了些修改: - 1. Write the USART_DR register address in the DMA control register to configure it as the
) i9 G; V: s d2 J: v, Y - destination of the transfer. The data will be moved to this address from memory after
% F3 @9 `5 Z6 l- n - each TXE event.# C: c/ Y$ |$ a) M& g4 _6 x V
- 2. Write the memory address in the DMA control register to configure it as the source of6 I) H% G( B: f/ y
- the transfer. The data will be loaded into the USART_DR register from this memory4 T8 g" N& R5 e4 J5 v
- area after each TXE event.
" }: d3 p2 O$ q, Z. C - 3. Configure the total number of bytes to be transferred to the DMA control register.
; [4 ?' ^! |2 g" P% C; S - 4. Configure the channel priority in the DMA register1 ?3 t9 E( L6 j, C
- 5. Configure DMA interrupt generation after half/ full transfer as required by the% r \' f- e2 `5 Q: H+ x+ T3 e
- application.5 D7 b+ m/ S# R8 r& ]; q! T6 b; `
- 6. Clear the TC bit in the SR register by writing 0 to it. a# A/ v* Z# N7 h0 ?* |. y
- 7. Activate the channel in the DMA register.
0 R3 B. R1 A6 \9 }% ? - When the number of data transfers programmed
复制代码
$ E0 A+ g) ?6 j' r( t+ I多了一步,即第6步,清除SR寄存器的TC标志位。照做,ok了。
. n- m. i% R) _% m! L' a- Z重启DMA传输成功的条件:后来,又做了其他一些测试,发现即使不清除TC标志位,只要把对应DMA通道的TCIF标志(传输完成标志,DMA_ISR寄存器里面)清除,同样也可以正常重启传输。所以,要重启传输,保证在重新ENABLE DMA之前,满足以下两个条件之一即可: - DMA_ISR 对应的TCIF标志清除
- USART_SR 的TC标志位清除
8 t. V8 Y! V1 ?/ \+ f 一个诡异的问题:发现即使上面提到的两个条件都不满足,但是下面的代码依然可以正常传输:
6 p' H( W# {- A; `8 f6 e/ ` - //ENBALE 之前USART1的TC,DMA1 对应USART TX的DMA通道TC标志都已经置位,没有清除* w0 y# ?* x8 z0 i3 K# J
- DMA_Cmd(USART1_Tx_DMA_Channel, ENABLE);) y+ @) P6 H! _* @0 M' Y. w
- while((USART1->SR & USART_FLAG_TC) == 0x00);
复制代码 ; N/ |5 Y6 f Z* W3 _/ e
这样也可以。但是只要换成: - DMA_Cmd(USART1_Tx_DMA_Channel, ENABLE);. [# f1 e4 j7 K& ^) F9 y% U1 c1 y
- : Z& d" T5 Q" K6 O. {! }6 b
- /* Wait until USARTy TX DMA1 Channel Transfer Complete */2 V5 g0 z& a; z/ }! o3 [
- while (DMA_GetFlagStatus(USART1_Tx_DMA_FLAG ) == RESET) {0 W- E! i. k; l9 M
- }
复制代码 1 ~' r' P, o8 r4 H
就不行了。比较奇怪。 - a% ?0 C6 A$ z0 l& \
结论:不管那些诡异的问题了,以后重启DMA之前,清除串口的TC或者DMA的TC。不过根据使用的感觉,清除DMA的TC简单些。 + [; U6 g0 b5 o$ k& O* G: N
|