知识点

1. 理解SPI总线原理
2. 强化按时序图编程
3. 掌握FLASH

SPI原理

  1. SPI(Serial Peripheral Interface)串行外设接口,是Motorola公司推出的一种同步串行接口技术。具体高速、全双工、同步的特点。总线本身并没有提供流控、应答确认和校验机制,需要特别注意。

  2. 如图,SPI是主从型总线有且只有一个主设备,可以有1个或多个从设备

  3. SPI至少需要4根线,分别是

    1. SCK 时钟信号,由主设备产生
    2. MOSI (Master Out Slave In) 主设备输出,从设备输入线
    3. MISO (Master In Slave Out) 主设备输入,从设备输出线
    4. CS(可以有多根) 片选信号线,用于控制跟哪个从设备通信
  4. 如图,因SPI同步双工串行的特性,其内部原理非常简洁

  5. 从上面,可以理解SPI的收发其实就是在时钟信号下采样过程,那么我们是在时钟的上升沿、下降沿、高电平、低电平采样呢?SPI按时钟极性(CPOL)和时钟相位(CPHA)分成4种模式用于控制时钟采样,通信双方模式必须一致,具体如下

    1. 模式 CPOL CPHA
      Mode0 0 0
      Mode1 0 1
      Mode2 1 0
      Mode3 1 1
    2. 时钟极性CPOL是用来配置SCLK的电平出于哪种状态时是空闲态或者有效态,时钟相位CPHA
      是用来配置在第几个边沿采样数据。
      CPOL=0,表示当SCLK=0时处于空闲态,所以有效状态就是SCLK处于高电平时
      CPOL=1,表示当SCLK=1时处于空闲态,所以有效状态就是SCLK处于低电平时
      CPHA=0,表示数据采样是在第1个边沿,数据发送在第2个边沿
      CPHA=1,表示数据采样是在第2个边沿,数据发送在第1个边沿

    3. 主机和从机的发送数据是同时完成的,两者的接收数据也是同时完成的。所以为了保证主从机正确通信,应使得它们的SPI具有相同的时钟极性和时钟相位。

    4. SPI协议并没有规定数据一定是8位的,也可以是16位的,要看通信双方支持哪种。

    5. 传输时是高位先传还是低位先传,协议也没强制要求,也要看双方支持。

  6. 至此,SPI协议部分已经全部介绍完了,可见SPI非常的简洁高效,且弹性很大,需要结合具体的应用配置。

FLASH芯片手册导读

​ GD25Q40芯片是一款Nor FLash,Nor Flash的特点是以‘块’为操作的最小擦写单元,字节为最小读取单元。例如,该款芯片有512KByte=4094Kbit=4MBit,每页大小256Byte,每页就是Nand Flash的最下操作‘块’,具体如图

​ 而且需要遵循先擦后写的操作。flash进行写操作时,只能将相应的位由1变0,而擦除才能把块内所有位由0变1。所有写入数据时,如果该页已经存在数据,必须先擦除再写。

​ 既然Flash有如此特性,芯片是如何管理该芯片的呢?如下图,芯片通过一个命令和逻辑控制器与真正的存储空间进行交互,也可以这么理解,外部MCU要通过SPI总线,向FLASH芯片发送各式各样的命令来控制芯片的读写擦操作。

​ 具体命令如下,命令很多,初看有点眼花,可以先看我标红的几个命令,可以看出,命令无非是写使能/去使能,读芯片状态,读数据,写数据,擦除命令。再仔细看其他命令也无法这几类,只是在不同模式的下操作罢了。注:此处提到的模式时芯片提供的,例如快速读写模式等等,有兴趣可阅读芯片手册,我选的是标准模式。

最后,只要安装命令的时序要求写成代码就可以了,具体时序在下面说。

通过SPI读写FLASH

​ 用SPI总线读写FLASH芯片是非常常见的应用

硬件设计

软件设计

​ GD32是支持SPI控制器的,为了加深对SPI协议的理解,我会软件模拟一个SPI,也会用SPI控制器来实现一个,毕竟在实际应用中多少用硬件SPI控制器的。

软件GPIO模拟SPI

​ 从手册里可以查到,支持mode 0和3,此处我选mode 0,即CPOL和CPHA都为0。

