RTThread-W25Q128的驱动基于SPI和SFUD

前言

本文是一个初学者的学习记录,可能有误。
SFUD是针对flash的一种通用的组件,简单来说就是使用SFUD再加上RTT的SPI驱动,基本可以兼容大多数的FLASH,而不用自己再去写驱动。

一、kconfig的配置

1.配置对应的SPI和SFUD

代码如下(示例):

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中打开对应的选项。重新生成工程。

二、 SFUD的使用

首先使用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);

W25Q128的读写操作

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);//写入缓存数组
		}
	}
	
	
}
  • 0
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值