本文是一个初学者的学习记录,可能有误。
SFUD是针对flash的一种通用的组件,简单来说就是使用SFUD再加上RTT的SPI驱动,基本可以兼容大多数的FLASH,而不用自己再去写驱动。
代码如下(示例):
menu "Onboard Peripheral Drivers"
menuconfig BSP_USING_SPI_FLASH
bool "Enable SPI FLASH(W25Q128 spi2)"
default n
select BSP_USING_SPI
select BSP_USING_SPI2
select RT_USING_SFUD
if BSP_USING_SPI_FLASH
config SPI_FALSH_NAME
string "spi flash device name"
default "W25Q128"
config SPI_FLASH_BUS_NAME
string "spi flash bus name"
default "spi2"
config SPI_FLASH_DEVICE_NAME
string "spi flash device name"
default "spi20"
endif
endmenu
menu "On-chip Peripheral Drivers"
config BSP_USING_GPIO
bool "Enable GPIO"
select RT_USING_PIN
default y
menuconfig BSP_USING_SPI
bool "Enable SPI BUS"
default y
select RT_USING_SPI
if BSP_USING_SPI
config BSP_USING_SPI2
bool "Enable SPI2 BUS"
default y
endif
source "../libraries/HAL_Drivers/Kconfig"
endmenu
并在ENV中打开对应的选项。重新生成工程。
首先使用rt_hw_spi_device_attach,挂载SPI设备
使用rt_sfud_flash_probe,挂载SFUD驱动
使用INIT_COMPONENT_EXPORT自动在开机时加载运行。
int rt_hw_spi_flash_init(void)
{
__HAL_RCC_GPIOB_CLK_ENABLE();
rt_hw_spi_device_attach(SPI_FLASH_BUS_NAME,SPI_FLASH_DEVICE_NAME, GPIOB, GPIO_PIN_12);
if (RT_NULL == rt_sfud_flash_probe(SPI_FALSH_NAME,SPI_FLASH_DEVICE_NAME))
{
return -RT_ERROR;
};
return RT_EOK;
}
INIT_COMPONENT_EXPORT(rt_hw_spi_flash_init);
sfud_w25q_sample是sfud_w25q_sample中提供了2种操作方式,两种方式都可以进行读写。
用SFUD操作
使用rt_sfud_flash_find_by_dev_name,找到加载的SFUD设备的句柄
sfud_erase_write和sfud_read进行相应的读写
用RTT自身的驱动操作
rt_device_find,找到设备句柄
rt_device_open,打开flash设备
rt_device_control 对设备进行控制,下文中是获取了设备的基本信息
rt_device_write 写入
rt_device_read 读取
rt_device_close 关闭设备
rt_uint8_t wData[4096] = {"QSPI bus write data to W25Q flash."};
rt_uint8_t rData[4096];
static void static void sfud_w25q_sample(void)
{
rt_spi_flash_device_t flash_dev;
sfud_flash_t sfud_dev;
struct rt_device_blk_geometry geometry;
// 1- use sfud api
rt_kprintf("\n 1 - Use SFUD API \n");
sfud_dev = rt_sfud_flash_find_by_dev_name(SPI_FALSH_NAME);
if(sfud_dev == RT_NULL){
rt_kprintf("sfud can't find %s device.\n", SPI_FALSH_NAME);
}else{
rt_kprintf("sfud device name: %s, sector_count: %d, bytes_per_sector: %d, block_size: %d.\n",
sfud_dev->name, sfud_dev->chip.capacity / sfud_dev->chip.erase_gran,
sfud_dev->chip.erase_gran, sfud_dev->chip.erase_gran);
if(sfud_erase_write(sfud_dev, 0x002000, sizeof(wData), wData) == SFUD_SUCCESS)
rt_kprintf("sfud api write data to w25q128(address:0x2000) success.\n");
if(sfud_read(sfud_dev, 0x002000, sizeof(rData), rData) == SFUD_SUCCESS)
rt_kprintf("sfud api read data from w25q128(address:0x2000) is:%s\n", rData);
}
// 2- use rt_device api
rt_kprintf("\n 2 - Use rt_device API \n");
flash_dev = (rt_spi_flash_device_t)rt_device_find(SPI_FALSH_NAME);
if(flash_dev == RT_NULL){
rt_kprintf("rt_device api can't find %s device.\n", SPI_FALSH_NAME);
}else{
rt_device_open(&flash_dev->flash_device, RT_DEVICE_OFLAG_OPEN);
if(rt_device_control(&flash_dev->flash_device, RT_DEVICE_CTRL_BLK_GETGEOME, &geometry) == RT_EOK)
rt_kprintf("spi flash device name: %s, sector_count: %d, bytes_per_sector: %d, block_size: %d.\n",
flash_dev->flash_device.parent.name, geometry.sector_count, geometry.bytes_per_sector, geometry.block_size);
if(rt_device_write(&flash_dev->flash_device, 0x03, wData, 1) > 0)
rt_kprintf("rt_device api write data to w25q128(address:0x3000) success.\n");
if(rt_device_read(&flash_dev->flash_device, 0x03, rData, 1) > 0)
rt_kprintf("rt_device api read data from w25q128(address:0x3000) is:%s\n", rData);
rt_device_close(&flash_dev->flash_device);
}
}
自此W25Q128Q128就可以驱动起来了
由于W29Q128是页写入的,所以每次写入和擦除都是按页来的,根据不同的falsh页也不同,w25Q128一页是4096字节。上述的函数会导致单写某几个字节的时候会把之前写入的全擦除。
所以当需要小段写入的时候,应该是先判断写入地址和写入的长度,如果要写的数据在一个扇区内,就读取该扇区的内容到缓存数组中,然后擦除这个扇区,把要写的数据加入到这个缓存数组,然后把缓存数组写入,如果写入的数据要跨越两个扇区,那就要按照上述操作,写第一个扇区,然后写第二个扇区。
//读出扇区,擦除扇区后写入。最大不要超过4096字节,只能跨越两个扇区
void sfud_w25q_write(rt_uint32_t wadd,rt_uint8_t *wdata,rt_uint16_t size)
{
sfud_flash_t sfud_dev;
uint16_t i;
uint32_t sec_pos,sec_off,sec_remain;
sec_pos = wadd / 4096;//扇区地址
sec_off = wadd % 4096;//扇区内的偏移
sec_remain = 4096 - sec_off; //扇区剩余空间大小
sfud_dev = rt_sfud_flash_find_by_dev_name(SPI_FALSH_NAME);
if(sfud_dev == RT_NULL)
{
rt_kprintf("sfud can't find %s device.\n", SPI_FALSH_NAME);
}
else
{ //判断扇区的剩余大小是否小于数据长度,如果小于那就说明是在一个扇区
if(sec_remain>size)//一个扇区
{
sfud_read(sfud_dev,sec_pos*4096,4096,rData);//读取整个扇区的内容
sfud_erase(sfud_dev,sec_pos*4096,4096);//擦除扇区
for(i=0;i<size;i++)//加入要写入的数据
{
rData[sec_off+i] = *(wdata+i);
}
sfud_write(sfud_dev,sec_pos*4096,4096,rData);
}
else //跨越两个扇区
{
sfud_read(sfud_dev,sec_pos*4096,4096,rData);//读取第一位扇区的内容
sfud_erase(sfud_dev,sec_pos*4096,4096);//擦除扇区
for(i=0;i<sec_remain;i++) //加入要写入的数组前半段
{
rData[sec_off+i] = *(wdata+i);
}
sfud_write(sfud_dev,sec_pos*4096,4096,rData);//写入缓存数组
sfud_read(sfud_dev,(sec_pos+1)*4096,4096,rData);//读取第二个扇区的内容
sfud_erase(sfud_dev,(sec_pos+1)*4096,4096);//擦除扇区
for(i=0;i<(size-sec_remain);i++) //加入要写入的数组后半段
{
rData[i] = *(wdata+sec_remain+i);
}
sfud_write(sfud_dev,(sec_pos+1)*4096,4096,rData);//写入缓存数组
}
}
}