注意,时序参数要求必须按照手册描述写,如下

参考上表,我把延时时间固定5us,这样代码会简单很多,且满足了上述ns级最小延时的要求,虽然有几项有最大时间要求,却不影响我们编码。代码如下:

U8 DRV_SPI_SwapByte(IN U8 byte)
{U8 i = 0;U8 inDate = byte;U8 outBit = 0;U8 outDate = 0;/* SCKPL = 0; SCKPH = 0 */for (i = 0; i < 8; i++){if (inDate & 0x80){SPI_MOSI_HIGH;}else{SPI_MOSI_LOW;}SPI_Delay(5);SPI_SCK_HIGH;outBit = SPI_MISO_READ;if (outBit){outDate |= 0x1;}SPI_Delay(5);SPI_SCK_LOW;SPI_Delay(5);inDate <<= 1;if (i <7){outDate <<= 1;}}return outDate;
}
硬件SPI

​ 详细阅读SPI配置部分代码,与前面原理部分说的完全一致,且参数GD32手册里也有详细说明。

U8 DRV_SPI_SwapByte(IN U8 byte)
{while (SPI_I2S_GetBitState(SPI1, SPI_FLAG_TBE) == RESET);SPI_I2S_SendData(SPI1, byte);while (SPI_I2S_GetBitState(SPI1, SPI_FLAG_RBNE) == RESET);return SPI_I2S_ReceiveData(SPI1);
}static VOID SPI_Configuration(VOID)
{SPI_InitPara  SPI_InitStructure;RCC_APB2PeriphClock_Enable(RCC_APB2PERIPH_SPI1, ENABLE);  SPI_InitStructure.SPI_TransType = SPI_TRANSTYPE_FULLDUPLEX;SPI_InitStructure.SPI_Mode = SPI_MODE_MASTER;SPI_InitStructure.SPI_FrameFormat = SPI_FRAMEFORMAT_8BIT;SPI_InitStructure.SPI_SCKPL = SPI_SCKPL_LOW;SPI_InitStructure.SPI_SCKPH = SPI_SCKPH_1EDGE;SPI_InitStructure.SPI_SWNSSEN = SPI_SWNSS_SOFT;SPI_InitStructure.SPI_PSC = SPI_PSC_32; SPI_InitStructure.SPI_FirstBit = SPI_FIRSTBIT_MSB;SPI_InitStructure.SPI_CRCPOL = 7;SPI_Init(SPI1, &SPI_InitStructure);SPI_Enable(SPI1, ENABLE);
}
FLASH芯片操作
  1. 写使能,图中1,2,3步所示,代码也是按这个时序写的,下面的命令就不再画图说明了

    static VOID GD25Q40_WriteEnable(VOID)
    {GD25Q40_CS_LOW();DRV_SPI_SwapByte(WREN);GD25Q40_CS_HIGH();
    }
    
  2. 等待写操作结束,代码使用的是05H,所以只回一个字节

    static VOID GD25Q40_WaitForWriteEnd(VOID)
    {U8 FLASH_Status = 0;GD25Q40_CS_LOW();DRV_SPI_SwapByte(RDSR);do{FLASH_Status = DRV_SPI_SwapByte(Dummy_Byte);}while ((FLASH_Status & WIP_Flag) == SET); /* Write in progress */GD25Q40_CS_HIGH();
    }
    
  3. Sector擦除,擦除可以理解为一个特殊的写过程,即写FF的过程,所以在下一个操作之前需要等待写完成

    VOID DRV_GD25Q40_SectorErase(U32 SectorAddr)
    {GD25Q40_WriteEnable();GD25Q40_CS_LOW();DRV_SPI_SwapByte(SE);DRV_SPI_SwapByte((SectorAddr & 0xFF0000) >> 16);DRV_SPI_SwapByte((SectorAddr & 0xFF00) >> 8);DRV_SPI_SwapByte(SectorAddr & 0xFF);GD25Q40_CS_HIGH();GD25Q40_WaitForWriteEnd();
    }
    
  4. Block擦除

    VOID DRV_GD25Q40_BulkErase(VOID)
    {GD25Q40_WriteEnable();GD25Q40_CS_LOW();DRV_SPI_SwapByte(BE);GD25Q40_CS_HIGH();GD25Q40_WaitForWriteEnd();
    }
    
  5. 读数据,读的时候可以指定任意地址读,且可以按字节读

    VOID DRV_GD25Q40_BufferRead(U8* pBuffer, U32 ReadAddr, U16 NumByteToRead)
    {GD25Q40_CS_LOW();DRV_SPI_SwapByte(READ);DRV_SPI_SwapByte((ReadAddr & 0xFF0000) >> 16);DRV_SPI_SwapByte((ReadAddr& 0xFF00) >> 8);DRV_SPI_SwapByte(ReadAddr & 0xFF);while (NumByteToRead--) {*pBuffer = DRV_SPI_SwapByte(Dummy_Byte);pBuffer++;}GD25Q40_CS_HIGH();
    }
    
  6. 按页写

    VOID DRV_GD25Q40_PageWrite(U8* pBuffer, U32 WriteAddr, U16 NumByteToWrite)
    {GD25Q40_WriteEnable();GD25Q40_CS_LOW();DRV_SPI_SwapByte(WRITE);DRV_SPI_SwapByte((WriteAddr & 0xFF0000) >> 16);DRV_SPI_SwapByte((WriteAddr & 0xFF00) >> 8);DRV_SPI_SwapByte(WriteAddr & 0xFF);while (NumByteToWrite--){DRV_SPI_SwapByte(*pBuffer);pBuffer++;}GD25Q40_CS_HIGH();GD25Q40_WaitForWriteEnd();
    }
    
  7. 写入一段buf,该接口是在按页的基础上,增加是否换页写的封装接口

    VOID DRV_GD25Q40_BufferWrite(U8* pBuffer, U32 WriteAddr, U16 NumByteToWrite)
    {U8 NumOfPage = 0, NumOfSingle = 0, Addr = 0, count = 0, temp = 0;Addr = WriteAddr % GD25Q40_PageSize;count = GD25Q40_PageSize - Addr;NumOfPage =  NumByteToWrite / GD25Q40_PageSize;NumOfSingle = NumByteToWrite % GD25Q40_PageSize;/* WriteAddr is GD25Q40_PageSize aligned  */if (Addr == 0){   /* NumByteToWrite < GD25Q40_PageSize */if (NumOfPage == 0) {DRV_GD25Q40_PageWrite(pBuffer, WriteAddr, NumByteToWrite);}else /* NumByteToWrite > GD25Q40_PageSize */{while (NumOfPage--){DRV_GD25Q40_PageWrite(pBuffer, WriteAddr, GD25Q40_PageSize);WriteAddr +=  GD25Q40_PageSize;pBuffer += GD25Q40_PageSize;}DRV_GD25Q40_PageWrite(pBuffer, WriteAddr, NumOfSingle);}}else /* WriteAddr is not GD25Q40_PageSize aligned  */{if (NumOfPage == 0){/* (NumByteToWrite + WriteAddr) > GD25Q40_PageSize */if (NumOfSingle > count) {temp = NumOfSingle - count;DRV_GD25Q40_PageWrite(pBuffer, WriteAddr, count);WriteAddr +=  count;pBuffer += count;DRV_GD25Q40_PageWrite(pBuffer, WriteAddr, temp);}else{DRV_GD25Q40_PageWrite(pBuffer, WriteAddr, NumByteToWrite);}}else /* NumByteToWrite > GD25Q40_PageSize */{NumByteToWrite -= count;NumOfPage =  NumByteToWrite / GD25Q40_PageSize;NumOfSingle = NumByteToWrite % GD25Q40_PageSize;DRV_GD25Q40_PageWrite(pBuffer, WriteAddr, count);WriteAddr +=  count;pBuffer += count;while (NumOfPage--){DRV_GD25Q40_PageWrite(pBuffer, WriteAddr, GD25Q40_PageSize);WriteAddr +=  GD25Q40_PageSize;pBuffer += GD25Q40_PageSize;}if (NumOfSingle != 0){DRV_GD25Q40_PageWrite(pBuffer, WriteAddr, NumOfSingle);}}}
    }
    
