奇怪的DMA写FLASH数据异常
这几天都在调试DMA写入W25Q16,每次写1页256字节数据,现在遇到2个字节数据错位的问题,比如从1到256个字节依次存数值1,2,3...256,结果却成了3,4,5...256,1,2。我怀疑问题可能和DMA发送完成后的SPI善后处理有关,我的做法是DMA发送完成后进入DMA传输完成中断,中断中清除SPI的TXE中断请求后打开TXE中断,清除DMA中断请求关闭该DMA通道。 在SPI中断中判断:1.如果是TXE中断:清除TXE中断请求,关闭TXE中断,清除RXNE中断请求后打开RXNE中断;
2.如果如果是RXNE中断:清除RXNE中断请求,关闭RXNE中断,置高片选信号退出中断处理。
代码如下:
/************************************************
函数名称 : SPI_Configuration
功 能 : SPI配置
SPI为主模式,时钟线平时为低,上升沿采集数据
8位数据格式,软件控制片选,数据高位在前
参 数 : 无
返 回 值 : 无
作 者 : ShiPeng
*************************************************/
void SPI3_Configuration(void)
{
SPI_InitTypeDefSPI_InitStructure;
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB1PeriphClockCmd(RCC_APB1Periph_SPI3, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB | RCC_APB2Periph_AFIO, ENABLE);
GPIO_PinRemapConfig(GPIO_Remap_SWJ_JTAGDisable, ENABLE);//JTAG Disable SWDP Enable
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3|GPIO_Pin_4| GPIO_Pin_5;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_Init(GPIOB, &GPIO_InitStructure);
/* SPI: CS推挽输出 */
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_15;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
#ifdef SPI3_NSS_HARD
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
#else
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
#endif
GPIO_Init(GPIOA, &GPIO_InitStructure);
/* SPI 初始化定义 */
SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex; //SPI设置为双线双向全双工
SPI_InitStructure.SPI_Mode = SPI_Mode_Master; //设置为主 SPI
SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b; //SPI发送接收 8 位帧结构
SPI_InitStructure.SPI_CPOL = SPI_CPOL_High; //时钟悬空高
SPI_InitStructure.SPI_CPHA = SPI_CPHA_2Edge; //数据捕获于第二个时钟沿
#ifdef SPI3_NSS_HARD
SPI_InitStructure.SPI_NSS = SPI_NSS_Hard; //硬件控制 NSS 信号
#else
SPI_InitStructure.SPI_NSS = SPI_NSS_Soft; //软件控制 NSS 信号
#endif
SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_2; //波特率预分频值为2
SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB; //数据传输从 MSB 位开始
SPI_InitStructure.SPI_CRCPolynomial = 7; //定义了用于 CRC值计算的多项式
SPI_Init(SPI3, &SPI_InitStructure);
#ifdef SPI3_NSS_HARD
SPI_SSOutputCmd(SPI3, ENABLE);
SPI_Cmd(SPI3, DISABLE);
#else
SPI_Cmd(SPI3, ENABLE);
#endif
DMA_Config();
}
/************************************************
函数名称 : DMA_Config
功 能 : DMA配置
参 数 : 无
返 回 值 : 无
作 者 : ShiPeng
*************************************************/
void DMA_Config(void)
{
#define SPI3_DR_Addr ((u32)0x40003C0C)
DMA_InitTypeDef DMA_InitStructure;
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA2, ENABLE);
/*
DMA_DeInit(DMA2_Channel1);
DMA_InitStructure.DMA_PeripheralBaseAddr = SPI3_DR_Addr;
DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)Rx_Buffer;
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;
DMA_InitStructure.DMA_BufferSize = 256;
DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;
DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;
DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;
DMA_InitStructure.DMA_Priority = DMA_Priority_VeryHigh;
DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;
DMA_Init(DMA2_Channel1, &DMA_InitStructure);
DMA_ITConfig(DMA2_Channel1, DMA_IT_TC, ENABLE);
// Disable SPI3 DMA RX request //
SPI_I2S_DMACmd(SPI3, SPI_I2S_DMAReq_Rx, ENABLE);
DMA_Cmd (DMA2_Channel1,DISABLE);
*/
DMA_DeInit(DMA2_Channel2);
DMA_InitStructure.DMA_PeripheralBaseAddr = SPI3_DR_Addr;
DMA_InitStructure.DMA_MemoryBaseAddr = (u32)Tx_Buffer;
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST;
DMA_InitStructure.DMA_BufferSize = 256;
DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;
DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;
DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;
DMA_InitStructure.DMA_Priority = DMA_Priority_Medium;
DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;
DMA_Init(DMA2_Channel2, &DMA_InitStructure);
DMA_ITConfig(DMA2_Channel2, DMA_IT_TC, ENABLE);
DMA_ITConfig(DMA2_Channel2, DMA_IT_TE, ENABLE);
/* Disable SPI3 DMA TX request */
SPI_I2S_DMACmd(SPI3, SPI_I2S_DMAReq_Tx, ENABLE);
DMA_Cmd (DMA2_Channel2,DISABLE);
}
/************************************************
函数名称 : SPI_WriteByte
功 能 : SPI写一字节数据
参 数 : TxData --- 发送的字节数据
返 回 值 : 读回来的字节数据
作 者 :
*************************************************/
uint8_t SPI3_WriteByte(uint8_t TxData)
{
while((SPI3->SR & SPI_I2S_FLAG_TXE) == (uint16_t)RESET);
SPI3->DR = TxData;
while((SPI3->SR & SPI_I2S_FLAG_RXNE) == (uint16_t)RESET);
return SPI3->DR;
}
/*******************************************************************************
* Function Name: DMA2Channel2_IRQHandler
* Description : This function handles DMA Stream 1 interrupt request.
* Input : None
* Output : None
* Return : None
*******************************************************************************/
void DMA2_Channel2_IRQHandler(void)
{
/* Test on DMA Channel1 Transfer Complete interrupt */
if(DMA_GetITStatus(DMA2_IT_TC2)) //DMA2_IT_TC1:Transfer Completed
{
/* Get Current Data Counter value after complete transfer */
//u16 buf16= DMA_GetCurrDataCounter(DMA2_Channel2);
DMA_Cmd (DMA2_Channel2,DISABLE);//SPI_I2S_DMACmd(SPI3, SPI_I2S_DMAReq_Tx, DISABLE);
//if (0==buf16){}
SPI_I2S_ClearITPendingBit(SPI3,SPI_I2S_IT_TXE);
SPI_I2S_ITConfig(SPI3, SPI_I2S_IT_TXE, ENABLE);
/* Clear DMA Channel1 Half Transfer, Transfer Complete and Global interrupt pending bits */
DMA_ClearITPendingBit(DMA2_IT_GL2);
}
if(DMA_GetITStatus(DMA2_IT_TE2))
{
SPI3_CS_DISABLE;
DMA_Config();
DMA_ClearITPendingBit(DMA2_IT_TE2);
}
}
void SPI3_IRQHandler(void)
{
if (SPI_I2S_GetITStatus(SPI3,SPI_I2S_IT_TXE))
{
SPI_I2S_ClearITPendingBit(SPI3,SPI_I2S_IT_TXE);
SPI_I2S_ITConfig(SPI3, SPI_I2S_IT_TXE, DISABLE);
SPI_I2S_ClearITPendingBit(SPI3,SPI_I2S_IT_RXNE);
SPI_I2S_ITConfig(SPI3, SPI_I2S_IT_RXNE, ENABLE);
}
if (SPI_I2S_GetITStatus(SPI3,SPI_I2S_IT_RXNE))
{
SPI3_CS_DISABLE;
SPI_I2S_ClearITPendingBit(SPI3,SPI_I2S_IT_RXNE);
SPI_I2S_ITConfig(SPI3, SPI_I2S_IT_RXNE, DISABLE);
}
}
void WriteFLASHPageUseDMA(uint32_t faddr)
{
SFLASH_WaitForNoBusy();
SFLASH_WriteEnable();
SPI3_CS_ENABLE;
SPI3_WriteByte(SFLASH_WRITE_PAGE); //《页编程》指令
SPI3_WriteByte((uint8_t)(faddr>>16)); //发送24bit地址
SPI3_WriteByte((uint8_t)(faddr>>8));
SPI3_WriteByte(0);
DMA_Cmd (DMA2_Channel2,ENABLE);
}
如果放弃DMA采用轮询写入就不会有此问题:
void SFLASH_WritePage(uint8_t* pBuffer, uint32_t WriteAddr, uint16_t nByte)
{
SFLASH_WriteEnable(); //写使能
SPI3_CS_ENABLE; //使能器件
SPI3_WriteByte(SFLASH_WRITE_PAGE); //《页编程》指令
SPI3_WriteByte((uint8_t)((WriteAddr)>>16)); //发送24bit地址
SPI3_WriteByte((uint8_t)((WriteAddr)>>8));
SPI3_WriteByte((uint8_t)WriteAddr);
while (nByte--)
{
SPI3_WriteByte(*pBuffer);
pBuffer++;
}
SPI3_CS_DISABLE;
SFLASH_WaitForNoBusy(); //等待空闲(等待写入结束)
}
原因找到了,是因为在DMA_Config(void)函数中采用了循环模式"DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;",这样在DMA传输完成中断后会多发送两个字节的数据,虽然接下来关闭了DMA通道,但下次再启动DMA时不会从0地址开始发而是接着前面的2个字节往后发。解决办法就是采用DMA_Mode_Normal模式每次发送前初始化DMA传输计数寄存器DMA2_Channel2->CNDTR = 256 ; 谢谢分享
页:
[1]