问题描述: 我有一个需求,AD采得一定数目的数据之后,由串口DMA发出,由于AD使用双缓冲,所以每次开始DMA的时候都需要重新设置开始的内存地址以及传输的数目(这些都是理所当然的),但是在开始调试的时候,遇到了一些问题,问题如下:当第一次DMA传输完毕,关闭DMA以设置内存地址等,再开启DMA,发现不启动了。 开始是参考了《STM32中文参考手册REV10》,里面的发送步骤如下: - 1. 在DMA控制寄存器上将USART_DR寄存器地址配置成DMA传输的目的地址。在每个TXE事件后,数据将被传送到这个地址。
. U) p" a, [: V1 v7 U% I - 2. 在DMA控制寄存器上将存储器地址配置成DMA传输的源地址。在每个TXE事件后,将从此存储器区读出数据并传送到USART_DR寄存器。
: l$ |) a( U% `) s5 A5 d - 3. 在DMA控制寄存器中配置要传输的总的字节数。) \* J: M* f) U {9 }+ i% z7 l* g
- 4. 在DMA寄存器上配置通道优先级。
9 o/ V9 y& w! k$ e( s - 5. 根据应用程序的要求,配置在传输完成一半还是全部完成时产生DMA中断。
& b5 Z0 V. c2 i - 6. 在DMA寄存器上激活该通道。
复制代码
; p6 V( ?2 @' l! a! q% }- z) S1 S2 M- K检查代码,发现没问题,但是问题还是解决不了,就找了英文的参考手册(REV14),发现上面的步骤有了些修改: - 1. Write the USART_DR register address in the DMA control register to configure it as the
/ f: g5 s# ^/ e - destination of the transfer. The data will be moved to this address from memory after
: w- @9 R9 i V$ z0 c - each TXE event.
+ m' i2 ~+ h9 U* s; Y! c - 2. Write the memory address in the DMA control register to configure it as the source of
# ]! u( S4 _ t% Y+ n/ J2 ~4 i% M - the transfer. The data will be loaded into the USART_DR register from this memory
+ M8 f3 \; G9 Z2 M* j - area after each TXE event.
- Q, t; Z6 U9 | b+ `8 o4 H% t2 g - 3. Configure the total number of bytes to be transferred to the DMA control register./ L+ w5 m2 ?8 j* F: x) B
- 4. Configure the channel priority in the DMA register
: R" G/ X! O$ j. i* P - 5. Configure DMA interrupt generation after half/ full transfer as required by the% c. `, J$ }/ G! E
- application.
+ r% [0 k; l9 C6 n4 ?. B4 d - 6. Clear the TC bit in the SR register by writing 0 to it.
) _9 ~8 V a6 }) M - 7. Activate the channel in the DMA register.1 F: K0 c: s5 G% S) X9 A
- When the number of data transfers programmed
复制代码
# ]: C% w2 {4 ?0 t5 B1 u7 W多了一步,即第6步,清除SR寄存器的TC标志位。照做,ok了。
5 j3 N3 l7 W* U6 h& J% x重启DMA传输成功的条件:后来,又做了其他一些测试,发现即使不清除TC标志位,只要把对应DMA通道的TCIF标志(传输完成标志,DMA_ISR寄存器里面)清除,同样也可以正常重启传输。所以,要重启传输,保证在重新ENABLE DMA之前,满足以下两个条件之一即可: - DMA_ISR 对应的TCIF标志清除
- USART_SR 的TC标志位清除* F+ h# J1 M! A2 N3 L' u
一个诡异的问题:发现即使上面提到的两个条件都不满足,但是下面的代码依然可以正常传输:
# g) ]+ h: _' }* b( A! I - //ENBALE 之前USART1的TC,DMA1 对应USART TX的DMA通道TC标志都已经置位,没有清除
. y5 w0 D$ ^9 E6 ~; p# m - DMA_Cmd(USART1_Tx_DMA_Channel, ENABLE);
& j7 V I9 t3 Z - while((USART1->SR & USART_FLAG_TC) == 0x00);
复制代码 6 |- R: C1 Q, b8 `
这样也可以。但是只要换成: - DMA_Cmd(USART1_Tx_DMA_Channel, ENABLE);
' @3 s- w! a1 Y/ ]0 ?; d - ) r! q+ k" h+ q
- /* Wait until USARTy TX DMA1 Channel Transfer Complete */
! R9 L/ O; M6 F0 a/ r" N8 S! K - while (DMA_GetFlagStatus(USART1_Tx_DMA_FLAG ) == RESET) {# {' B8 o. Q" b& S
- }
复制代码
$ X1 Y) v9 `# O5 J' ?9 |" \就不行了。比较奇怪。
4 b/ t9 q& \! K结论:不管那些诡异的问题了,以后重启DMA之前,清除串口的TC或者DMA的TC。不过根据使用的感觉,清除DMA的TC简单些。 , d$ E# c) N: h& [
|