文章目录
一、前言
学习记录,已建好工程。FAL、SFUD、硬件SPI、W25Q128、EasyFlash。
二、步骤
一)配置Cube MX
双击 “Cube Mx Settings”(或“cubemx”->“cubemx.ioc”)打开Cube Mx,配置硬件SPI,选择hal库,点击生成。

二)配置RT-thread Settings
双击 “RT-Thread Settings”打开配置页面,添加软件包和组件。
1、添加组件FAL
使能组件“FAL”,并使能“使用SFUD驱动”,更改“设置使用FAL的设备名称”为“W25Q128”。

2、添加组件SPI
在组件“设备驱动程序”里使能“SPI驱动”,并使用“SFUD”,设置总线最大速度(自行更改)。

设置好后,图形配置界面会点亮这两个驱动。

3、EasyFlash
添加软件包“EasyFlash”,并配置。调试日志在测试完成后可关闭。

三)配置工程
1、排除构建
打开工程中“cubemx”文件夹,除“cubemx.ioc”外,其余全部排除构建。
2、复制生成的SPI相关函数
在软件左上角的“导航器”中选择项目,并将“cubemx”文件夹中“spi.c”文件的“MX_SPI1_Init”、“HAL_SPI_MspInit”、“HAL_SPI_MspDeInit”函数复制到“drivers”文件夹中“board.c”文件末尾。

有些SPI口上电后会默认为JTAG口,此时SPI初始化就会失败,需要在“HAL_SPI_MspInit”函数中禁止JTAG功能。

__HAL_RCC_AFIO_CLK_ENABLE();
__HAL_RCC_PWR_CLK_ENABLE();
__HAL_AFIO_REMAP_SWJ_NOJTAG();
3、配置board.h文件
将SPI外设在“board.h"文件中打开宏定义。

4、配置stm32f1xx_hal_conf.h
在生成程序时,提示将“stm32f1xx_hal_conf.h”备份,并在“drivers”文件夹新建了“stm32f1xx_hal_conf_bak.h”文件,但是众多文件中调用的还是改名前的文件,所以会报错,这里选择将新建文件改为原来的名字(不知是否应这样处理)。

打开此文件,打开使能SPI模块宏。

5、添加fal_cfg.h
将“rt_thrread”->“components”->“fal”->“samples”->“porting”->“fal_cfg.h”文件移动到“drivers”->“include”文件夹中。

并在此文件中对设备名称、类型、空间分配函数进行更改。
#define NOR_FLASH_DEV_NAME "W25Q128" //设备名称,必须与fal设置一致
/* ===================== Flash device Configuration ========================= */
extern const struct fal_flash_dev stm32f2_onchip_flash;
extern struct fal_flash_dev nor_flash0;
/* flash device table */
//#define FAL_FLASH_DEV_TABLE \
//{ \
// &stm32f2_onchip_flash, \
// &nor_flash0, \
//}
#define FAL_FLASH_DEV_TABLE \
{ \
&nor_flash0, \
}
/* ====================== Partition Configuration ========================== */
#ifdef FAL_PART_HAS_TABLE_CFG
/* partition table */
//#define FAL_PART_TABLE \
//{ \
// {FAL_PART_MAGIC_WORD, "bl", "stm32_onchip", 0, 64*1024, 0}, \
// {FAL_PART_MAGIC_WORD, "app", "stm32_onchip", 64*1024, 704*1024, 0}, \
// {FAL_PART_MAGIC_WORD, "easyflash", NOR_FLASH_DEV_NAME, 0, 1024*1024, 0}, \
// {FAL_PART_MAGIC_WORD, "download", NOR_FLASH_DEV_NAME, 1024*1024, 1024*1024, 0}, \
//}
#define FAL_PART_TABLE \
{ \
{FAL_PART_MAGIC_WORD, "easyflash", NOR_FLASH_DEV_NAME, 0, 1024*1024, 0}, \
{FAL_PART_MAGIC_WORD, "download", NOR_FLASH_DEV_NAME, 1024*1024, 1024*1024, 0}, \
{FAL_PART_MAGIC_WORD, "datafile", NOR_FLASH_DEV_NAME, 2*1024*1024, 8*1024*1024, 0}, \
}
#endif /* FAL_PART_HAS_TABLE_CFG */
6、添加fal_flash_sfud_port.c
将“rt_thrread”->“components”->“fal”->“samples”->“porting”->“fal_flash_sfud_port.c”文件移动到“drivers”文件夹中。打开此文件,按实际参数修改flash配置(大多数SPI Flash都支持sfud,无需程序配置即可自动读取参数)。

