用到的rt-thread studio组件有:finsh命令、DFS、Fatfs
用到的Drivers有:串口、Pin、SPI、SFUD
IDE环境:RT-Thread Studio 2.1.0
首先是打开CubeMX Settings把时钟、串口、SPI等打开
因为默认的STM32F103RC的封装是16k+256k的那种,所以要先点击左上角的芯片名字,把封装改为LQFP64,如果用的不是这种就不用改。
随后就是点击System Core—-RCC一栏的HSE和LSE都设为Crystal/Cerammic Resonator
再配置时钟树Clock Configuration,我设置为最大的频率72mhz,随后在Connectivity一栏中打开SPI1,模式为Full-Duplex Master全双工主机模式。因为频率设为了72mhz,所以在SPI1下面的Parameter Settings中要把Clock Parameters的Prescaler(for Baoud Rate)改为4。
接下来设置USART1的Mode为Asynchronous,RS232默认为Disable。
可以将PA2设为GPIO_Output,因为F103RC的片选脚为PA2.
随后就是点击Project Manager,因为改了芯片的封装,所以这里的Project Name消失了,要手动把cubemx写上去,还要ProjectLocation和Toolchain Folder Location也要和项目的路径对上,Toolchain/IDE默认为EWARM就可以了。Code Generator中选择Copy only the necessary library files。另外把Application Stucture设置为Basic
然后点击右上角Generate code生成芯片配置。
接下来是设置RT-Thread Settings
默认打开的组件有finsh命令、串口、Pin,所以我们要把接下来用到的SPI、SFUD、DFS、Fatfs单击鼠标左键打开。
其中Fatfs右键点击详细配置,把设置要处理的最大扇区大小改为4096,这是根据flash的性质设置的。
随后是点击上方的软件包的Add,把fal添加进去,这个软件包用了管理Flash和分区,右键详细配置,把FAL使用SFUD驱动程序勾选上,下面的扩展栏可以设置我们的使用FAL的设备名称,即我们的片外Flash名称。
设置好上面的组件和软件包后,按ctrl+s保存设置等待保存即可。
接下来找到drivers中的board.h,找到#define BSP_USING_SPI1,把这行的注释取消,打开SPI1,在这一步可以把文件拉到最下面,把#define BSP_USING_ON_CHIP_FLASH片上Flash也打开。
随后可以在applications目录右键添加xxx.c源文件,进行spi flash的挂载。
用到的头文件有(可能有的并没有用到,具体看实现):
#include <rtthread.h>
#include "rtdevice.h"
#include <drv_spi.h>
#include <rtconfig.h>
#include "spi_flash_sfud.h"
#include <dfs_fs.h>
#include <dfs_file.h>
#include "dfs_private.h"
#include "fal.h"
挂载spi_flash用到了rt_hw_spi_device_attach(const char bus_name, const char device_name, GPIO_TypeDef *cs_gpiox, uint16_t cs_gpio_pin)函数,参数分别为:bus_name-挂载的总线名字,选用了SPI1,所以这里填“spi1”,device_name-设备名字,这里填“spi10”表示挂载在spi1上的0号设备,gpiox是打开哪个GPIO,看芯片资料知道PA2是我们的片选脚,所以这里填“GPIOA”,后面的cs_gpio_pin填“GPIO_PIN_2”。
还用到了rt_sfud_flash_probe(const char spi_flash_dev_name, const char spi_dev_name)函数把spi flash挂载到spi10设备上
详细代码:
static int rt_hw_spi_flash_init(void)
{
__HAL_RCC_GPIOA_CLK_ENABLE();
rt_hw_spi_device_attach("spi1", "spi10", GPIOA, GPIO_PIN_2);// spi10 表示挂载在 spi1 总线上的 0 号设备,PA2是片选,这一步就可以将从设备挂在到总线中
if(RT_NULL == rt_sfud_flash_probe("w25qxx", "spi10"))
{
return -RT_ERROR;
}
return RT_EOK;
}
//初始化自动挂载设备
INIT_DEVICE_EXPORT(rt_hw_spi_flash_init);
随后我们要对挂载的flash进行分区,才能进行后续的bootloader写入以及文件系统的创建。
找到项目中fal-v0.5.0文件夹,打开里面的samples,点击README.md有详细教程,主要要设置的是fal_cfg.h,并且要把项目drivers文件夹中的drv_flash_f1.c也要做相应设置。
要注意的地方就是FAL_FLASH_DEV_TABLE里面放的就是flash分区表的内容
FAL_PART_TABLE 就是进一步如何进行分区,这个要根据具体需求设置。不过要注意的就是用文档上的stm32通用bootload创建的话,必须要有“app”、“download”两个分区。
fal_cfg.h代码如下:
#ifndef FAL_CFG_H
#define FAL_CFG_H
#include <rtconfig.h>
#include <board.h>
#define NOR_FLASH_DEV_NAME "w25qxx" //自定义的片外flash名称
#define RT_APP_PART_ADDR 0x08020000 //修改app分区的首地址为0x08020000
#define FLASH_SIZE_GRANULARITY_16K (4 * 16 * 1024)
#define FLASH_SIZE_GRANULARITY_64K (64 * 1024)
#define FLASH_SIZE_GRANULARITY_128K (1 * 128 * 1024)
#define STM32_FLASH_START_ADRESS_16K STM32_FLASH_START_ADRESS
#define STM32_FLASH_START_ADRESS_64K (STM32_FLASH_START_ADRESS_16K + FLASH_SIZE_GRANULARITY_16K)
#define STM32_FLASH_START_ADRESS_128K (STM32_FLASH_START_ADRESS_64K + FLASH_SIZE_GRANULARITY_64K)
/* ===================== Flash device Configuration ========================= /
extern const struct fal_flash_dev stm32_onchip_flash_16k;
extern const struct fal_flash_dev stm32_onchip_flash_64k;
extern const struct fal_flash_dev stm32_onchip_flash_128k;
/ ===================== Flash device Configuration ========================= /
//extern const struct fal_flash_dev stm32_onchip_flash;
extern struct fal_flash_dev nor_flash0;
/ flash device table /
#define FAL_FLASH_DEV_TABLE
{
&stm32_onchip_flash_16k,
&stm32_onchip_flash_64k,
&stm32_onchip_flash_128k,
&nor_flash0,
}
/ ====================== Partition Configuration ========================== /
#ifdef FAL_PART_HAS_TABLE_CFG
/ partition table /
#define FAL_PART_TABLE
{
{FAL_PART_MAGIC_WORD, "bootloader", "onchip_flash_16k", 0, 481024, 0},
{FAL_PART_MAGIC_WORD, "release", "onchip_flash_16k", 481024, 161024, 0},
{FAL_PART_MAGIC_WORD, "easyflash", "onchip_flash_64k", 0, 641024, 0},
{FAL_PART_MAGIC_WORD, "app", "onchip_flash_128k", 0, 1281024, 0},
{FAL_PART_MAGIC_WORD, "download", "w25qxx", 0, 110241024, 0},
{FAL_PART_MAGIC_WORD, "filesystem", "w25qxx", 110241024, 710241024, 0},
}
#endif /* FAL_PART_HAS_TABLE_CFG /
#endif / FAL_CFG_H */
这里直接进行编译是过不了的,因为没有stm32_onchip_flash_16k、stm32_onchip_flash_64k、stm32_onchip_flash_128k的定义,所以我们要去到drivers文件夹中的drv_flash_f1.c中,找到
const struct fal_flash_dev stm32_onchip_flash = { "onchip_flash", STM32_FLASH_START_ADRESS, STM_FLASH_SIZE, FLASH_PAGE_SIZE, {NULL, fal_flash_read, fal_flash_write, fal_flash_erase} };
改成相应的
const struct fal_flash_dev stm32_onchip_flash_16k = { "onchip_flash_16k", STM32_FLASH_START_ADRESS_16K, FLASH_SIZE_GRANULARITY_16K, FLASH_PAGE_SIZE, {NULL, fal_flash_read_16k, fal_flash_write_16k, fal_flash_erase_16k} };
const struct fal_flash_dev stm32_onchip_flash_64k = { "onchip_flash_64k", STM32_FLASH_START_ADRESS_64K, FLASH_SIZE_GRANULARITY_64K, FLASH_PAGE_SIZE, {NULL, fal_flash_read_64k, fal_flash_write_64k, fal_flash_erase_64k} };
const struct fal_flash_dev stm32_onchip_flash_128k = { "onchip_flash_128k", STM32_FLASH_START_ADRESS_128K, FLASH_SIZE_GRANULARITY_128K, FLASH_PAGE_SIZE, {NULL, fal_flash_read_128k, fal_flash_write_128k, fal_flash_erase_128k} };
同理,要把上面的
static int fal_flash_read(long offset, rt_uint8_t *buf, size_t size);
static int fal_flash_write(long offset, const rt_uint8_t *buf, size_t size);
static int fal_flash_erase(long offset, size_t size);
改为
static int fal_flash_read_16k(long offset, rt_uint8_t *buf, size_t size);
static int fal_flash_write_16k(long offset, const rt_uint8_t *buf, size_t size);
static int fal_flash_erase_16k(long offset, size_t size);
。。。。。。
然后就是具体函数实现中的返回
return stm32_flash_read(stm32_onchip_flash.addr + offset, buf, size);
stm32_onchip_flash改成对应的stm32_onchip_flash_16k、stm32_onchip_flash_64k、stm32_onchip_flash_128k。
然后samples中的fal_flash_sfud_port.c文件中,可以把宏定义的norflash0改为自己设置的名字“w25qxx”,修改结构体中的.name为 = “w25qxx”。
如无意外到这里可以进行spi flash的分区了。
返回我们创建的xxx.c文件,在下面添加上
static int flash_fal_init(void)
{
int res;
res = fal_init();
//if(res) {
//res = easyflash_init();
//}
return res;
}
INIT_COMPONENT_EXPORT(flash_fal_init);
或者main.c中while()循环外添加fal_init();进行flash分区。
接下来就是文件系统的挂载写入。(同样在xxx.c文件下添加代码)
先定义一个宏
#define FS_PARTITION_NAME "filesystem"
随后在我们的flash分区上创建一个块设备,调用fal_blk_device_create函数
struct rt_device *flash_dev = fal_blk_device_create(FS_PARTITION_NAME);
if (flash_dev == NULL) {
LOG_E("Can't create a block device on '%s' partition.", FS_PARTITION_NAME);
}
else {
LOG_D("Create a block device on the %s partition of flash successful.", FS_PARTITION_NAME);
}
成功后在该块设备上挂载我们的文件系统
/* 挂载 spi flash 中名为 "fs" 的分区上的文件系统 /
if (dfs_mount(flash_dev->parent.name, "/", "elm", 0, 0) == 0) { //这个dfs_mount()函数完成文件系统的挂载
LOG_I("Filesystem initialized!");
/ 初始化日志输出 /
//init_ulog_logfile();
} else {
if (dfs_mkfs("elm", flash_dev->parent.name) == 0) {
if (dfs_mount(flash_dev->parent.name, "/", "elm", 0, 0) == 0) {
LOG_I("Filesystem initialized!");
/ 初始化日志输出 */
//init_ulog_logfile();
} else {
LOG_E("Failed to mount filesystem!");
}
} else {
LOG_E("Failed to initialize filesystem!");
}
}
整个函数代码为:
static int app_mount(void)
{
/* 在 spi flash 中名为 "fs" 的分区上创建一个块设备 */
struct rt_device flash_dev = fal_blk_device_create(FS_PARTITION_NAME);
if (flash_dev == NULL) {
LOG_E("Can't create a block device on '%s' partition.", FS_PARTITION_NAME);
}
else {
LOG_D("Create a block device on the %s partition of flash successful.", FS_PARTITION_NAME);
}
/ 挂载 spi flash 中名为 "fs" 的分区上的文件系统 /
if (dfs_mount(flash_dev->parent.name, "/", "elm", 0, 0) == 0) {
LOG_I("Filesystem initialized!");
/ 初始化日志输出 /
//init_ulog_logfile();
} else {
if (dfs_mkfs("elm", flash_dev->parent.name) == 0) {
if (dfs_mount(flash_dev->parent.name, "/", "elm", 0, 0) == 0) {
LOG_I("Filesystem initialized!");
/ 初始化日志输出 */
//init_ulog_logfile();
} else {
LOG_E("Failed to mount filesystem!");
}
} else {
LOG_E("Failed to initialize filesystem!");
}
}
return 0;
}
//初始化自动调用
INIT_ENV_EXPORT(app_mount);
接下来就是bootloader的制作和下载。
首先就是跟着rt-thread官方stm32通用bootloader文档完成bootloader的制作,然后通过jlink等烧录工具烧录到芯片上。
需要注意的就是,bootloader制作时的app、download的偏移要和你分区的偏移对上,我这里因为rct6的片上flash只有256k,bootload还要128k保留空间地址,所以我把app偏移地址设为0x20000,大小为128k,download放在了片外flash上,因为片外flash起始地址为0,所以偏移地址设为0,大小设置为1024,剩下的71024留给文件系统使用。另外用到片外flash要勾选支持spi flash,引脚都要和芯片手册对应上。
看资料知道rct6的ROM为256k、RAM为48k,然后点击右上角生成等待即可。
烧写bootloader的起始地址选*0x08000000,芯片选对应芯片。
烧写bootload后再发现程序跑不动,因为这里烧写的app地址偏移了,我们要去项目的linkscripts-stm32f103rc文件夹的link.lds修改ROM(rx)起始地址为0x08020000,大小为128k(256减去bootload保留的128),另外要去libraries-CMSIS-DEVICE-ST-STM32F1XX-Source-Templates找到system_stm32f1xx.c文件,找到
/*#define VECT_TAB_SRAM /
#define VECT_TAB_OFFSET 0x00000000U /!< Vector Table base offset field.
This value must be a multiple of 0x200. */
修改0x00000000U为0x20000U,我们的app偏移量。
接下来返回到我们的fal_cfg.h文件,添加这个宏
#define RT_APP_PART_ADDR 0x08020000 //修改app分区的首地址为0x08020000
然后去到我们的xxx.c文件,添加中断向量表的重定向函数
/*
- board 中断向量表重定向
/
static int ota_app_vtor_reconfig(void)
{
#define NVIC_VTOR_MASK 0x3FFFFF80
/ Set the Vector Table base location by user application firmware definition */
SCB->VTOR = RT_APP_PART_ADDR & NVIC_VTOR_MASK;
return 0;
}
INIT_BOARD_EXPORT(ota_app_vtor_reconfig);
好像差不多就这样了,第一次写log,有说错或讲得不好的地方请多多包涵。
原作者:巴菲特不非