功能测试举例
VOID APP_SPI_Test(VOID)
{U32 Manufact_ID = 0;U8  Tx_Buffer[256];U8  Rx_Buffer[256];U16 i = 0;DRV_GD25Q40_Init();printf("\n\rGD32103C-EVAL-V1.1 SPI Flash: configured...\n\r");Manufact_ID = DRV_GD25Q40_ReadID();printf("\n\rThe Flash_ID:0x%X\n\r", Manufact_ID);if (Manufact_ID == sFLASH_ID)   {printf("\n\rWrite to Tx_Buffer:\n\r");for(i=0; i<=255; i++)    {Tx_Buffer[i] = i;printf("0x%02X ",Tx_Buffer[i]);if(i%16 == 15){printf("\n\r");}}printf("\n\rRead from Rx_Buffer:\n\r");DRV_GD25Q40_SectorErase(FLASH_WriteAddress);DRV_GD25Q40_BufferWrite(Tx_Buffer,FLASH_WriteAddress, 256);APP_Delay(10);DRV_GD25Q40_BufferRead(Rx_Buffer,FLASH_ReadAddress, 256);  for(i=0; i<=255; i++) { printf("0x%02X ", Rx_Buffer[i]);if(i%16 == 15){printf("\n\r");}}printf("\n\rSPI Flash: Initialize Successfully!\n\r");}else{printf("\n\rSPI Flash: Initialize Fail!\n\r");}}

