问题描述: 我有一个需求,AD采得一定数目的数据之后,由串口DMA发出,由于AD使用双缓冲,所以每次开始DMA的时候都需要重新设置开始的内存地址以及传输的数目(这些都是理所当然的),但是在开始调试的时候,遇到了一些问题,问题如下:当第一次DMA传输完毕,关闭DMA以设置内存地址等,再开启DMA,发现不启动了。 开始是参考了《STM32中文参考手册REV10》,里面的发送步骤如下: - 1. 在DMA控制寄存器上将USART_DR寄存器地址配置成DMA传输的目的地址。在每个TXE事件后,数据将被传送到这个地址。! O2 c7 m- K% T" {( b( h* G) j
- 2. 在DMA控制寄存器上将存储器地址配置成DMA传输的源地址。在每个TXE事件后,将从此存储器区读出数据并传送到USART_DR寄存器。
, f7 _. }( `4 K( d! z - 3. 在DMA控制寄存器中配置要传输的总的字节数。7 m+ r' Z$ \: ?2 Q3 _
- 4. 在DMA寄存器上配置通道优先级。) C1 `. C* _: c" A
- 5. 根据应用程序的要求,配置在传输完成一半还是全部完成时产生DMA中断。) q/ A* m/ N+ W
- 6. 在DMA寄存器上激活该通道。
复制代码
/ {. u0 U! c6 H9 C/ e) M检查代码,发现没问题,但是问题还是解决不了,就找了英文的参考手册(REV14),发现上面的步骤有了些修改: - 1. Write the USART_DR register address in the DMA control register to configure it as the
: _3 V9 m1 Q# @ g, G" i; Z - destination of the transfer. The data will be moved to this address from memory after
0 d& h- r$ k3 {+ e, M - each TXE event.
- f, a# a# e( V) B5 N9 t' r - 2. Write the memory address in the DMA control register to configure it as the source of. D9 T- S/ g$ `9 D- Y& f: k
- the transfer. The data will be loaded into the USART_DR register from this memory. E/ m- F2 W9 V
- area after each TXE event.: d: T3 f6 ^' Q8 t. ]
- 3. Configure the total number of bytes to be transferred to the DMA control register.
5 c' s. }! Q( Y( {7 N! C5 Z/ C - 4. Configure the channel priority in the DMA register
* l1 d& K& D2 w2 x - 5. Configure DMA interrupt generation after half/ full transfer as required by the
3 k6 ~ T$ D+ r* q( K9 |3 c - application.
2 B- v; G, b0 h2 h( h% d2 k0 B8 M - 6. Clear the TC bit in the SR register by writing 0 to it.+ @2 \6 n1 r+ \) M' f
- 7. Activate the channel in the DMA register.2 Z4 V2 G" M+ n+ [& Z" | a$ ]' X
- When the number of data transfers programmed
复制代码 1 ~* c* k7 w' d) g
多了一步,即第6步,清除SR寄存器的TC标志位。照做,ok了。
2 m) u' P$ Q0 O5 ^6 W) o" a) t重启DMA传输成功的条件:后来,又做了其他一些测试,发现即使不清除TC标志位,只要把对应DMA通道的TCIF标志(传输完成标志,DMA_ISR寄存器里面)清除,同样也可以正常重启传输。所以,要重启传输,保证在重新ENABLE DMA之前,满足以下两个条件之一即可: - DMA_ISR 对应的TCIF标志清除
- USART_SR 的TC标志位清除8 H$ m0 Z: H# i9 X6 W, c
一个诡异的问题:发现即使上面提到的两个条件都不满足,但是下面的代码依然可以正常传输:5 z( c' Y& e- T' U
- //ENBALE 之前USART1的TC,DMA1 对应USART TX的DMA通道TC标志都已经置位,没有清除1 Y& p/ ]9 p1 U" q8 X, V9 M- Q
- DMA_Cmd(USART1_Tx_DMA_Channel, ENABLE); z5 V! \6 g2 D* N* Z
- while((USART1->SR & USART_FLAG_TC) == 0x00);
复制代码 8 O$ r* c, H2 v' p. _
这样也可以。但是只要换成: - DMA_Cmd(USART1_Tx_DMA_Channel, ENABLE);% Z- A. z5 a u& @ G( S
-
3 X1 U8 J, O- [: j7 g0 ]/ u - /* Wait until USARTy TX DMA1 Channel Transfer Complete */
' p4 f# y) C9 `, G$ ` - while (DMA_GetFlagStatus(USART1_Tx_DMA_FLAG ) == RESET) {2 z0 R2 s7 P, ]6 S; D* A
- }
复制代码
' Y# Y" @ ?+ t& c5 h0 m1 |% O就不行了。比较奇怪。
) k/ ?* N) I" M结论:不管那些诡异的问题了,以后重启DMA之前,清除串口的TC或者DMA的TC。不过根据使用的感觉,清除DMA的TC简单些。 ) T& |$ {+ Y4 |3 W3 \2 }: N
|