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

奇怪的DMA写FLASH数据异常

[复制链接]
歹徒兄弟 提问时间: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个回答
歹徒兄弟 回答时间: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管理
行使您的权利
官方最新发布
STM32Cube扩展软件包
意法半导体边缘AI套件
ST - 理想汽车豪华SUV案例
ST意法半导体智能家居案例
STM32 ARM Cortex 32位微控制器
关注我们
st-img 微信公众号
st-img 手机版