问题描述: 我有一个需求,AD采得一定数目的数据之后,由串口DMA发出,由于AD使用双缓冲,所以每次开始DMA的时候都需要重新设置开始的内存地址以及传输的数目(这些都是理所当然的),但是在开始调试的时候,遇到了一些问题,问题如下:当第一次DMA传输完毕,关闭DMA以设置内存地址等,再开启DMA,发现不启动了。 开始是参考了《STM32中文参考手册REV10》,里面的发送步骤如下: - 1. 在DMA控制寄存器上将USART_DR寄存器地址配置成DMA传输的目的地址。在每个TXE事件后,数据将被传送到这个地址。4 O6 w, Q' o) X8 C3 }/ n
- 2. 在DMA控制寄存器上将存储器地址配置成DMA传输的源地址。在每个TXE事件后,将从此存储器区读出数据并传送到USART_DR寄存器。
" p$ y# s" t" H8 I1 P# k7 W - 3. 在DMA控制寄存器中配置要传输的总的字节数。
& x+ }* l: z& b' ]+ l8 M3 J# K - 4. 在DMA寄存器上配置通道优先级。
+ Z9 a+ c4 M1 Y7 ?0 p @% w6 e E - 5. 根据应用程序的要求,配置在传输完成一半还是全部完成时产生DMA中断。$ o, k9 K: N$ n+ v
- 6. 在DMA寄存器上激活该通道。
复制代码 $ v F) b c- C) z H+ b! s
检查代码,发现没问题,但是问题还是解决不了,就找了英文的参考手册(REV14),发现上面的步骤有了些修改: - 1. Write the USART_DR register address in the DMA control register to configure it as the9 M; |1 L: B$ K, Q
- destination of the transfer. The data will be moved to this address from memory after
* ]* ^' o; i+ P$ [ - each TXE event.
2 r+ w8 j X2 c; a - 2. Write the memory address in the DMA control register to configure it as the source of
1 D3 q9 q1 {* s - the transfer. The data will be loaded into the USART_DR register from this memory; k w$ {' R# ? K. d* w& F; D
- area after each TXE event.
6 m* c5 i9 _2 M9 n; T6 m$ G - 3. Configure the total number of bytes to be transferred to the DMA control register.% O* J% f0 g- ]7 Y9 C5 v
- 4. Configure the channel priority in the DMA register6 W0 g$ Y! C T7 { x; i9 c+ J
- 5. Configure DMA interrupt generation after half/ full transfer as required by the
% T N1 a0 R# R$ ^ - application.+ l1 `& {% W0 F+ @) Y
- 6. Clear the TC bit in the SR register by writing 0 to it.
6 ]: o7 Z% l2 ]7 j8 X9 d - 7. Activate the channel in the DMA register.
$ U* `$ y. }7 S* b) H3 m0 v - When the number of data transfers programmed
复制代码
2 L6 A. P; `8 O+ {2 x多了一步,即第6步,清除SR寄存器的TC标志位。照做,ok了。 ; n1 \' a9 e# S2 J7 J4 T4 I+ ?
重启DMA传输成功的条件:后来,又做了其他一些测试,发现即使不清除TC标志位,只要把对应DMA通道的TCIF标志(传输完成标志,DMA_ISR寄存器里面)清除,同样也可以正常重启传输。所以,要重启传输,保证在重新ENABLE DMA之前,满足以下两个条件之一即可: - DMA_ISR 对应的TCIF标志清除
- USART_SR 的TC标志位清除
' H: g7 b8 D) n6 R* n 一个诡异的问题:发现即使上面提到的两个条件都不满足,但是下面的代码依然可以正常传输:
& l% a' {6 C+ N3 n3 ]% N! d( S - //ENBALE 之前USART1的TC,DMA1 对应USART TX的DMA通道TC标志都已经置位,没有清除
$ I( e6 R# _: I$ o. B - DMA_Cmd(USART1_Tx_DMA_Channel, ENABLE);" _8 C# m3 @7 Q2 S1 b2 S
- while((USART1->SR & USART_FLAG_TC) == 0x00);
复制代码 j" L }% ^/ ]! {/ s$ v1 m: E- R$ |
这样也可以。但是只要换成: - DMA_Cmd(USART1_Tx_DMA_Channel, ENABLE);
4 h* k5 k7 p# @4 i( | -
" C) f" h2 [ q1 [# |" t8 } - /* Wait until USARTy TX DMA1 Channel Transfer Complete */
: e: m7 Q9 G, e: F- i - while (DMA_GetFlagStatus(USART1_Tx_DMA_FLAG ) == RESET) {
+ [) [7 s5 P; \4 T! c) F4 b2 s - }
复制代码
4 P& B9 v4 }) s! h$ H/ I; a P就不行了。比较奇怪。 & ^3 h# O2 {) } d( X0 a
结论:不管那些诡异的问题了,以后重启DMA之前,清除串口的TC或者DMA的TC。不过根据使用的感觉,清除DMA的TC简单些。 ~# f7 Z4 _ q) s! u+ s* b( S
|