你的浏览器版本过低,可能导致网站不能正常访问!
为了你能正常使用网站功能,请使用这些浏览器。

奇怪的DMA写FLASH数据异常

[复制链接]
shipeng1989 提问时间:2019-6-14 17:10 /
这几天都在调试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_InitTypeDef  SPI_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();                        //等待空闲(等待写入结束)
}


收藏 评论2 发布时间:2019-6-14 17:10

举报

2个回答
shipeng1989 回答时间:2019-6-15 08:35:49
原因找到了,是因为在DMA_Config(void)函数中采用了循环模式"DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;",这样在DMA传输完成中断后会多发送两个字节的数据,虽然接下来关闭了DMA通道,但下次再启动DMA时不会从0地址开始发而是接着前面的2个字节往后发。解决办法就是采用DMA_Mode_Normal模式每次发送前初始化DMA传输计数寄存器DMA2_Channel2->CNDTR = 256 ;

评分

参与人数 1蝴蝶豆 +2 收起 理由
STMCU + 2 自给自足

查看全部评分

五哥1 回答时间:2019-6-15 12:19:17
谢谢分享

所属标签

相似问题

关于
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
意法半导体官网
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
官方最新发布
STM32N6 AI生态系统
STM32MCU,MPU高性能GUI
ST ACEPACK电源模块
意法半导体生物传感器
STM32Cube扩展软件包
关注我们
微信公众号二维码 微信公众号
手机版二维码 手机版