补充,Nor Flash和Nand Flash

  1. 结构方面

    1. Nor Flash采用内存的随机读取技术。各单元之间是并联的,对存储单元进行统一编址,所以可以随机访问任意一个字。既然是统一编址,Nor Flash就可以芯片内执行,即应用程序可直接在flash内运行,而无需先拷贝到RAM。
    2. Nand Flash数据线和地址线共用I/O线,需额外联接一些控制的输入输出。
  2. 读写速度方面
    1. Nor Flash有更快的读取速度
    2. Nand Flash有更快的写、擦除速度。
  3. 寿命(耐用性)
    1. Flash写入和擦除数据时会导致介质的氧化降解。这方面Nor Flash尤甚,所以Nor Flash不适合频繁擦写。
    2. Nor的擦写次数是10万次,Nand的擦写次数是100万次。
  4. 坏块处理
    1. Nand器件的坏块是随机分布的,在使用过程中,难免会产生坏块。所以在使用时要进行坏块管理以保障数据可靠。
  5. 成本和容量
    1. 在面积和工艺相同的情况下,Nand的容量比Nor大的多,成本更低。
    2. Nor Flash可直接通过程序编程,根据地址直接读取,容量一般是M级别的
    3. Nand Flash是根据数据块来设计的,所有Nand Flash容量更大,一般是G级别的。
  6. 易用性
    1. Nor Flash有专用的地址引脚来寻址,较容易和其他芯片联接,还支持本地执行。
    2. Nand Flash的IO端口采用复用的数据线和地址线,必须先通过寄存器串行地进行数据存取。各厂商对信号的定义不同,增加了应用的难度。
  7. 编程角度
    1. Nor Flash采用统一编址(有独立地址线),可随机读取每个“字”,但NOR flash不能像RAM以字节改写数据,只能按“页”写,故Nor Flash不能代替RAM。擦除既可整页擦除,也可整块擦除。
    2. Nand Flash共用地址线和数据线,页是读写数据的最小单元,块是擦除数据的最小单元。
    3. 另外,Flash进行写操作时,只能将相应的位由1变0,而擦除才能把块内所有位由0变1。所有写入数据时,如果该页已经存在数据,必须先擦除再写。

代码路径

https://github.com/YaFood/GD32F103/tree/master/TestSPI
https://gitee.com/YaFOOD/GD32F103/tree/master/TestSPI

