打印
[STM32F1]

SPI FLASH 25Q 之16位SPI+DMA读写+模拟U盘

[复制链接]
3358|17
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
shipeng1989|  楼主 | 2020-3-19 22:23 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
本帖最后由 shipeng1989 于 2020-3-20 08:16 编辑

近来诸事不顺,做什么都磕磕绊绊,从来都没体验过一蹴而就的感觉。终于这个不走寻常路的我还是再次掉入泥坑。情况是这样的我想通过模拟U盘的方式直接向SPI FLASH写入一些相关的文件,如:字库和一些显示用的图片等。由于设置了2个盘符同时还可以通过U盘IAP升级固件岂不美哉。
奈何天公不作美,废了老大的劲才算搞定了IAP,接着又开始调试SPI的16位DMA读写.前后花了得有3天时间终于把SPI FLASH的16位DMA读写都调通了。在此说一下感想吧:DMA读SPI FLASH还是很简单的没什么特别要注意的地方,关键DMA写SPI FLASH有个地方一定要注意:在DMA传输完成后其实SPI并没有发完数据此时SPI正处于移位寄存器中正在往外发数据且寄存器DR中也还有一个数据待发,此时如果将CS脚拉高则会丢失这两个数据,并且W25Q也会丢弃前面发的数据而不会写入。解决方案是在DMA传输完成后增加SPI的两个标志位判断:
        while((SPI3->SR & SPI_I2S_FLAG_TXE) == (uint16_t)RESET || (SPI3->SR & SPI_I2S_FLAG_BSY) != (uint16_t)RESET){}
如此便能保证SPI数据的完整传输。
      经过单独测试后发现W25QXX FLASH已经可以正常读写了,于是接入USB模拟U盘无奈新的问题又出现了,单片机对W25QXX FLASH格式化后电脑可以正常识别但却不能通过电脑写入否则格式化的内容都会被破坏变成不能识别了,我有用逻辑分析仪看过电脑写入的数据发现完全是一堆乱码找不到任何规律(可能我的装备不太行逻辑分析仪是100块钱的翻新货数据量一大就装死,而且抓电脑读写U盘的数据很凌乱,光修改个卷名的数据都多到看不过来),但奇怪的是如果采用轮询的8位SPI模式则电脑读写都正常模拟U盘可以正常读写。崩溃中,求高人指点。 Udisk.zip (538.91 KB)

使用特权

评论回复
沙发
21ic小喇叭| | 2020-3-20 09:46 | 只看该作者
楼主把自己的问题描述的比较清晰,还请论坛上各位用户,有相关经验的朋友,看到本帖给予楼主帮助~~ 谢谢大家!

使用特权

评论回复
板凳
21ic小喇叭| | 2020-3-20 09:47 | 只看该作者
谢谢楼主分享问题,虽然问题未被解答,您可以@ST的版主来看看您的问题,奖励您10家园币,这是我们新上线的产品,您可以用家园币兑换礼品~

使用特权

评论回复
地板
WoodData| | 2020-3-20 10:38 | 只看该作者
既然Flash写入有问题,那你在Flash写入函数那打印一下写入的数据和地址看看。

使用特权

评论回复
5
taobaofarmer| | 2020-3-20 16:28 | 只看该作者
while((SPI3->SR & SPI_I2S_FLAG_TXE) == (uint16_t)RESET || (SPI3->SR & SPI_I2S_FLAG_BSY) != (uint16_t)RESET){}
这句话让DMA方式失去了意义

使用特权