7、新建W25Qxx.c
在“application”文件夹新建“W25Qxx.c”文件,并在文件中自动挂载SPI设备,探测SPI Flash。
#include <rtthread.h>
#include <rtdevice.h>
#include <board.h>
#define DBG_TAG "W25Qxx"
#define DBG_LVL DBG_LOG
#include <rtdbg.h>
#include <fal.h>
#include "drv_spi.h"
#include "spi_flash_sfud.h"
extern void Peripheral_power_ctrl(int powermode); //flash供电
#define W25Q_SPI_DEVICE_NAME "spi10"
/*********************************************************************************/
/*********************************************************************************/
/* 挂载设备到SPI总线,并向内核注册SPI设备
* 设备为 spi10,即SPI1总线上的第0号设备
* 设置SPI片选引脚
* */
int w25q_spi_device_init()
{
__HAL_RCC_GPIOA_CLK_ENABLE();
return rt_hw_spi_device_attach("spi1", "spi10", GPIOA, GPIO_PIN_4);
}
INIT_DEVICE_EXPORT(w25q_spi_device_init);
/*********************************************************************************/
/*********************************************************************************/
/* 通过SFUD驱动库和SPI设备探测SPI Flash
* */
static int rt_hw_spi_flash_with_sfud_init(void)
{
Peripheral_power_ctrl(1);
if (RT_NULL == rt_sfud_flash_probe("W25Q128", "spi10"))
{
return RT_ERROR;
};
return RT_EOK;
}
INIT_COMPONENT_EXPORT(rt_hw_spi_flash_with_sfud_init);
/*********************************************************************************/
此时编译下载程序,可以在打印出的调试信息中看到SPI Flash已经挂载成功。

如果需要直接驱动W25Q128,不使用fal,则不需要添加“fal_cfg.h”与“fal_flash_sfud_port.c”,通过获取sfud驱动句柄,使用sfud函数操作flash。
sfud_flash *g_pSfudDev_s = NULL;
/*********************************************************************************/
/* 挂载设备到SPI总线,并向内核注册SPI设备
* 设备为 spi10,即SPI1总线上的第0号设备
* 设置SPI片选引脚
* */
int w25q_spi_device_init()
{
__HAL_RCC_GPIOA_CLK_ENABLE();
return rt_hw_spi_device_attach("spi1", "spi10", GPIOA, GPIO_PIN_4); //设置SPI片选引脚
}
INIT_DEVICE_EXPORT(w25q_spi_device_init);
/*********************************************************************************/
/*********************************************************************************/
/* 通过SFUD驱动库和SPI设备探测SPI Flash
* */
int rt_hw_spi_flash_with_sfud_init(void)
{
if (RT_NULL == rt_sfud_flash_probe("W25Q128", "spi10"))
{
return RT_ERROR;
}
g_pSfudDev_s = rt_sfud_flash_find("spi10");
if(g_pSfudDev_s == RT_NULL)
{
return RT_ERROR;
}
return RT_EOK;
}
INIT_COMPONENT_EXPORT(rt_hw_spi_flash_with_sfud_init);
/*********************************************************************************/
/*********************************************************************************/
/* Flash校验函数
*/
void L_CheckFlash(void)
{
static uint32_t s_DataAddr_u32 = 0;
static uint8_t s_WriteData_u8 = 0, s_CommErrCnt_u8 = 0;
uint8_t l_ReadData_u8 = 0;
rt_uint32_t e = 0;
sfud_err ret;
//地址
//每个扇区仅写1次,便转到下一个扇区
if((s_DataAddr_u32 += 4096) > 16384000) // 4096Bytes/sector, 4096sector/chip
{
s_DataAddr_u32 = 0;
}
LOG_D("W25Q128 Addr: %d ", s_DataAddr_u32);
//写入
s_WriteData_u8 ++;
ret = sfud_erase_write(g_pSfudDev_s, s_DataAddr_u32, 1, &s_WriteData_u8);
if(ret == SFUD_SUCCESS)
{
LOG_D("write: %d ", s_WriteData_u8);
}
else
{
s_CommErrCnt_u8++;
LOG_W("W25Q128 write err !");
}
//读取
ret = sfud_read(g_pSfudDev_s, s_DataAddr_u32, 1, &l_ReadData_u8);
if(ret == SFUD_SUCCESS)
{
LOG_D("read: %d ", l_ReadData_u8);
}
else
{
s_CommErrCnt_u8++;
LOG_W("W25Q128 read err !");
}
//判断
if(l_ReadData_u8 != s_WriteData_u8)
{
if(++s_CommErrCnt_u8 > 1) //连续2次校验不通过才认为失败
{
s_CommErrCnt_u8 = 1;
LOG_E("comm err !"); //检验失败
}
}
else
{
s_CommErrCnt_u8 = 0; //校验通过
}
}
/*********************************************************************************/
在合适的地方调用fal初始化函数,编译下载程序,可以在打印出的调试信息中看到Flash已经按照设置分区。
fal_init(); //fal初始化

8、添加ef_fal_port.c
在软件左上角的“导航器”中选择项目,将“packages”->“EasyFlash”->“ports”->“ef_fal_port.c”文件文件移动到“drivers”文件夹中。并将文件中宏“FAL_EF_PART_NAME”改为““easyflash””。“default_env_set”结构体数组中存储的是环境变量的key-value值,可在此定义新的环境变量。

