问题描述: 我有一个需求,AD采得一定数目的数据之后,由串口DMA发出,由于AD使用双缓冲,所以每次开始DMA的时候都需要重新设置开始的内存地址以及传输的数目(这些都是理所当然的),但是在开始调试的时候,遇到了一些问题,问题如下:当第一次DMA传输完毕,关闭DMA以设置内存地址等,再开启DMA,发现不启动了。 开始是参考了《STM32中文参考手册REV10》,里面的发送步骤如下: - 1. 在DMA控制寄存器上将USART_DR寄存器地址配置成DMA传输的目的地址。在每个TXE事件后,数据将被传送到这个地址。7 F8 q) A/ u% w5 L* K$ e' o
- 2. 在DMA控制寄存器上将存储器地址配置成DMA传输的源地址。在每个TXE事件后,将从此存储器区读出数据并传送到USART_DR寄存器。2 e. ?2 r, x n7 d: G4 R8 g1 O
- 3. 在DMA控制寄存器中配置要传输的总的字节数。% F: T) r( [8 \0 o' c! [/ z9 N
- 4. 在DMA寄存器上配置通道优先级。' N9 T1 @+ p2 B0 j/ O. |5 o
- 5. 根据应用程序的要求,配置在传输完成一半还是全部完成时产生DMA中断。
3 S% ^, g" [( I$ \$ V/ M - 6. 在DMA寄存器上激活该通道。
复制代码 2 d8 H d4 i: O- _- H; X3 S6 l
检查代码,发现没问题,但是问题还是解决不了,就找了英文的参考手册(REV14),发现上面的步骤有了些修改: - 1. Write the USART_DR register address in the DMA control register to configure it as the
7 \1 a9 j. _( Q2 C X/ q - destination of the transfer. The data will be moved to this address from memory after
( o7 L3 U+ w# `) @, {7 o9 l - each TXE event.& |9 \$ q3 C2 N
- 2. Write the memory address in the DMA control register to configure it as the source of
1 I" u3 a1 p) ?) C' [ - the transfer. The data will be loaded into the USART_DR register from this memory2 M! x% G+ j$ X* H8 A" z
- area after each TXE event.
5 t, w& f5 g2 [ - 3. Configure the total number of bytes to be transferred to the DMA control register.
. R) d* b, O' H6 F - 4. Configure the channel priority in the DMA register
- K) @+ h1 f) `5 A( E) \ - 5. Configure DMA interrupt generation after half/ full transfer as required by the6 s( M7 C$ R0 }* @
- application.
! J" j" H% f' {- y, Y0 Z* X - 6. Clear the TC bit in the SR register by writing 0 to it.
# [0 ]: m+ I8 u: u- b" d - 7. Activate the channel in the DMA register.
. d* i+ q7 v* E- u+ ` - When the number of data transfers programmed
复制代码
) Z; o1 [" u# Q* W$ O y多了一步,即第6步,清除SR寄存器的TC标志位。照做,ok了。 ) \8 `7 Y$ M6 `" C+ r
重启DMA传输成功的条件:后来,又做了其他一些测试,发现即使不清除TC标志位,只要把对应DMA通道的TCIF标志(传输完成标志,DMA_ISR寄存器里面)清除,同样也可以正常重启传输。所以,要重启传输,保证在重新ENABLE DMA之前,满足以下两个条件之一即可: - DMA_ISR 对应的TCIF标志清除
- USART_SR 的TC标志位清除' h2 ]9 ?+ \* j# x
一个诡异的问题:发现即使上面提到的两个条件都不满足,但是下面的代码依然可以正常传输:
) C" g( n' u; m w - //ENBALE 之前USART1的TC,DMA1 对应USART TX的DMA通道TC标志都已经置位,没有清除4 ^/ P* i; P1 {' x. M: M' a0 Y5 C
- DMA_Cmd(USART1_Tx_DMA_Channel, ENABLE);
" u3 j9 d( m7 K2 h* F. U$ c - while((USART1->SR & USART_FLAG_TC) == 0x00);
复制代码 * ^ ]7 N2 d; I; @3 @0 R2 F! k( y
这样也可以。但是只要换成: - DMA_Cmd(USART1_Tx_DMA_Channel, ENABLE);2 E' m" T5 d1 S' L2 f) w& n/ I8 A
- 0 ?# P$ C6 o$ O) D
- /* Wait until USARTy TX DMA1 Channel Transfer Complete */ K5 O& z1 O5 d! }
- while (DMA_GetFlagStatus(USART1_Tx_DMA_FLAG ) == RESET) {
! `( \" s8 z' m* p' {. N - }
复制代码
: ~2 L! j$ h. ?0 r6 w就不行了。比较奇怪。 ) ? d. N, V( h. G
结论:不管那些诡异的问题了,以后重启DMA之前,清除串口的TC或者DMA的TC。不过根据使用的感觉,清除DMA的TC简单些。 ; @) J8 z" f( ~9 W1 P( x
|