评论回复
6
sonicll| | 2020-3-20 16:49 | 只看该作者
taobaofarmer 发表于 2020-3-20 16:28
while((SPI3->SR & SPI_I2S_FLAG_TXE) == (uint16_t)RESET || (SPI3->SR & SPI_I2S_FLAG_BSY) != (uint16_t ...

楼主是在DMA传输完成后才判断这个发送标志的,又不是每个字节都判断一次,这样做没什么问题吧

使用特权

评论回复
7
shipeng1989|  楼主 | 2020-3-20 18:02 | 只看该作者
21ic小喇叭 发表于 2020-3-20 09:47
谢谢楼主分享问题,虽然问题未被解答,您可以@ST的版主来看看您的问题,奖励您10家园币,这是我们新上线的 ...

感谢!

使用特权

评论回复
8
shipeng1989|  楼主 | 2020-3-20 18:10 | 只看该作者
WoodData 发表于 2020-3-20 10:38
既然Flash写入有问题,那你在Flash写入函数那打印一下写入的数据和地址看看。 ...

数据量太大估计不好排查,我有用逻辑分析仪看过发现发出来的数据完全乱了套了,我怀疑可能是硬件有问题但是把波特率降到256分频也还是不行,真是见鬼了读写程序单独拿出来测试都是没问题的没想到居然会在USB上翻船,完全说不通的啊

使用特权

评论回复
9
WoodData| | 2020-3-20 19:42 | 只看该作者
shipeng1989 发表于 2020-3-20 18:10
数据量太大估计不好排查,我有用逻辑分析仪看过发现发出来的数据完全乱了套了,我怀疑可能是硬件有问题但 ...

可以只打印前面几个数据啊,关键是写入地址位置。

使用特权

评论回复
10
shipeng1989|  楼主 | 2020-3-21 20:20 | 只看该作者
本帖最后由 shipeng1989 于 2020-3-22 17:21 编辑

跟大家说一声25Q系列FLASH的16位SPI读写今天总算调通了,感觉对模拟U盘的读写速度有所提升。最主要的好处是可以通过DMA直接将数据传给16位FSMC的LCD这是最让我兴奋的地方显示速度一下子提升数倍,并且连读写容器的缓存都不用了,还可以结合文件系统直接打开图片文件再也不用做内存搬运工了,这里其实有个问题:f_read在读文件的时候缓存指针一直在增加而此时如果指向我们的LCD写数据地址则指向的就不再是一个固定值,但只要每次读数据的地址范围不会太大让RS脚电平跳变就不会有问题,拿我的程序来说我用的是FSMC_A16脚做LCD的RS信号,定义LCD_REG为(0x60000000|0x1FFFE),LCD_RAM范围为0x60020000~0x6002FFFF或0x60000000~0x6000FFFF都可以正常显示。下面我把这段神奇的代码分享给大家:

void PrintPictureFile(uint16_t x1,uint16_t y1,uint16_t x2,uint16_t y2,const TCHAR* path)
{
        FIL fil;
        if (FR_OK==f_open(&fil,path,FA_READ|FA_OPEN_EXISTING))
        {
                UINT rbs=1,desire_size=(x2-x1)*(y2-y1)*2;
                LCD_SetWindows(x1,y1,x2-1,y2-1);
                for (uint32_t i=0;rbs!=0 && i<desire_size;i+=rbs)
                {
                        f_read(&fil,&LCD->LCD_RAM,FF_MAX_SS,&rbs);//簇最好设置大一点让单次读取的数据更多,
                }                                                                           //以减少循环寻址次数。我设的簇是4096,
                f_close(&fil);                                      //扇区不要用512写文件会很慢改成4096速度可以提升好几倍
        }
        else Print8x16String(x1,y1,RED,BLACK,"Error!");
}


美中不足的是写FLASH还是没能用上DMA原因不详,不知道这算不算是一种强迫症这个小遗憾曾让我寝食难安。下面我将贴上相关代码忘路过的大神帮忙分析一下:

void SFLASH_WritePage(uint16_t* pBuffer, uint32_t WriteAddr,uint16_t len)
{
        SFLASH_WriteEnable();len >>= 1;
        SPI3_CS_ENABLE;
        SPI3_WriteByte(SFLASH_WRITE_PAGE);
        SPI3_WriteByte((uint8_t)(WriteAddr>>16));
        SPI3_WriteByte((uint8_t)(WriteAddr>>8));
        SPI3_WriteByte((uint8_t)WriteAddr);
        DISABLE_SPI3;
        SPI3->CR1 |= SPI_DataSize_16b;
        ENABLE_SPI3;
        for(uint16_t i=0;i<len;i++)
        {
                while((SPI3->SR & SPI_I2S_FLAG_TXE) == RESET);
                SPI3->DR = *((u16*)pBuffer+i);
        }
        //下面是DMA方式被我注释了,否则就会写入失败。但是我移植FATFS调用格式化函数“f_mkfs”采用下面的DMA写入又可以成功格式化,并且插上电脑后可以正常识别U盘。实在让人摸不着头脑了。
        /* Write FLASH Page Use DMA:Start*/                                                                                                                                                                                           /*SPI3->CR2 |= SPI_I2S_DMAReq_Tx;
        DMA2_Channel2->CCR &= ~(1<<0);
        DMA2->IFCR |= (1<<5);
        DMA2_Channel2->CMAR = (uint32_t)pBuffer;
        DMA2_Channel2->CNDTR = 128;
        DMA2_Channel2->CCR |= (1<<0);
        ENABLE_SPI3;
        while((DMA2->ISR&(1<<5))==RESET){}
        DMA2_Channel2->CCR &= ~(1<<0);*/                                                                                                                                                                                                      /* Write FLASH Page Use DMA:End*/

        while((SPI3->SR & SPI_I2S_FLAG_TXE) == RESET
                        || (SPI3->SR & SPI_I2S_FLAG_BSY) != RESET){}
        
        SPI3_CS_DISABLE;
        ResetSPI3DataSize8b();
        SFLASH_WaitForNoBusy();
}
现在是只要采用了上面的DMA插上电脑后读可以只要有修改当时貌似都没问题,但你拔掉USB重新插入就会提示格式化了,并且你格式化了也会提示无法格式化。跪求高人指点迷津@香水城


使用特权

评论回复
11
shipeng1989|  楼主 | 2020-3-22 17:31 | 只看该作者
对了为了让大家可以直观的查找我的软件问题,再贴上我的SPI DMA初始化代码吧:
void SPI3_Configuration(void)
{
SPI_InitTypeDef  SPI_InitStructure;
GPIO_InitTypeDef GPIO_InitStructure;

RCC_PCLK1Config(RCC_HCLK_Div2);  //36M
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_5;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_Init(GPIOB, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_Init(GPIOB, &GPIO_InitStructure);
/* SPI: CSÍÆÍìÊä³ö */
GPIO_SetBits(GPIOB,GPIO_Pin_0);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_Init(GPIOB, &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;                       //Êý¾Ý²¶»ñÓÚµÚ¶þ¸öʱÖÓÑØ
SPI_InitStructure.SPI_NSS = SPI_NSS_Soft;                          //Èí¼þ¿ØÖÆ NSS ÐźÅ
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);
SPI_Cmd(SPI3, ENABLE);
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)FPW_Cache.u8Arry;
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;
DMA_InitStructure.DMA_BufferSize = 0;
DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;//Byte
DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord;//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, DISABLE);

// 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 = (uint32_t)FPW_Cache.u8Arry;
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST;
DMA_InitStructure.DMA_BufferSize = 0;
DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;//Byte
DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord;//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_Channel2, &DMA_InitStructure);
DMA_ITConfig(DMA2_Channel2, DMA_IT_TC, DISABLE);
DMA_ITConfig(DMA2_Channel2, DMA_IT_TE, DISABLE);

/* Disable SPI3 DMA TX request */
SPI_I2S_DMACmd(SPI3, SPI_I2S_DMAReq_Tx, ENABLE);
DMA_Cmd (DMA2_Channel2,DISABLE);
}
再附上完整代码,有需要的可以为你所用 Udisk.zip (534.12 KB)

使用特权

评论回复
12
香水城| | 2020-3-23 16:44 | 只看该作者
本帖最后由 香水城 于 2020-3-23 17:08 编辑

看样子,问题主要卡在Flash读写操作的DMA传输了。

我看了下,你下面这句代码会有问题,它会影响DMA的触发及传输。

/* Write FLASH Page Use DMA:Start*/                                                                                                                                                                                           /*SPI3->CR2 |= SPI_I2S_DMAReq_Tx;
         DMA2_Channel2->CCR &= ~(1<<0);
         DMA2->IFCR |= (1<<5);
         DMA2_Channel2->CMAR = (uint32_t)pBuffer;
         DMA2_Channel2->CNDTR = 128;
         DMA2_Channel2->CCR |= (1<<0);
         ENABLE_SPI3;
         while((DMA2->ISR&(1<<5))==RESET){}
         DMA2_Channel2->CCR &= ~(1<<0);*/                                                                                                                                                                                                      /* Write FLASH Page Use DMA:End*/

         while((SPI3->SR & SPI_I2S_FLAG_TXE) == RESET || (SPI3->SR & SPI_I2S_FLAG_BSY) != RESET){}
         
         SPI3_CS_DISABLE;
         ResetSPI3DataSize8b();
         SFLASH_WaitForNoBusy();


你既然开启DMA传输,就不要这样操作了。建议你开启传输完成中断,在完成中断里做SPI传输完成的检查确认
之后再做SPI3_CS_DISABLE;等后续操作。

使用特权

评论回复
13
shipeng1989|  楼主 | 2020-3-23 17:52 | 只看该作者
香水城 发表于 2020-3-23 16:44
看样子,问题主要卡在Flash读写操作的DMA传输了。

我看了下,你下面这句代码会有问题,它会影响DMA的触发 ...

感谢大神指点,我试试先

使用特权

评论回复
14
八层楼| | 2020-4-6 18:25 | 只看该作者
非常感谢楼主分享

使用特权

评论回复
15
观海| | 2020-4-6 18:25 | 只看该作者
非常感谢楼主分享

使用特权

评论回复
16
guanjiaer| | 2020-4-6 18:25 | 只看该作者
非常感谢楼主分享

使用特权

评论回复
17
heimaojingzhang| | 2020-4-6 18:25 | 只看该作者
非常感谢楼主分享

使用特权

评论回复
18
keaibukelian| | 2020-4-6 18:26 | 只看该作者
非常感谢楼主分享

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

29

主题

134

帖子

1

粉丝