修改事项: 1、新增TFTP IAP升级功能,只是代码移植完毕,没有测试使用 2、代码空间编译优化,零等待区域空间已满,而应用层代码已全部挪移到非零等待区域中,但还是会增加零等待区的空间占用。 待优化
521 lines
18 KiB
C
521 lines
18 KiB
C
/*
|
||
* spi_flash.c
|
||
*
|
||
* Created on: May 20, 2025
|
||
* Author: cc
|
||
*/
|
||
|
||
#include "spi_flash.h"
|
||
#include "debug.h"
|
||
|
||
uint8_t Temp_Flash_Buff[4100]; //FLash 写入缓存BUFF
|
||
|
||
__attribute__((section(".non_0_wait"))) void SPI_FLASH_Init(void)
|
||
{
|
||
/* SPI Flash 与 SPI SRAM 共用SPI引脚
|
||
* 因此 SPI Flash 无需引脚初始化
|
||
* */
|
||
|
||
Dbg_Println(DBG_BIT_SYS_STATUS_bit,"SPI Flash ID:0x%04x\r\n",Flash_ReadID());
|
||
}
|
||
|
||
/*******************************************************************************
|
||
* Function Name : Flash_ReadSR
|
||
* Description : P25Q40H Flash读取状态寄存器
|
||
* Input : None
|
||
* Return : P25Q40H Flash状态寄存器值
|
||
BIT7 6 5 4 3 2 1 0
|
||
SPR0 BP4 BP3 BP2 BP1 BP0 WEL WIP
|
||
SPR:默认0,状态寄存器保护位,配合WP使用
|
||
BP4,BP3,BP2,BP1,BP0:FLASH区域写保护设置
|
||
WEL:写使能锁定
|
||
BUSY:忙标记位(1,忙;0,空闲)
|
||
默认:0x00
|
||
*******************************************************************************/
|
||
__attribute__((section(".non_0_wait"))) uint8_t Flash_ReadSR(void)
|
||
{
|
||
uint8_t byte = 0;
|
||
Flash_CS_L;
|
||
SPI0_MasterSendByte(P24Q40H_ReadStatusReg); //发送读取状态寄存器命令
|
||
byte = SPI0_MasterRecvByte();
|
||
Flash_CS_H;
|
||
return byte;
|
||
}
|
||
|
||
/*******************************************************************************
|
||
* Function Name : Flash_WriteSR
|
||
* Description : P25Q40H Flash写状态寄存器
|
||
* Input :
|
||
sr_val:写入状态寄存器的值
|
||
BIT7 6 5 4 3 2 1 0
|
||
SPR0 BP4 BP3 BP2 BP1 BP0 WEL WIP
|
||
|
||
只有SPR0,BP3,BP2,BP1,BP0(bit 7,5,4,3,2)可以写!!!
|
||
* Return : None
|
||
*******************************************************************************/
|
||
__attribute__((section(".non_0_wait"))) void Flash_WriteSR(uint8_t sr_val)
|
||
{
|
||
Flash_CS_L;
|
||
SPI0_MasterSendByte(P24Q40H_WriteStatusReg);
|
||
SPI0_MasterSendByte(sr_val);
|
||
Flash_CS_H;
|
||
}
|
||
|
||
/*******************************************************************************
|
||
* Function Name : Flash_Write_Enable
|
||
* Description : P25Q40H 写使能 -- 将WEL置位
|
||
* Input : None
|
||
* Return : None
|
||
*******************************************************************************/
|
||
__attribute__((section(".non_0_wait"))) void Flash_Write_Enable(void)
|
||
{
|
||
Flash_CS_L;
|
||
SPI0_MasterSendByte(P24Q40H_WriteEnable);
|
||
Flash_CS_H;
|
||
}
|
||
|
||
/*******************************************************************************
|
||
* Function Name : Flash_Write_Disable
|
||
* Description : P25Q40H 写禁止 -- 将WEL清零
|
||
* Input : None
|
||
* Return : None
|
||
*******************************************************************************/
|
||
__attribute__((section(".non_0_wait"))) void Flash_Write_Disable(void)
|
||
{
|
||
Flash_CS_L;
|
||
SPI0_MasterSendByte(P24Q40H_WriteDisable);
|
||
Flash_CS_H;
|
||
}
|
||
|
||
/*******************************************************************************
|
||
* Function Name : Flash_ReadID
|
||
* Description : P25Q40H Flash 读取芯片ID
|
||
* Input : None
|
||
* Return : 返回值如下:
|
||
0x8512:表示芯片型号为P25Q40H
|
||
0x8511:表示芯片型号为P25Q20H
|
||
0x8510:表示芯片型号为P25Q10H
|
||
0x8509:表示芯片型号为P25Q05H
|
||
*******************************************************************************/
|
||
__attribute__((section(".non_0_wait"))) uint16_t Flash_ReadID(void)
|
||
{
|
||
uint16_t temp=0;
|
||
Flash_CS_L;
|
||
SPI0_MasterSendByte(P24Q40H_ReadManufactureID);
|
||
SPI0_MasterRecvByte();
|
||
SPI0_MasterRecvByte();
|
||
SPI0_MasterRecvByte();
|
||
temp |= SPI0_MasterRecvByte()<<8;
|
||
temp |= SPI0_MasterRecvByte();
|
||
Flash_CS_H;
|
||
return temp;
|
||
}
|
||
|
||
/*******************************************************************************
|
||
* Function Name : Flash_Wait_Busy
|
||
* Description : 等待空闲
|
||
* Input : None
|
||
* Return : 1:等待超时,处于繁忙状态
|
||
0:空闲状态
|
||
*******************************************************************************/
|
||
__attribute__((section(".non_0_wait"))) uint8_t Flash_Wait_Busy(void)
|
||
{
|
||
uint8_t temp=0;
|
||
uint16_t i=0;
|
||
temp = Flash_ReadSR();
|
||
while((temp&0x01)==0x01)
|
||
{
|
||
FEED_DOG(); //喂狗
|
||
Delay_Us(100);
|
||
temp = Flash_ReadSR();
|
||
i++;
|
||
if(i>3000) return 1;
|
||
};
|
||
return 0;
|
||
}
|
||
|
||
/*******************************************************************************
|
||
* Function Name : Flash_PowerDown
|
||
* Description : Flash 进入掉电模式
|
||
* Input : None
|
||
* Return : None
|
||
*******************************************************************************/
|
||
__attribute__((section(".non_0_wait"))) void Flash_PowerDown(void)
|
||
{
|
||
Flash_CS_L;
|
||
SPI0_MasterSendByte(P24Q40H_PowerDown);
|
||
Delay_Us(3);
|
||
Flash_CS_H;
|
||
}
|
||
|
||
/*******************************************************************************
|
||
* Function Name : Flash_PowerDown
|
||
* Description : Flash 唤醒掉电模式
|
||
* Input : None
|
||
* Return : None
|
||
*******************************************************************************/
|
||
__attribute__((section(".non_0_wait"))) void Flash_Wakeup(void)
|
||
{
|
||
Flash_CS_L;
|
||
SPI0_MasterSendByte(P24Q40H_ReleasePowerDown);
|
||
Delay_Us(3);
|
||
Flash_CS_H;
|
||
}
|
||
|
||
/*******************************************************************************
|
||
* Function Name : Flash_Erase_Chip
|
||
* Description : 擦除整个芯片
|
||
* Input : None
|
||
* Return : None
|
||
*******************************************************************************/
|
||
__attribute__((section(".non_0_wait"))) void Flash_Erase_Chip(void)
|
||
{
|
||
Flash_Write_Enable();
|
||
Flash_Wait_Busy();
|
||
Flash_CS_L;
|
||
SPI0_MasterSendByte(P24Q40H_ChipErase);
|
||
Flash_CS_H;
|
||
Flash_Wait_Busy();
|
||
}
|
||
|
||
/*******************************************************************************
|
||
* Function Name : Flash_Erase_Block
|
||
* Description : 擦除块
|
||
* Input : BLK_ID:块号(0~31) 2M
|
||
* Return : None
|
||
*******************************************************************************/
|
||
__attribute__((section(".non_0_wait"))) void Flash_Erase_Block(uint32_t BLK_ID)
|
||
{
|
||
uint8_t flash_buff[5];
|
||
|
||
BLK_ID*=0x10000; //64K
|
||
flash_buff[0] = P24Q40H_BlockErase;
|
||
flash_buff[1] = (uint8_t)((BLK_ID >> 16) & 0xFF);
|
||
flash_buff[2] = (uint8_t)((BLK_ID >> 8) & 0xFF);
|
||
flash_buff[3] = (uint8_t)((BLK_ID) & 0xFF);
|
||
flash_buff[4] = 0x00;
|
||
|
||
Flash_Write_Enable();
|
||
Flash_Wait_Busy();
|
||
Flash_CS_L;
|
||
SPI0_DMATrans(flash_buff,0x04);
|
||
Flash_CS_H;
|
||
|
||
Flash_Wait_Busy();
|
||
}
|
||
|
||
/*******************************************************************************
|
||
* Function Name : Flash_Erase_Sector
|
||
* Description : 擦除扇区
|
||
* Input : DST_Addr:扇区号(0~511) 2M
|
||
* Return : None
|
||
*******************************************************************************/
|
||
__attribute__((section(".non_0_wait"))) void Flash_Erase_Sector(uint32_t DST_ID)
|
||
{
|
||
uint8_t flash_buff[5];
|
||
|
||
DST_ID*=4096;
|
||
flash_buff[0] = P24Q40H_SectorErase;
|
||
flash_buff[1] = (uint8_t)((DST_ID >> 16) & 0xFF);
|
||
flash_buff[2] = (uint8_t)((DST_ID >> 8) & 0xFF);
|
||
flash_buff[3] = (uint8_t)((DST_ID) & 0xFF);
|
||
flash_buff[4] = 0x00;
|
||
|
||
Flash_Write_Enable();
|
||
Flash_Wait_Busy();
|
||
Flash_CS_L;
|
||
SPI0_DMATrans(flash_buff,0x04);
|
||
Flash_CS_H;
|
||
|
||
Flash_Wait_Busy();
|
||
}
|
||
|
||
/*******************************************************************************
|
||
* Function Name : Flash_Erase_Page
|
||
* Description : 擦除页区
|
||
* Input : Page_ID:页号(0~8191)
|
||
* Return : None
|
||
*******************************************************************************/
|
||
__attribute__((section(".non_0_wait"))) void Flash_Erase_Page(uint32_t Page_ID)
|
||
{
|
||
uint8_t flash_buff[5];
|
||
|
||
Page_ID*=256;
|
||
flash_buff[0] = P24Q40H_PageErase;
|
||
flash_buff[1] = (uint8_t)((Page_ID >> 16) & 0xFF);
|
||
flash_buff[2] = (uint8_t)((Page_ID >> 8) & 0xFF);
|
||
flash_buff[3] = (uint8_t)((Page_ID) & 0xFF);
|
||
flash_buff[4] = 0x00;
|
||
|
||
Flash_Write_Enable();
|
||
Flash_Wait_Busy();
|
||
Flash_CS_L;
|
||
SPI0_DMATrans(flash_buff,0x04);
|
||
Flash_CS_H;
|
||
Flash_Wait_Busy();
|
||
}
|
||
|
||
/*******************************************************************************
|
||
* Function Name : Flash_Erase_Page
|
||
* Description : 擦除页区
|
||
* Input : Page_addr:地址
|
||
* Return : None
|
||
*******************************************************************************/
|
||
__attribute__((section(".non_0_wait"))) void Flash_Erase_Pageaddr(uint32_t Page_addr)
|
||
{
|
||
uint8_t flash_buff[5];
|
||
|
||
flash_buff[0] = P24Q40H_PageErase;
|
||
flash_buff[1] = (uint8_t)((Page_addr >> 16) & 0xFF);
|
||
flash_buff[2] = (uint8_t)((Page_addr >> 8) & 0xFF);
|
||
flash_buff[3] = (uint8_t)((Page_addr) & 0xFF);
|
||
flash_buff[4] = 0x00;
|
||
|
||
Flash_Write_Enable();
|
||
Flash_Wait_Busy();
|
||
Flash_CS_L;
|
||
SPI0_DMATrans(flash_buff,0x04);
|
||
Flash_CS_H;
|
||
Flash_Wait_Busy();
|
||
}
|
||
|
||
/*******************************************************************************
|
||
* Function Name : Flash_Read
|
||
* Description : P25Q40H Flash 指定地址开始读取指定长度的数据
|
||
* Input :
|
||
pBuffer:数据存储区
|
||
NumByteToRead:要读取的字节数(最大65535)
|
||
ReadAddr:读取起始地址(24bit)
|
||
* Return : None
|
||
*******************************************************************************/
|
||
__attribute__((section(".non_0_wait"))) void Flash_Read(uint8_t* pBuffer,uint16_t NumByteToRead,uint32_t ReadAddr)
|
||
{
|
||
uint8_t flash_buff[5];
|
||
|
||
flash_buff[0] = P24Q40H_ReadData;
|
||
flash_buff[1] = (uint8_t)((ReadAddr >> 16) & 0xFF);
|
||
flash_buff[2] = (uint8_t)((ReadAddr >> 8) & 0xFF);
|
||
flash_buff[3] = (uint8_t)((ReadAddr) & 0xFF);
|
||
flash_buff[4] = 0x00;
|
||
|
||
Flash_CS_L;
|
||
SPI0_DMATrans(flash_buff,0x04);
|
||
SPI0_DMARecv(pBuffer,NumByteToRead);
|
||
Flash_CS_H;
|
||
}
|
||
|
||
/*******************************************************************************
|
||
* Function Name : Flash_Write_Page
|
||
* Description : P25Q40H Flash 指定地址开始写指定长度的数据
|
||
* Input :
|
||
pBuffer:数据存储区
|
||
NumByteToRead:要写的字节数(最大256),该数不应该超过该页的剩余字节数!!!
|
||
ReadAddr:读取起始地址(24bit)
|
||
* Return : None
|
||
*******************************************************************************/
|
||
__attribute__((section(".non_0_wait"))) void Flash_Write_Page(uint8_t* pBuffer,uint16_t NumByteToWrite,uint32_t writeAddr)
|
||
{
|
||
uint8_t flash_buff[5];
|
||
|
||
flash_buff[0] = P24Q40H_PageProgram;
|
||
flash_buff[1] = (uint8_t)((writeAddr >> 16) & 0xFF);
|
||
flash_buff[2] = (uint8_t)((writeAddr >> 8) & 0xFF);
|
||
flash_buff[3] = (uint8_t)((writeAddr) & 0xFF);
|
||
flash_buff[4] = 0x00;
|
||
|
||
Flash_Write_Enable();
|
||
Flash_CS_L;
|
||
SPI0_DMATrans(flash_buff,0x04);
|
||
SPI0_DMATrans(pBuffer,NumByteToWrite);
|
||
Flash_CS_H;
|
||
Flash_Wait_Busy();
|
||
}
|
||
|
||
/*******************************************************************************
|
||
* Function Name : Flash_Write_NoCheck
|
||
* Description : 无检验写P25Q40H FLASH
|
||
注意:必须确保所写的地址范围内的数据全部为0XFF,否则在非0XFF处写入的数据将失败!
|
||
具有自动换页功能
|
||
在指定地址开始写入指定长度的数据,但是要确保地址不越界!
|
||
* Input :
|
||
pBuffer:数据存储区
|
||
NumByteToRead:要写的字节数(最大65535)
|
||
ReadAddr:读取起始地址(24bit)
|
||
* Return : None
|
||
*******************************************************************************/
|
||
__attribute__((section(".non_0_wait"))) void Flash_Write_NoCheck(uint8_t* pBuffer,uint16_t NumByteToWrite,uint32_t writeAddr)
|
||
{
|
||
uint16_t pageremain;
|
||
pageremain=256-writeAddr%256; //单页剩余的字节数
|
||
if(NumByteToWrite<=pageremain) pageremain=NumByteToWrite;//不大于256个字节
|
||
while(1)
|
||
{
|
||
FEED_DOG(); //喂狗
|
||
|
||
Flash_Write_Page(pBuffer,pageremain,writeAddr);
|
||
if(pageremain == NumByteToWrite) break; //写入完成
|
||
else {
|
||
pBuffer+=pageremain;
|
||
writeAddr+=pageremain;
|
||
|
||
NumByteToWrite-=pageremain;
|
||
if(NumByteToWrite>256) pageremain=256;
|
||
else pageremain=NumByteToWrite;
|
||
}
|
||
};
|
||
}
|
||
|
||
/*******************************************************************************
|
||
* Function Name : Flash_Write
|
||
* Description : 无检验写P25Q40H FLASH
|
||
注意:必须确保所写的地址范围内的数据全部为0XFF,否则在非0XFF处写入的数据将失败!
|
||
具有自动换页功能
|
||
在指定地址开始写入指定长度的数据,但是要确保地址不越界!
|
||
* Input :
|
||
pBuffer:数据存储区
|
||
NumByteToRead:要写的字节数(最大65535)
|
||
ReadAddr:读取起始地址(24bit)
|
||
* Return : None
|
||
*******************************************************************************/
|
||
__attribute__((section(".non_0_wait"))) void Flash_Write(uint8_t* pBuffer,uint16_t NumByteToWrite,uint32_t WriteAddr)
|
||
{
|
||
uint32_t secpos;
|
||
uint16_t secoff,secremain,i;
|
||
uint8_t* Write_Buff;
|
||
|
||
if(NumByteToWrite <= 256*2)
|
||
{
|
||
|
||
Write_Buff = Temp_Flash_Buff;
|
||
|
||
secpos = WriteAddr/256; //页区地址
|
||
secoff = WriteAddr%256; //在扇区内的偏移
|
||
secremain = 256 - secoff; //扇区剩余空间
|
||
|
||
if(NumByteToWrite<=secremain) secremain = NumByteToWrite; //当前页区剩余空间可以存放
|
||
|
||
while(1)
|
||
{
|
||
FEED_DOG(); //喂狗
|
||
|
||
Flash_Read(Write_Buff,256,secpos*256); //读取整个扇区的内容
|
||
|
||
for(i=0;i<secremain;i++) //校验数据,判断是否是要擦除扇区
|
||
{
|
||
if(Write_Buff[secoff+i]!=0xFF) break;
|
||
}
|
||
|
||
if(i<secremain) //需要擦除页区
|
||
{
|
||
Flash_Erase_Page(secpos); //擦除整个页区
|
||
for(i=0;i<secremain;i++) //复制
|
||
{
|
||
Write_Buff[i+secoff]=pBuffer[i];
|
||
}
|
||
Flash_Write_NoCheck(Write_Buff,256,secpos*256); //写入整个页区
|
||
}else {
|
||
if(secremain == 256)
|
||
{
|
||
Flash_Write_NoCheck(pBuffer,256,secpos*256); //写入整个页区
|
||
|
||
}else if(secremain < 256){
|
||
Flash_Write_NoCheck(pBuffer,secremain,WriteAddr); //不需要擦除可以直接,写入整个页区
|
||
}
|
||
}
|
||
if(NumByteToWrite == secremain) break; //写入完成
|
||
else //写入未结束
|
||
{
|
||
secpos++; //页区地址增1
|
||
secoff = 0; //页区偏移位置归零
|
||
pBuffer += secremain; //数据指针偏移
|
||
WriteAddr += secremain; //数据保存地址偏移
|
||
NumByteToWrite -= secremain; //剩余写入字节数递减
|
||
if(NumByteToWrite > 256) secremain = 256; //下一个页区还是写不完
|
||
else secremain = NumByteToWrite; //下一个页区可以写完
|
||
}
|
||
}
|
||
}
|
||
else
|
||
{
|
||
Write_Buff = Temp_Flash_Buff;
|
||
|
||
secpos = WriteAddr/4096; //扇区地址
|
||
secoff = WriteAddr%4096; //在扇区内的偏移
|
||
secremain = 4096 - secoff; //扇区剩余空间
|
||
|
||
if(NumByteToWrite<=secremain) secremain = NumByteToWrite; //当前扇区剩余空间可以存放
|
||
|
||
while(1)
|
||
{
|
||
FEED_DOG(); //喂狗
|
||
|
||
Flash_Read(Write_Buff,2048,secpos*4096); //读取整个扇区的内容
|
||
Flash_Read(Write_Buff+2048,2048,secpos*4096+2048); //读取整个扇区的内容
|
||
|
||
for(i=0;i<secremain;i++) //校验数据,判断是否是要擦除扇区
|
||
{
|
||
if(Write_Buff[secoff+i]!=0xFF)break;
|
||
}
|
||
|
||
if(i<secremain) //需要擦除扇区
|
||
{
|
||
Flash_Erase_Sector(secpos); //擦除整个扇区
|
||
for(i=0;i<secremain;i++) //复制
|
||
{
|
||
Write_Buff[i+secoff]=pBuffer[i];
|
||
}
|
||
Flash_Write_NoCheck(Write_Buff,2048,secpos*4096); //写入整个扇区
|
||
Flash_Write_NoCheck(Write_Buff+2048,2048,secpos*4096+2048); //写入整个扇区
|
||
}else {
|
||
if(secremain == 4096)
|
||
{
|
||
Flash_Write_NoCheck(pBuffer,2048,secpos*4096); //写入整个扇区
|
||
Flash_Write_NoCheck(pBuffer+2048,2048,secpos*4096+2048); //写入整个扇区
|
||
}else if(secremain < 4096){
|
||
Flash_Write_NoCheck(pBuffer,secremain,WriteAddr); //不需要擦除可以直接,写入整个扇区
|
||
}
|
||
}
|
||
if(NumByteToWrite == secremain) break; //写入完成
|
||
else //写入未结束
|
||
{
|
||
secpos++; //扇区地址增1
|
||
secoff = 0; //扇区偏移位置归零
|
||
pBuffer += secremain; //数据指针偏移
|
||
WriteAddr += secremain; //数据保存地址偏移
|
||
NumByteToWrite -= secremain; //剩余写入字节数递减
|
||
if(NumByteToWrite > 4096) secremain = 4096; //下一个扇区还是写不完
|
||
else secremain = NumByteToWrite; //下一个扇区可以写完
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
/*******************************************************************************
|
||
* Function Name : SPI_FLASH_APP_Data_Erase
|
||
* Description : 外部Flash APP空间擦除
|
||
* APP空间大小 Size: 0x00070000 地址范围:0x00000000 ~ 0x0006FFFF
|
||
*******************************************************************************/
|
||
__attribute__((section(".non_0_wait"))) void SPI_FLASH_APP_Data_Erase(void)
|
||
{
|
||
for(uint8_t i = 0;i < 7;i++)
|
||
{
|
||
Flash_Erase_Block(i);
|
||
}
|
||
}
|
||
|
||
/*******************************************************************************
|
||
* Function Name : SPI_FLASH_Logic_File_Erase
|
||
* Description : 外部Flash 配置文件空间擦除
|
||
* APP空间大小 Size: 0x00090000 地址范围:0x00070000 ~ 0x000FFFFF
|
||
*******************************************************************************/
|
||
__attribute__((section(".non_0_wait"))) void SPI_FLASH_Logic_File_Erase(void)
|
||
{
|
||
for(uint8_t i = 7;i < 16;i++)
|
||
{
|
||
Flash_Erase_Block(i);
|
||
}
|
||
}
|
||
|
||
|
||
|