此系列文章是小白学习STM32的一些学习笔记。小白第一次写笔记文章,有不足或是错误之处,请多体谅和交流!
1.FLASH指令编码表
我们将FLASH芯片的各个指令用宏定义的方式定义下来,这样相对于纯16进制指令,使用的时候清晰明了。
2.读取FLASH芯片ID函数
读取FLASH的ID函数编写如下:
u32 SPI_FLASH_ReadID(void)
{
u32 Temp=0,Temp0=0,Temp1=0,Temp2=0;
SPI_FLASH_CS_LOW();
SPI_FLASH_SendByte(W25X_JedecDeviceID);
Temp0=SPI_FLASH_SendByte(Dummy_Byte);
Temp1=SPI_FLASH_SendByte(Dummy_Byte);
Temp2=SPI_FLASH_SendByte(Dummy_Byte);
SPI_FLASH_CS_HIGH();
Temp = (Temp0<<16)|(Temp1<<8)|Temp2;
return Temp;
}
最后一步操作,是合并三个字节,得到完整的芯片ID。
3.FLASH写使能以及读取当前状态函数
对FLASH进行写操作之前,需要对写操作使能。
void SPI_FLASH_WriteEnable(void)
{
SPI_FLASH_CS_LOW();
SPI_FLASH_SendByte(W25X_WriteEnable);
SPI_FLASH_CS_HIGH();
}
FLASH的读写操作需要一定时间,所以需要一个函数判断是否在忙碌状态。FLASH芯片内部有一个状态寄存器,其第0位即是忙碌与否状态标志位。
根据指令,我们读取此状态寄存器,然后判断第0位是否为1,如果为1就是忙碌状态,继续等待,直到为0,忙碌状态结束,然后可以开始其他操作。其代码如下:
#define WIP_Flag 0x01
void SPI_FLASH_WaitForWriteEnd(void)
{
u8 FLASH_Status = 0;
SPI_FLASH_CS_LOW();
SPI_FLASH_SendByte(W25X_ReadStatusReg);
do
{
FLASH_Status = SPI_FLASH_SendByte(Dummy_Byte);
}while((FLASH_Status & WIP_Flag)==SET);
//取出第零位判断是否为1,是的话继续循环,否的话结束while;
SPI_FLASH_CS_HIGH();
}
4.FLASH扇区擦除
由于FLASH存储器特性,它只能将存储单元状态由1改为0,而不能将0改为1。因此,对FLASH写数据前,需对扇区进行擦除,即将所有单元置1,然后写数据,如果数据是1,即不需改变;如果数据是0,即将其由1改为0,故而完成数据的写入操作。
擦除一般多个字节进行:
擦除单位 | V大小 |
---|---|
扇区擦除(Sector Erase) | 4KB |
块擦除(Block Erase) | 64KB |
正片擦除(Chip Erase) | 整块芯片 |
扇区擦除,先发送指令,紧接着发送三个字节作为地址。
Tips:扇区擦除指令发送前,也需要写使能指令。
1 /**
2 * @brief 擦除 FLASH 扇区
3 * @param SectorAddr:要擦除的扇区地址
4 * @retval 无
5 */
6 void SPI_FLASH_SectorErase(u32 SectorAddr)
7 {
8 /* 发送 FLASH 写使能命令 */
9 SPI_FLASH_WriteEnable();
10 SPI_FLASH_WaitForWriteEnd();
11 /* 擦除扇区 */
12 /* 选择 FLASH: CS 低电平 */
13 SPI_FLASH_CS_LOW();
14 /* 发送扇区擦除指令*/
15 SPI_FLASH_SendByte(W25X_SectorErase);
16 /*发送擦除扇区地址的高位*/
17 SPI_FLASH_SendByte((SectorAddr & 0xFF0000) >> 16);
18 /* 发送擦除扇区地址的中位 */
19 SPI_FLASH_SendByte((SectorAddr & 0xFF00) >> 8);
20 /* 发送擦除扇区地址的低位 */
21 SPI_FLASH_SendByte(SectorAddr & 0xFF);
22 /* 停止信号 FLASH: CS 高电平 */
23 SPI_FLASH_CS_HIGH();
24 /* 等待擦除完毕*/
25 SPI_FLASH_WaitForWriteEnd();
26 }
5.FLASH的页写入
目标扇区擦除结束后可以进行数据写入,页写入一次性最多可以向FLASH传输256字节的数据。
1 /**
2 * @brief 对 FLASH 按页写入数据,调用本函数写入数据前需要先擦除扇区
3 * @param pBuffer,要写入数据的指针
4 * @param WriteAddr,写入地址
5 * @param NumByteToWrite,写入数据长度,必须小于等于页大小
6 * @retval 无
7 */
8 void SPI_FLASH_PageWrite(u8* pBuffer, u32 WriteAddr, u16 NumByteToWrite)
9 {
10 /* 发送 FLASH 写使能命令 */
11 SPI_FLASH_WriteEnable();
12
13 /* 选择 FLASH: CS 低电平 */
14 SPI_FLASH_CS_LOW();
15 /* 写送写指令*/
16 SPI_FLASH_SendByte(W25X_PageProgram);
17 /*发送写地址的高位*/
18 SPI_FLASH_SendByte((WriteAddr & 0xFF0000) >> 16);
19 /*发送写地址的中位*/
20 SPI_FLASH_SendByte((WriteAddr & 0xFF00) >> 8);
21 /*发送写地址的低位*/
22 SPI_FLASH_SendByte(WriteAddr & 0xFF);
23
24 if (NumByteToWrite > SPI_FLASH_PerWritePageSize)
25 {
26 NumByteToWrite = SPI_FLASH_PerWritePageSize;
27 FLASH_ERROR("SPI_FLASH_PageWrite too large!");
28 }
29
30 /* 写入数据*/
31 while (NumByteToWrite--)
32 {
33 /* 发送当前要写入的字节数据 */
34 SPI_FLASH_SendByte(*pBuffer);
35 /* 指向下一字节数据 */
36 pBuffer++;
37 }
38
39 /* 停止信号 FLASH: CS 高电平 */
40 SPI_FLASH_CS_HIGH();
41
42 /* 等待写入完毕*/
43 SPI_FLASH_WaitForWriteEnd();
44 }
6.从FLASH读取数据
发送了指令编码及要读的起始地址后,FLASH 芯片就会按地址递增的方式返回存储矩阵的内容,读取的数据量没有限制,只要没有停止通讯,FLASH 芯片就会一直返回数据。
1 /**
2 * @brief 读取 FLASH 数据
3 * @param pBuffer,存储读出数据的指针
4 * @param ReadAddr,读取地址
5 * @param NumByteToRead,读取数据长度
6 * @retval 无
7 */
8 void SPI_FLASH_BufferRead(u8* pBuffer, u32 ReadAddr, u16 NumByteToRead)
9 {
10 /* 选择 FLASH: CS 低电平 */
11 SPI_FLASH_CS_LOW();
12
13 /* 发送 读 指令 */
14 SPI_FLASH_SendByte(W25X_ReadData);
15
16 /* 发送 读 地址高位 */
17 SPI_FLASH_SendByte((ReadAddr & 0xFF0000) >> 16);
18 /* 发送 读 地址中位 */
19 SPI_FLASH_SendByte((ReadAddr& 0xFF00) >> 8);
20 /* 发送 读 地址低位 */
21 SPI_FLASH_SendByte(ReadAddr & 0xFF);
22
23 /* 读取数据 */
24 while (NumByteToRead--)
25 {
26 /* 读取一个字节*/
27 *pBuffer = SPI_FLASH_SendByte(Dummy_Byte);
28 /* 指向下一个字节缓冲区 */
29 pBuffer++;
30 }
31
32 /* 停止信号 FLASH: CS 高电平 */
33 SPI_FLASH_CS_HIGH();
34 }
本系列笔记内容、代码和图片均基于电子书《【野火®】零死角玩转STM32—F103-MINI》