调用EasyFlash初始化函数,编译下载程序,可以在打印出的调试信息中看到EasyFlash已经初始化成功。
easyflash_init(); //easyflash初始化

9、测试功能
在main函数中新建2个线程,1个信号量。在线程1中设置环境变量,在线程2中读取环境变量。
fal_init(); //fal初始化
easyflash_init(); //easyflash初始化
env_load(); //环境变量载入
test_sem1 = rt_sem_create("sem1", 0, RT_IPC_FLAG_FIFO);
rt_thread_t thread1 = rt_thread_create("set_env", (void *)G_SetEnv_entry, RT_NULL, 2048, 25, 10);
if (thread1 != RT_NULL)
{
rt_thread_startup(thread1);
}
rt_thread_t thread2 = rt_thread_create("read_env", (void *)G_ReadEnv_entry, RT_NULL, 1024, 25, 10);
if (thread2 != RT_NULL)
{
rt_thread_startup(thread2);
}
/*********************************************************************************/
/* 环境变量载入
*/
void env_load(void)
{
char temp[25] = {0};
unsigned int len = 0;
ef_get_env_blob("HardwareVersion",RT_NULL, 0, &len);
if(len > 15)
{
LOG_E("HardwareVersion get env too lengthy");
return;
}
ef_get_env_blob("HardwareVersion", temp, len, RT_NULL);
get_config_data.HardwareVersion=atoi(temp);
rt_memset(temp, 0, 25);
ef_get_env_blob("SoftwareVersion",RT_NULL, 0, &len);
if(len > 15)
{
LOG_E("SoftwareVersion get env too lengthy");
return;
}
ef_get_env_blob("SoftwareVersion", temp, len, RT_NULL);
get_config_data.SoftwareVersion = atoi(temp);
rt_memset(temp, 0, 25);
ef_get_env_blob("testdata1",RT_NULL, 0, &len);
if(len > 15)
{
LOG_E("testdata1 get env too lengthy");
return;
}
ef_get_env_blob("testdata1", temp, len, RT_NULL);
get_config_data.testdata1 = atoi(temp);
rt_memset(temp, 0, 25);
ef_get_env_blob("testdata2",RT_NULL, 0, &len);
if(len > 15)
{
LOG_E("testdata2 get env too lengthy");
return;
}
ef_get_env_blob("testdata2", temp, len, RT_NULL);
get_config_data.testdata2 = atoi(temp);
rt_memset(temp, 0, 25);
LOG_D("---------------ENV-------------------");
LOG_D("Heartbeat:%d", get_config_data.Heartbeat);
LOG_D("Sending_interval_val:%d", get_config_data.Sending_interval_val);
LOG_D("SoftwareVersion:%d", get_config_data.SoftwareVersion);
LOG_D("HardwareVersion:%d", get_config_data.HardwareVersion);
LOG_D("testdata1:%d", get_config_data.testdata1);
LOG_D("testdata2:%d", get_config_data.testdata2);
LOG_D("-------------------------------------");
}
/*********************************************************************************/
/*********************************************************************************/
void G_SetEnv_entry(void)
{
char chartemp[15];
int data1 = 20;
int data2 = 11;
while(1)
{
rt_memset(chartemp, 0, sizeof(chartemp));
itoa(++data1, chartemp, 10);
ef_set_env("testdata1", chartemp); //存入env中
LOG_I("write testdata1: %d", data1);
rt_memset(chartemp, 0, sizeof(chartemp));
itoa(data2 += 2, chartemp, 10);
ef_set_env("testdata2", chartemp); //存入env中
LOG_I("write testdata2: %d", data2);
rt_sem_release(test_sem1);
rt_thread_delay(2000);
}
}
/*********************************************************************************/
/*********************************************************************************/
void G_ReadEnv_entry(void)
{
char temp[25] = {0};
unsigned int len = 0;
while(1)
{
rt_sem_take(test_sem1, RT_WAITING_FOREVER);
ef_get_env_blob("testdata1",RT_NULL, 0, &len);
if(len > 15)
{
LOG_E("testdata1 get env too lengthy");
// return;
}
ef_get_env_blob("testdata1", temp, len, RT_NULL);
get_config_data.testdata1 = atoi(temp);
LOG_I("read testdata1: %d", get_config_data.testdata1);
rt_memset(temp, 0, 25);
ef_get_env_blob("testdata2", RT_NULL, 0, &len);
if(len > 15)
{
LOG_E("testdata2 get env too lengthy");
// return;
}
ef_get_env_blob("testdata2", temp, len, RT_NULL);
get_config_data.testdata2 = atoi(temp);
LOG_I("read testdata2: %d", get_config_data.testdata2);
rt_memset(temp, 0, 25);
rt_thread_delay(5);
}
}
/*********************************************************************************/
编译下载后,可以看到环境变量依次写入与读取。

2184





