Job_SignsPads/STM32/Code/STM32F405/Application/en25qx64.c
2025-04-22 10:29:37 +08:00

384 lines
14 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#include "main.h"
u16 EN25QXX_TYPE = EN25Q64; //默认是EN25Q64
/*******************************************************************************
* 函 数 名: EN25QXX_Init
* 函数功能: 初始化SPI FLASH的IO口
* 输 入: 无
* 输 出: 无
* 描 述: EN25Q64
256字节 为 1页
4KB为一个Sector扇区,
16个扇区为1个Block
容量为64Mb = 8MB,共有128个Block,2048个Sector
*******************************************************************************/
void EN25QXX_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE); //使能GPIOB时钟
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOC, ENABLE); //使能GPIOB时钟
/* FLASH_CS PA4 */
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4; // PB14
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT; //输出
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; //推挽输出
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz; // 100MHz
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP; //上拉
GPIO_Init(GPIOA, &GPIO_InitStructure); //初始化
/* FLASH_HOLD PC4 */
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4; // PB14
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT; //输出
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; //推挽输出
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz; // 100MHz
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP; //上拉
GPIO_Init(GPIOC, &GPIO_InitStructure); //初始化
EN25QXX_CS = 1; // SPI FLASH不选中
EN25QXX_HOLD = 1;
SPI1_Init(); //初始化SPI1
SPI1_SetSpeed(SPI_BaudRatePrescaler_2); //设置为42M时钟,高速模式
EN25QXX_TYPE = EN25QXX_ReadID(); //读取FLASH ID.
printf("EN25QXX_TYPE=%x\r\n", EN25QXX_TYPE);
}
/*******************************************************************************
* 函 数 名: EN25QXX_ReadSR
* 函数功能: 读取EN25QXX的状态寄存器
* 输 入: 无
* 输 出: BIT7 6 5 4 3 2 1 0
SPR RV TB BP2 BP1 BP0 WEL BUSY
SPR:默认0,状态寄存器保护位,配合WP使用
TB,BP2,BP1,BP0:FLASH区域写保护设置
WEL:写使能锁定
BUSY:忙标记位(1,忙;0,空闲)
默认:0x00
*******************************************************************************/
u8 EN25QXX_ReadSR(void)
{
u8 byte = 0;
EN25QXX_CS = 0; //使能器件
SPI1_ReadWriteByte(EN25X_ReadStatusReg); //发送读取状态寄存器命令
byte = SPI1_ReadWriteByte(0Xff); //读取一个字节
EN25QXX_CS = 1; //取消片选
return byte;
}
/*******************************************************************************
* 函 数 名: EN25QXX_Write_SR
* 函数功能: 写EN25QXX状态寄存器
* 输 入:
* 输 出:
* 描 述: 只有SPR,TB,BP2,BP1,BP0(bit 7,5,4,3,2)可以写!!!
*******************************************************************************/
void EN25QXX_Write_SR(u8 sr)
{
EN25QXX_CS = 0; //使能器件
SPI1_ReadWriteByte(EN25X_WriteStatusReg); //发送写取状态寄存器命令
SPI1_ReadWriteByte(sr); //写入一个字节
EN25QXX_CS = 1; //取消片选
}
/*******************************************************************************
* 函 数 名: EN25QXX_Write_Enable
* 函数功能: EN25QXX写使能
* 输 入: 无
* 输 出: 无
* 描 述: 将WEL置位
*******************************************************************************/
void EN25QXX_Write_Enable(void)
{
EN25QXX_CS = 0; //使能器件
SPI1_ReadWriteByte(EN25X_WriteEnable); //发送写使能
EN25QXX_CS = 1; //取消片选
}
/*******************************************************************************
* 函 数 名: EN25QXX_Write_Disable
* 函数功能: EN25QXX写禁止
* 输 入: 无
* 输 出: 无
* 描 述: 将WEL清零
*******************************************************************************/
void EN25QXX_Write_Disable(void)
{
EN25QXX_CS = 0; //使能器件
SPI1_ReadWriteByte(EN25X_WriteDisable); //发送写禁止指令
EN25QXX_CS = 1; //取消片选
}
/*******************************************************************************
* 函 数 名: EN25QXX_ReadID
* 函数功能: 读取芯片ID
* 输 入: 无
* 输 出: 0XEF13,表示芯片型号为EN25Q80
0XEF14,表示芯片型号为EN25Q16
0XEF15,表示芯片型号为EN25Q32
0XEF16,表示芯片型号为EN25Q64
0XEF17,表示芯片型号为EN25Q128
* 描 述: 要结合芯片手册来写代码
*******************************************************************************/
u16 EN25QXX_ReadID(void)
{
u16 Temp = 0;
EN25QXX_CS = 0;
SPI1_ReadWriteByte(0x90); //发送读取ID命令
SPI1_ReadWriteByte(0x00);
SPI1_ReadWriteByte(0x00);
SPI1_ReadWriteByte(0x00);
Temp |= SPI1_ReadWriteByte(0xFF) << 8; //高8位
Temp |= SPI1_ReadWriteByte(0xFF); //低8位
EN25QXX_CS = 1;
return Temp;
}
/*******************************************************************************
* 函 数 名: EN25QXX_Read
* 函数功能: 读取SPI FLASH
* 输 入: pBuffer:数据存储区(存储读出来的数据)
ReadAddr:开始读取的地址(24bit)
NumByteToRead:要读取的字节数(最大65535)
* 输 出: 无
* 描 述: 在指定地址开始读取指定长度的数据
*******************************************************************************/
void EN25QXX_Read(u8 *pBuffer, u32 ReadAddr, u16 NumByteToRead)
{
u16 i;
EN25QXX_CS = 0; //使能器件
SPI1_ReadWriteByte(EN25X_ReadData); //发送读取命令
SPI1_ReadWriteByte((u8)((ReadAddr) >> 16)); //发送24bit地址
SPI1_ReadWriteByte((u8)((ReadAddr) >> 8));
SPI1_ReadWriteByte((u8)ReadAddr);
for (i = 0; i < NumByteToRead; i++)
{
pBuffer[i] = SPI1_ReadWriteByte(0XFF); //循环读数
}
EN25QXX_CS = 1;
}
/*******************************************************************************
* 函 数 名: EN25QXX_Write_Page
* 函数功能: 在指定地址开始写入最大256字节的数据
* 输 入: pBuffer:数据存储区
WriteAddr:开始写入的地址(24bit)
NumByteToWrite:要写入的字节数(最大256),该数不应该超过该页的剩余字节数!!!
* 输 出: 无
* 描 述: SPI在一页(0~65535)内写入少于256个字节的数据
*******************************************************************************/
void EN25QXX_Write_Page(u8 *pBuffer, u32 WriteAddr, u16 NumByteToWrite)
{
u16 i;
EN25QXX_Write_Enable(); // SET WEL
EN25QXX_CS = 0; //使能器件
SPI1_ReadWriteByte(EN25X_PageProgram); //发送写页命令
SPI1_ReadWriteByte((u8)((WriteAddr) >> 16)); //发送24bit地址
SPI1_ReadWriteByte((u8)((WriteAddr) >> 8));
SPI1_ReadWriteByte((u8)WriteAddr);
for (i = 0; i < NumByteToWrite; i++)
SPI1_ReadWriteByte(pBuffer[i]); //循环写数
EN25QXX_CS = 1; //取消片选
EN25QXX_Wait_Busy(); //等待写入结束
}
/*******************************************************************************
* 函 数 名: EN25QXX_Write_NoCheck
* 函数功能: 无检验写SPI FLASH
* 输 入: pBuffer:数据存储区
WriteAddr:开始写入的地址(24bit)
NumByteToWrite:要写入的字节数(最大65535)
CHECK OK
* 输 出: 无
* 描 述: 必须确保所写的地址范围内的数据全部为0XFF,否则在非0XFF处写入的数据将失败!
具有自动换页功能
在指定地址开始写入指定长度的数据,但是要确保地址不越界!
*******************************************************************************/
void EN25QXX_Write_NoCheck(u8 *pBuffer, u32 WriteAddr, u16 NumByteToWrite)
{
u16 pageremain;
pageremain = 256 - WriteAddr % 256; //单页剩余的字节数
if (NumByteToWrite <= pageremain)
pageremain = NumByteToWrite; //不大于256个字节
while (1)
{
EN25QXX_Write_Page(pBuffer, WriteAddr, pageremain);
if (NumByteToWrite == pageremain)
break; //写入结束了
else // NumByteToWrite>pageremain
{
pBuffer += pageremain;
WriteAddr += pageremain;
NumByteToWrite -= pageremain; //减去已经写入了的字节数
if (NumByteToWrite > 256)
pageremain = 256; //一次可以写入256个字节
else
pageremain = NumByteToWrite; //不够256个字节了
}
}
}
/*******************************************************************************
* 函 数 名: EN25QXX_Write
* 函数功能: 写SPI FLASH
* 输 入: pBuffer:数据存储区
WriteAddr:开始写入的地址(24bit)
NumByteToWrite:要写入的字节数(最大65535)
* 输 出: 无
* 描 述: 在指定地址开始写入指定长度的数据
该函数带擦除操作!
*******************************************************************************/
u8 EN25QXX_BUFFER[4096];
void EN25QXX_Write(u8 *pBuffer, u32 WriteAddr, u16 NumByteToWrite)
{
u32 secpos;
u16 secoff;
u16 secremain;
u16 i;
u8 *EN25QXX_BUF;
EN25QXX_BUF = EN25QXX_BUFFER;
secpos = WriteAddr / 4096; //扇区地址
secoff = WriteAddr % 4096; //在扇区内的偏移
secremain = 4096 - secoff; //扇区剩余空间大小
// printf("ad:%X,nb:%X\r\n",WriteAddr,NumByteToWrite); //测试用
if (NumByteToWrite <= secremain)
secremain = NumByteToWrite; //不大于4096个字节
while (1)
{
EN25QXX_Read(EN25QXX_BUF, secpos * 4096, 4096); //读出整个扇区的内容
for (i = 0; i < secremain; i++) //校验数据
{
if (EN25QXX_BUF[secoff + i] != 0XFF)
break; //需要擦除
}
if (i < secremain) //需要擦除
{
EN25QXX_Erase_Sector(secpos); //擦除这个扇区
for (i = 0; i < secremain; i++) //复制
{
EN25QXX_BUF[i + secoff] = pBuffer[i];
}
EN25QXX_Write_NoCheck(EN25QXX_BUF, secpos * 4096, 4096); //写入整个扇区
}
else
EN25QXX_Write_NoCheck(pBuffer, WriteAddr, secremain); //写已经擦除了的,直接写入扇区剩余区间.
if (NumByteToWrite == secremain)
break; //写入结束了
else //写入未结束
{
secpos++; //扇区地址增1
secoff = 0; //偏移位置为0
pBuffer += secremain; //指针偏移
WriteAddr += secremain; //写地址偏移
NumByteToWrite -= secremain; //字节数递减
if (NumByteToWrite > 4096)
secremain = 4096; //下一个扇区还是写不完
else
secremain = NumByteToWrite; //下一个扇区可以写完了
}
}
}
/*******************************************************************************
* 函 数 名: EN25QXX_Erase_Chip
* 函数功能: 擦除整个芯片
* 输 入: 无
* 输 出: 无
* 描 述: 等待时间超长...
*******************************************************************************/
void EN25QXX_Erase_Chip(void)
{
EN25QXX_Write_Enable(); // SET WEL
EN25QXX_Wait_Busy();
EN25QXX_CS = 0; //使能器件
SPI1_ReadWriteByte(EN25X_ChipErase); //发送片擦除命令
EN25QXX_CS = 1; //取消片选
EN25QXX_Wait_Busy(); //等待芯片擦除结束
}
/*******************************************************************************
* 函 数 名: EN25QXX_Erase_Sector
* 函数功能: 擦除一个扇区
* 输 入: Dst_Addr:扇区地址 根据实际容量设置
* 输 出: 无
* 描 述: 擦除一个扇区的最少时间:150ms
*******************************************************************************/
void EN25QXX_Erase_Sector(u32 Dst_Addr)
{
printf("fe:%x\r\n", Dst_Addr); //监视falsh擦除情况,测试用
Dst_Addr *= 4096;
EN25QXX_Write_Enable(); // SET WEL
EN25QXX_Wait_Busy();
EN25QXX_CS = 0; //使能器件
SPI1_ReadWriteByte(EN25X_SectorErase); //发送扇区擦除指令
SPI1_ReadWriteByte((u8)((Dst_Addr) >> 16)); //发送24bit地址
SPI1_ReadWriteByte((u8)((Dst_Addr) >> 8));
SPI1_ReadWriteByte((u8)Dst_Addr);
EN25QXX_CS = 1; //取消片选
EN25QXX_Wait_Busy(); //等待擦除完成
}
/*******************************************************************************
* 函 数 名: EN25QXX_Erase_Block
* 函数功能: 擦除一个块
* 输 入: Dst_Addr:扇区地址 根据实际容量设置
* 输 出: 无
* 描 述: 擦除一个扇区的最少时间:150ms
*******************************************************************************/
void EN25QXX_Erase_Block(u32 Dst_Addr)
{
char Block_Num = 0;
u32 Erase_Addr = 0;
printf("fe:%x\r\n", Dst_Addr); //监视falsh擦除情况,测试用
Block_Num = Dst_Addr >> 16;
Erase_Addr = Block_Num << 16;
for (int i = 0; i < 16; i++)
{
EN25QXX_Erase_Sector(Erase_Addr);
Erase_Addr += 0x1000;
}
}
/*******************************************************************************
* 函 数 名: EN25QXX_Wait_Busy
* 函数功能: 等待空闲
* 输 入: 无
* 输 出: 无
* 描 述:
*******************************************************************************/
void EN25QXX_Wait_Busy(void)
{
while ((EN25QXX_ReadSR() & 0x01) == 0x01)
; // 等待BUSY位清空
}
/*******************************************************************************
* 函 数 名: EN25QXX_PowerDown
* 函数功能: 进入掉电模式
* 输 入: 无
* 输 出: 无
* 描 述:
*******************************************************************************/
void EN25QXX_PowerDown(void)
{
EN25QXX_CS = 0; //使能器件
SPI1_ReadWriteByte(EN25X_PowerDown); //发送掉电命令
EN25QXX_CS = 1; //取消片选
delay_us(3); //等待TPD
}
/*******************************************************************************
* 函 数 名: EN25QXX_WAKEUP
* 函数功能: 唤醒
* 输 入: 无
* 输 出: 无
* 描 述:
*******************************************************************************/
void EN25QXX_WAKEUP(void)
{
EN25QXX_CS = 0; //使能器件
SPI1_ReadWriteByte(EN25X_ReleasePowerDown); // send EN25X_PowerDown command 0xAB
EN25QXX_CS = 1; //取消片选
delay_us(3); //等待TRES1
}