510
个编辑
更改
→gyu_flash_ex.c
这个文件,是我们这个例程中的重中之重,所以我们将它放在最后来说明。这个文件阅读之前,请大家一定对于W25W80的寄存器有一定了解,不然理解上会出现空缺。
首先我们封装了两个函数,分别是控制SPI CSN引脚上拉和下拉的功能。<syntaxhighlight lang="c++" line="1" start="84">
static void W25Q80_Flash_Select(void)
{
SPI_CSN_Ctr(GPIO_PIN_RESET); // 拉低Flash CSN引脚
}
</syntaxhighlight><syntaxhighlight lang="c++" line="1" start="97">
static void W25Q80_Flash_Deselect(void)
{
SPI_CSN_Ctr(GPIO_PIN_SET); // 拉高Flash CSN引脚
}
</syntaxhighlight>紧接着这一些函数,均是用于控制W25Q80,这部分大家可以参照代码后的注释,搭配W25Q80的芯片手册去理解。
主要功能就是几个:读取flash ID判断是否为W25Q80,唤醒W25Q80,判断W25Q80工作状态,驱动W25Q80写使能。<syntaxhighlight lang="c++" line="1" start="103">
//******************************************************************
// fn : W25Q80_Flash_PowerStandby
//
// brief : 将W25Q80设备退出省电模式并准备正常运行
//
// param : none
//
// return : 返回1代表命令成功
static bool W25Q80_Flash_PowerStandby(void)
{
uint8_t cmd;
bool success = 0;
cmd = BLS_CODE_RDP;
W25Q80_Flash_Select(); // 使能CSN引脚
success = Spi_write(&cmd,sizeof(cmd)) == 0; // 发送使能W25Q80的命令
W25Q80_Flash_Deselect(); // 禁止CSN引脚
if (success) // 如果使能W25Q80命令发送成功
{
if (W25Q80_Flash_waitReady() == 0) // 等待指令完成
{
success = 1;
}
}
return success;
}
//******************************************************************
// fn : W25Q80_Flash_readInfo
//
// brief : 读取W25Q80 Flash信息(制造商和设备ID)
//
// param : none
//
// return : 返回1代表命令成功
bool W25Q80_Flash_readInfo(void)
{
const uint8_t wbuf[] = { BLS_CODE_MDID, 0xFF, 0xFF, 0x00 };
W25Q80_Flash_Select(); // 使能CSN引脚
int ret = Spi_write(wbuf, sizeof(wbuf)); // 发送设备信息的命令
if (ret) // 如果SPI写失败
{
W25Q80_Flash_Deselect(); // 禁止CSN引脚
return 0; // 返回0
}
ret = Spi_read(infoBuf, sizeof(infoBuf)); // 读取设备信息的命令
W25Q80_Flash_Deselect(); // 禁止CSN引脚
if(ret == 0) // 如果SPI读成功
{
return 1; // 返回1
}
return 0; // 返回0
}
//******************************************************************
// fn : W25Q80_Flash_VerifyPart
//
// brief : 检测W25Q80 Flash信息是否正确
//
// param : none
//
// return : 返回1代表命令成功
static bool W25Q80_Flash_VerifyPart(void)
{
// 判断是否成功读取设备信息
if (!W25Q80_Flash_readInfo())
{
return false;
}
// 下面一段是判断读取的设备ID是否为W25Q80
pFlashInfo = flashInfo;
while (pFlashInfo->deviceSize > 0)
{
if (infoBuf[0] == pFlashInfo->manfId && infoBuf[1] == pFlashInfo->devId)
{
break;
}
pFlashInfo++;
}
return pFlashInfo->deviceSize > 0; // 成功返回1
}
//******************************************************************
// fn : W25Q80_Flash_waitReady
//
// brief : 等待上一次擦除/编程操作完成
//
// param : none
//
// return : 返回0代表命令成功
static int W25Q80_Flash_waitReady(void)
{
const uint8_t wbuf[1] = { BLS_CODE_READ_STATUS };
int ret;
// 等待SPI完成
W25Q80_Flash_Select();
Spi_flash();
W25Q80_Flash_Deselect();
for (;;)
{
uint8_t buf;
W25Q80_Flash_Select(); // 使能CSN引脚
Spi_write(wbuf, sizeof(wbuf)); // 发送读取W25Q80状态的命令
ret = Spi_read(&buf,sizeof(buf)); // 读取状态返回值
W25Q80_Flash_Deselect(); // 禁止CSN引脚
if (ret) // 如果SPI写失败
{
return -2; // 返回错误-2
}
if (!(buf & BLS_STATUS_BIT_BUSY)) // 如果状态返回为busy,则等待
{
/* Now ready */
break;
}
}
return 0;
}
//******************************************************************
// fn : W25Q80_Flash_writeEnable
//
// brief : W25Q80 Flash写使能
//
// param : none
//
// return : 返回0代表命令成功
static int W25Q80_Flash_writeEnable(void)
{
const uint8_t wbuf[] = { BLS_CODE_WRITE_ENABLE };
W25Q80_Flash_Select(); // 使能CSN引脚
int ret = Spi_write(wbuf,sizeof(wbuf)); // 发送W25Q80写使能命令
W25Q80_Flash_Deselect(); // 禁止CSN引脚
if (ret) // 如果SPI写失败
{
return -3; // 返回错误-3
}
return 0;
}
</syntaxhighlight>接下来的4个函数,我们将其与上面的几个W25Q80的控制函数分开讲解,原因在于这几个函数是用户用于读写flash的。
首先是W25Q80_Flash_open()函数,这个函数是用户唤醒W25Q80,并且检测flash ID是否正确。<syntaxhighlight lang="c++" line="1" start="272">
bool W25Q80_Flash_open(void)
{
bool f;
// 设置唤醒,返回1代表成功
f = W25Q80_Flash_PowerStandby();
// 如果成功,则获取ID
if (f)
{
// 获取ID(Verify manufacturer and device ID),记录成功状态
f = W25Q80_Flash_VerifyPart();
}
// 返回1则获取ID成功,返回0则失败
return f;
}
</syntaxhighlight>剩下的3个函数,分别是W25Q80擦除、写入、读取的功能,这3个函数都在gyu_flash.c中经过一层封装,留给大家使用了,这边大家感兴趣的可以仔细看下代码原型。<syntaxhighlight lang="c++" line="1" start="290">
//******************************************************************
// fn : W25Q80_Flash_read
//
// brief : W25Q80 flash芯片读函数
//
// param : offset -> flash地址
// length -> 准备读取的数据长度
// buf -> 指向准备读取的数据指针
//
// return : 返回1代表命令成功
bool W25Q80_Flash_read(size_t offset, size_t length, uint8_t *buf)
{
uint8_t wbuf[4];
// 等待之前的flash处理完成
int ret = W25Q80_Flash_waitReady();
if (ret)
{
return false;
}
// 设置读指令
wbuf[0] = BLS_CODE_READ; // 读命令
wbuf[1] = (offset >> 16) & 0xff; // 页面地址
wbuf[2] = (offset >> 8) & 0xff; // 页面地址
wbuf[3] = offset & 0xff; // 页面地址
W25Q80_Flash_Select(); // 使能CSN引脚
if (Spi_write(wbuf, sizeof(wbuf))) // 写读取数据命令,失败则进入下面的处理
{
W25Q80_Flash_Deselect(); // 禁止CSN引脚
return false; // 返回0
}
ret = Spi_read(buf, length); // SPI读取数据
W25Q80_Flash_Deselect(); // 禁止CSN引脚
return ret == 0; // SPI读取数据成功,返回1
}
//******************************************************************
// fn : W25Q80_Flash_write
//
// brief : W25Q80 flash芯片写函数
//
// param : offset -> flash地址
// length -> 准备写入的数据长度
// buf -> 指向准备写入的数据指针
//
// return : 返回1代表命令成功
bool W25Q80_Flash_write(size_t offset, size_t length, const uint8_t *buf)
{
uint8_t wbuf[4];
while (length > 0)
{
// 检测之前的W25Q80 flash操作是否完成
int ret = W25Q80_Flash_waitReady();
if (ret) // 如果失败,返回0
{
return false;
}
// W25Q80 flash写使能
ret = W25Q80_Flash_writeEnable();
if (ret) // 如果失败,返回0
{
return false;
}
// 下面一段是计算数据长度,并且将扇区信息赋值给SPI的写缓冲区(wbuf)
size_t ilen;
ilen = BLS_PROGRAM_PAGE_SIZE - (offset % BLS_PROGRAM_PAGE_SIZE);
if (length < ilen)
{
ilen = length;
}
wbuf[0] = BLS_CODE_PROGRAM; // 编写页面指令
wbuf[1] = (offset >> 16) & 0xff; // 页面地址
wbuf[2] = (offset >> 8) & 0xff; // 页面地址
wbuf[3] = offset & 0xff; // 页面地址
offset += ilen;
length -= ilen;
W25Q80_Flash_Select(); // 使能CSN引脚
if (Spi_write(wbuf, sizeof(wbuf)))// 写编写页面的命令,失败则进入如下处理
{
W25Q80_Flash_Deselect(); // 禁止CSN引脚
return false; // 返回0
}
if (Spi_write(buf,ilen)) // 写数据,失败则进入如下处理
{
W25Q80_Flash_Deselect(); // 禁止CSN引脚
return false; // 返回0
}
buf += ilen;
W25Q80_Flash_Deselect(); // 禁止CSN引脚
}
return true; // 返回1
}
//******************************************************************
// fn : W25Q80_Flash_erase
//
// brief : W25Q80 flash芯片擦除函数
//
// param : offset -> flash地址
// length -> 准备擦除的数据长度
//
// return : 返回1代表命令成功
bool W25Q80_Flash_erase(size_t offset, size_t length)
{
uint8_t wbuf[4];
size_t i, numsectors;
wbuf[0] = BLS_CODE_SECTOR_ERASE;
// 下面一段是计算擦除的扇区大小
{
size_t endoffset = offset + length - 1;
offset = (offset / BLS_ERASE_SECTOR_SIZE) * BLS_ERASE_SECTOR_SIZE;
numsectors = (endoffset - offset + BLS_ERASE_SECTOR_SIZE - 1) / BLS_ERASE_SECTOR_SIZE;
}
for (i = 0; i < numsectors; i++)
{
// 等待上一个擦除命令完成
int ret = W25Q80_Flash_waitReady();
if (ret) // 返回失败
{
return false; // 返回0
}
// 写使能
ret = W25Q80_Flash_writeEnable();
if (ret) // 返回失败
{
return false; // 返回0
}
wbuf[1] = (offset >> 16) & 0xff; // 页面地址
wbuf[2] = (offset >> 8) & 0xff; // 页面地址
wbuf[3] = offset & 0xff; // 页面地址
W25Q80_Flash_Select(); // 使能CSN引脚
if (Spi_write(wbuf, sizeof(wbuf)))// 发送擦除命令,失败则进入下面处理
{
W25Q80_Flash_Deselect(); // 禁止CSN引脚
return false; // 返回0
}
W25Q80_Flash_Deselect(); // 禁止CSN引脚
offset += BLS_ERASE_SECTOR_SIZE;
}
return true; // 返回1
}
</syntaxhighlight>
[[分类:NB-IOT]]
[[分类:NBDK-L4]]
[[分类:教程]]
__强显目录__