• STM32学习笔记:驱动SPI外设读写FLASH


    目录

    1、SPI驱动FLASH芯片:思路

    2、SPI发送数据和接收数据函数

    3、查看SPI传输状态

    4、FLASH芯片简介

    5、代码

    5.1 初始化SPI

    5.2 使用SPI发送一个字节,返回收到的数据

    5.3 从FLASH指定地址读出指定长度的数据

    5.4 从FLASH指定地址写入指定长度的数据

    5.5 通过SPI外设给FLASH写数——主函数


    1、SPI驱动FLASH芯片:思路

    (1)驱动GPIO及端口的时钟
    (2)是能SPI外设的时钟
    (3)配置SPI外设的模式、地址、速率等参数并使能SPI外设
    (4)编写基本SPI按字节收发的函数
    (5)编写对FLASH擦除及读写操作的函数

    (6)编写main()函数

    2、SPI发送数据和接收数据函数

    void SPI_I2S_SendData(SPI_TypeDef* SPIx,u16 Data)
    u16 SPI_I2S_ReceiveData(SPI_TypeDef* SPIx)

    3、查看SPI传输状态

    /* 等待发送区为空,TXE */
    /* 等待接收缓存区非空,RXNE*/

    SPI_I2S_GetFlagStatus(SPI1,SPI_I2S_FLAG_RXNE)

    4、FLASH芯片简介

    W25Q64:将8M容量分为128个块(Block),
    每个块64K字节,每个块分为16个扇区(Sector),每个扇区4K字节
    W25Q64的最小擦除单位为一个扇区,每次必须擦除4K个字节
    因此需要给W25Q24开辟一个4K缓存区;
    W25Q64:四线SPI,最大时钟80MHz。

    5、代码

    5.1 初始化SPI

    1. void SPI_Flash_Init(void)
    2. {
    3.     GPIO_InitTypeDef GPIO_InitStructure;
    4.     RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE );
    5.     RCC_APB1PeriphClockCmd(RCC_APB1Periph_SPI1, ENABLE );
    6.     GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5|GPIO_Pin_6|GPIO_Pin_7;
    7.     GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //PA5,6,7 复用推挽输出
    8.     GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    9.     GPIO_Init(GPIOA,&GPIO_InitStructure);//初始化串口A
    10.     GPIO_SetBits(GPIOA,GPIO_Pin_5|GPIO_Pin_6|GPIO_Pin_7);//初始化GPIO
    11.     
    12.     SPI_InitTypeDef SPI_InitStructure;
    13.     SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex; //双线双向全双工
    14.     SPI_InitStructure.SPI_Mode = SPI_Mode_Master; //主 SPI
    15.     SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b; // SPI 发送接收 8 位帧结构
    16.     SPI_InitStructure.SPI_CPOL = SPI_CPOL_High;//串行同步时钟的空闲状态为高电平
    17.     SPI_InitStructure.SPI_CPHA = SPI_CPHA_2Edge;//第二个跳变沿数据被采样
    18.     SPI_InitStructure.SPI_NSS = SPI_NSS_Soft; //NSS 信号由软件控制
    19.     SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_256; //预分频 256
    20.     SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB; //数据传输从 MSB 位开始
    21.     SPI_InitStructure.SPI_CRCPolynomial = 7; //CRC 值计算的多项式
    22.     SPI_Init(SPI1, &SPI_InitStructure); //根据指定的参数初始化外设 SPIx 寄存器
    23.     
    24.     SPI_Cmd(SPI1,ENABLE); //使能SPI外设
    25.     
    26.     SPI1_ReadWriteByte(0xff);
    27. }

    5.2 使用SPI发送一个字节,返回收到的数据

    /* TxData:发送的数  */

    /* u8返回值:读取到的字节 */

    1. u8 SPI1_ReadWriteByte(u8 TxData)
    2. {
    3. u8 retry=0;
    4. /* 发送缓存为空 */
    5. while(SPI_I2S_GetFlagStatus(SPI,SPI_I2S_FLAG_TXE)==RESET)
    6. {
    7. retry++;
    8. if(retry>200)return0;
    9. };
    10. SPI_I2S_SendData(SPI1,TxData);//通过外设SPI发送一个数据
    11. retry=0;
    12. /* 接收缓存为非空 */
    13. while(SPI_I2S_GetFlagStatus(SPI,SPI_I2S_FLAG_RXNE)==RESET)
    14. {
    15. retry++;
    16. if(retry>200)return0;
    17. };
    18. return SPI1_ReceiveData(SPI1);//返回SPI1收到的数据
    19. }

    5.3 从FLASH指定地址读出指定长度的数据

    /* 读取指定长度的数据*/
    /* pbuffer:数据存储区*/
    /* ReadAddr:开始读取的地址(24bit)*/
    /* NumByteToRead:要读取的字节数 (最大65535)*/

    1. void SPI_Flash_Read(u8* pbuffer,u32 ReadAddr,u16 NumByteToRead)
    2. {
    3. u16 i;
    4. SPI1_FLASH_CS = 0; //开始CS使能
    5. SPI1_ReadWriteByte(W25X_ReadData);//发送读取命令
    6. SPI1_ReadWriteByte((u8)((ReadAddr)>>16)); //发送 24bit 地址
    7. SPI1_ReadWriteByte((u8)((ReadAddr)>>8));
    8. SPI1_ReadWriteByte((u8)ReadAddr);
    9. for(i=0;i
    10. {
    11. pbuffer[i] = SPI1_ReadWriteByte(0XFF);
    12. }
    13. SPI1_FLASH_CS = 1; //取消CS使能
    14. }

    5.4 从FLASH指定地址写入指定长度的数据

    1. void SPI_Flash_Write(u8* pbuffer,u32 WriteAddr,u16 NumByteToRead)
    2. {
    3. u8 SPI_FLASH_BUFFER[4096];
    4. u32 secpos; u16 secoff;
    5. u16 secremain; u16 i;
    6. secpos=WriteAddr/4096; //扇区地址
    7. secoff=WriteAddr%4096; //在扇区内的偏移
    8. secremain=4096-secoff; //扇区剩余空间大小
    9. if(NumBytetoWrite<=secremain)
    10. {
    11. secremain = NumBytetoWrite;
    12. }
    13. while(1)
    14. {
    15. SPI_Flash_Read(SPI_FLASH_BUF,secpos*4096,4096);//读出整个扇区的内容
    16. for(i=0;i//校验数据
    17. {
    18. if(SPI_FLASH_BUF[secoff+i]!=0XFF)break;//需要擦除
    19. }
    20. if(i//需要擦除
    21. {
    22. SPI_Flash_Erase_Sector(secpos);//擦除这个扇区
    23. for(i=0;i//复制
    24. SPI_Flash_Write_NoCheck(SPI_FLASH_BUF,secpos*4096,4096);//写整个扇区
    25. }else SPI_Flash_Write_NoCheck(pBuffer,WriteAddr,secremain);//写已经擦除了的
    26. if(NumByteToWrite==secremain)break;//写入结束了
    27. else//写入未结束
    28. {
    29. secpos++; //扇区地址增 1
    30. secoff=0; //偏移位置为 0
    31. pBuffer+=secremain; //指针偏移
    32. WriteAddr+=secremain; //写地址偏移
    33. NumByteToWrite-=secremain; //字节数递减
    34. if(NumByteToWrite>4096)secremain=4096;//下一个扇区还是写不完
    35. else secremain=NumByteToWrite; //下一个扇区可以写完了
    36. }
    37. };
    38. }

    5.5 通过SPI外设给FLASH写数——主函数

    1. int main(void)
    2. {
    3. const u8 TEXT_Buffer[] = {"WarShipSTM32 SPI TEST"};
    4. u8 key;
    5. u16 i=0;
    6. u8 datatemp[size];
    7. u32 FLASH_SIZE;
    8. delay_init();
    9. SPI_Flash_Init();
    10. while(1)
    11. {
    12. if(key == WKUP_PRES) //WK_UP按键按下,写入FLASH
    13. {
    14. SPI_Flash_Write((u8*)TEXT_Buffer,FLASH_SIZE-100,SIZE);
    15. }
    16. if(key == KEY0_PRES) //key0按下,读取字符串并显示
    17. {
    18. SPI_Flash_Read(datatemp,FLASH_SIZE-100,SIZE);//从指定地址读 SIZE 字节
    19. }
    20. i++;
    21. delay_ms(10);
    22. }
    23. }
  • 相关阅读:
    【数据结构】栈和队列
    多浦乐IPO过会:预计全年营收2亿 拟募资4.9亿
    分享一个完全免费的GPT4站点,gpts也可以用
    爬虫及浏览器开发者工具
    【云原生 Kubernetes】认识 k8s、k8s 架构、核心概念点介绍
    微信小程序实现一些优惠券/卡券
    【Kubernetes】k8s集群资源调度
    go语言|数据结构:二叉树(2)广度和深度搜索
    各种网络协议在设计目的、工作方式、应用场景等方面存在显著的区别
    Vmware虚拟机 linux 固定IP
  • 原文地址:https://blog.csdn.net/lurong66/article/details/126800925