GD32实战11__SPI FLASH相关推荐

  1. GD32上FAL Flash分区驱动移植及Easyflash与FlashDB移植说明

    GD32上FAL Flash分区驱动移植及Easyflash与FlashDB移植说明 效果 移植前提 下载源码 移植过程 加入以下文件及文件夹到工程目录和工程 将demo目录下的接口文件做下修改 修改 ...

  2. GD32片内flash读写数据

    GD32现在越来越火,应用也越来越广泛.我们在开发项目的时候,总会有需要掉电存储一些配置信息的时候,但是使用外挂flash.或者EEPROM,或多或少都会占用一些外围接口或增加一定的成本.于是,直接将 ...

  3. GD32实战20__Boot综合实验

    知识点 设计并实现一个boot,需要用到如下知识点, 1. 体会boot的作用2. 新增闪存控制器学习3. 串口知识复习4. FLASH&SPI知识复习5. 状态机知识点复习6. 初识上位机软 ...

  4. GD32实战7__中断

    引子 什么是中断 ​ 举个生活中的小栗子吧,我正在编写这个文档,突然门铃响了,我去开下门,原来是快递,签收完快递后,又回来接着写. ​ 上面的例子中, 1. 我就是CPU 2. 编写文档,是主运行程序 ...

  5. 【IAP】STM32和GD32的IAP原理分析、教程、资料整理

    文章目录 前言 一.什么是IAP? 二.IAP执行原理(以STM32F10X为例) 2.1 STM32F10X的储存器映像 2.2 正常上电的运行流程 2.3 加入IAP后的Bootloader运行流 ...

  6. GD32 使用stm32 固件库

    1. 系统 1) 晶振起振区别 描述:启动时间,GD32 与STM32 启动时间都是2ms,实际上GD 的执行效率快,所以ST 的HSE_STARTUP_TIMEOUT ((uint16_t)0x05 ...

  7. GD32f303 flash加密

    1.简介 GD32加密即将flash中程序固件保护起来,防止别人通过外部调试接口或者其他方法读取烧写的flash中的程序.防止抄袭,防止破坏. 2.mcu加密方式 <1> 写特定配置字 & ...

  8. STM32到GD32移植攻略

    1. 系统 1) 晶振起振区别 描述:启动时间,GD32 与STM32 启动时间都是2ms,实际上GD 的执行效率快,所以ST 的HSE_STARTUP_TIMEOUT ((uint16_t)0x05 ...

  9. GD32F303 使用 STM32Cubmex 开发应用 使用GD32官方例程 开发Bootloader 修正错误 见评论

    GD32F303 使用 STM32Cubmex 开发应用程序 使用GD32官方例程 开发Bootloader程序 最近使用GD32F303开发项目,为了偷懒使用 STM32Cubmex CPU选STM ...

最新文章

  1. 使用Python和OpenCV检测图像中的条形码
  2. Nginx负载均衡的详细配置及使用案例
  3. php 插入表,php 向数据库表中插入数据
  4. 阿里京东带头打劫,下一个被干掉的就是你
  5. Kotlin与Java之争
  6. GIT项目管理工具(part6)--放弃工作区文件修改及从仓库区恢复文件
  7. c 定义结构体时提示应输入声明_C++|了解结构体的内存对齐(成员声明的顺序影响占用空间大小)...
  8. Mac设置多屏幕的时候程序坞的位置
  9. Tosca new project Repository as MS SQL Server
  10. jQuery常用语法总结
  11. cross-env跨平台设置环境变量
  12. 西安工程大学计算机是几本专业,2016年西安工程大学计算机科学与技术(卓越班)专业在陕西录取分数线...
  13. 英文视频字幕自动生成
  14. 中国城市轨道交通与设备产业十四五建设规划与运营模式咨询报告2022-2028年
  15. 分享【百度搜狗360】SEO优化交流讨论Q群【禁广告/精品群】
  16. 2005年10月--至今,开发过的项目
  17. 公开数据集分享(一)-MMWHS
  18. IDEA中出现Connection refused: connect问题的解决方法
  19. 知识付费分销直播营销系统源码
  20. flowci php,我和flow.ci的第一次亲密接触

热门文章

  1. python接口自动化-token
  2. 作文:The Harm of Divorce
  3. 软件开发,web开发和应用程序开发的区别?
  4. PDU会话建立流程(8)-SM Policy Association的建立
  5. vuetify图标大全
  6. 盘点CES 2018盛展上的黑科技
  7. Matlab:利用Matlab软件进行GUI界面设计实现图像的基本操作
  8. 葫芦侠三楼API整理
  9. Latex 制作表格出现以下错误 Extra alignment tab has been changed to \cr
  10. html表盘的代码,表盘.html