由于我的设备不能联网, 又想把最新的固件下载到客户已经买的设备上, 加上客户啥也不懂, 我又不想为了更新一个程序这种小事跑一趟, 只能把固件发给客户让他保存到U盘或SD上去更新程序.
首先这是一个简要的教程, 程序我已经完成有一段时间了, 具体细节已经忘记了, 等再有机会写一个更详细的开发配置流程, 这次只把核心写出来.
单片机: STM32F407
系统: RTThread4.1.1 (bootloader和app相同)
参数配置杂项代码
#ifndef APPLICATIONS_APP_CONFIG_H_
#define APPLICATIONS_APP_CONFIG_H_
/** flash **/
#define DRV_FLASH
#ifdef DRV_FLASH
#define SPI_FLASH_BUS "spi3"
#define SPI_FLASH_ATTACH "spi30"
#define SPI_FLASH_DEVICE FAL_USING_NOR_FLASH_DEV_NAME
#define FILE_SYS_PARTITION "filesys"
#define SPI_FLASH_PATH "/"
#define SPI_FLASH_CS_GPIO A
#define SPI_FLASH_CS_PIN 15
#endif
#include "gpio_define.h"
#endif /* APPLICATIONS_APP_CONFIG_H_ */
#ifndef APPLICATIONS_COMMON_GPIO_DEFINE_H_
#define APPLICATIONS_COMMON_GPIO_DEFINE_H_
#define XXX_GPIOx(x) GPIO##x
#define XXX_PINx(x) GPIO_PIN_##x
#define GPIOx(x) XXX_GPIOx(x)
#define PINx(x) XXX_PINx(x)
#endif /* APPLICATIONS_COMMON_GPIO_DEFINE_H_ */
挂载SPI Flash
/*
* Copyright (c) 2006-2021, RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*
* Change Logs:
* Date Author Notes
* 2024-01-15 admin the first version
*/
#include <rtthread.h>
#include <rtdevice.h>
#include "dfs_fs.h"
#include "sfud.h"
#include "drv_spi.h"
#include "spi_flash_sfud.h"
#include <fal.h>
#include "rtconfig.h"
#include "app_config.h"
#ifdef DRV_FLASH
/**
* attach flash device in spi1 bus
* @return
*/
static int rt_hw_spi_flash_init()
{
rt_hw_spi_device_attach(SPI_FLASH_BUS, SPI_FLASH_ATTACH, GPIOx(SPI_FLASH_CS_GPIO), PINx(SPI_FLASH_CS_PIN));
if (rt_sfud_flash_probe(SPI_FLASH_DEVICE, SPI_FLASH_ATTACH) == RT_NULL)
{
rt_kprintf("spi flash probe failed!\n");
}
return 0;
}
INIT_DEVICE_EXPORT(rt_hw_spi_flash_init);
//INIT_COMPONENT_EXPORT(rt_hw_spi_flash_init);
static int usr_fal_init(){
fal_init();
return 0;
}
INIT_COMPONENT_EXPORT(usr_fal_init);
extern int access(const char *path, int amode);
/**
* mount flash block device
* @return
*/
static int mnt_init()
{
#ifdef FILE_SYS_PARTITION
struct rt_device *flash_dev = fal_blk_device_create(FILE_SYS_PARTITION);
#else
struct rt_device *flash_dev = rt_device_find(SPI_FLASH_DEVICE);
#endif
if(flash_dev == RT_NULL){
rt_kprintf("not found block device\n");
return -1;
}
if (dfs_mount(flash_dev->parent.name, SPI_FLASH_PATH, "elm", 0, 0) == 0)
{
// rt_kprintf("dfs mount ok\n");
#ifdef UDISK_MOUNTPOINT
if(access(UDISK_MOUNTPOINT, 0) != 0){
mkdir(UDISK_MOUNTPOINT, 0);
}
#endif
#ifdef SDIO_MOUNT_PATH
if(access(SDIO_MOUNT_PATH, 0) != 0){
mkdir(SDIO_MOUNT_PATH, 0);
}
#endif
}
else
{
rt_kprintf("dfs mount failed\n");
dfs_mkfs("elm", flash_dev->parent.name);
rt_kprintf("tried format flash device\n");
rt_kprintf("reset device!\n");
rt_thread_mdelay(10000);
rt_hw_cpu_reset();
}
return 0;
}
INIT_ENV_EXPORT(mnt_init);
#endif
fal分区
/*
* Copyright (c) 2006-2021, RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*
* Change Logs:
* Date Author Notes
* 2024-01-31 admin the first version
*/
#ifndef _FAL_CFG_H_
#define _FAL_CFG_H_
#include <rtconfig.h>
#include <board.h>
#define FLASH_SIZE_GRANULARITY_16K (16*16*1024)
#define FLASH_SIZE_GRANULARITY_64K (2*64*1024)
#define FLASH_SIZE_GRANULARITY_128K (5*128*1024)
#define STM32_FLASH_START_ADRESS_16K ROM_START
#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)
#define RT_APP_PART_ADDR STM32_FLASH_START_ADRESS_128K
#define NOR_FLASH_DEV_NAME FAL_USING_NOR_FLASH_DEV_NAME
/* ===================== Flash device Configuration ========================= */
//extern const struct fal_flash_dev stm32_onchip_flash;
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;
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, "bl", "onchip_flash_16k", 0, FLASH_SIZE_GRANULARITY_16K, 0}, \
{FAL_PART_MAGIC_WORD, "prm", "onchip_flash_64k", 0, FLASH_SIZE_GRANULARITY_64K, 0}, \
{FAL_PART_MAGIC_WORD, "app", "onchip_flash_128k", 0, FLASH_SIZE_GRANULARITY_128K, 0}, \
{FAL_PART_MAGIC_WORD, "ef", NOR_FLASH_DEV_NAME, 0, 128*1024, 0}, \
{FAL_PART_MAGIC_WORD, "factory", NOR_FLASH_DEV_NAME, 128*1024, 512*1024, 0}, \
{FAL_PART_MAGIC_WORD, "download", NOR_FLASH_DEV_NAME, (128+512)*1024, 512*1024, 0}, \
{FAL_PART_MAGIC_WORD, "filesys", NOR_FLASH_DEV_NAME, (128+1024)*1024, ((8*1024)-(128+1024))*1024, 0}, \
}
#endif /* FAL_PART_HAS_TABLE_CFG */
#endif /* _FAL_CFG_H_ */
/*
* Copyright (c) 2006-2021, RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*
* Change Logs:
* Date Author Notes
* 2024-02-01 admin the first version
*/
#include <rtthread.h>
#include "rtconfig.h"
#include <fal.h>
#include <dirent.h>
#include <string.h>
#include <qboot.h>
#define DBG_TAG "FW"
#define DBG_LVL DBG_LOG
#include <rtdbg.h>
#define DOWNLOAD_PART QBOOT_DOWNLOAD_PART_NAME
#define FILE_BUFF_SIZE (4*1024)
static char file_buf[FILE_BUFF_SIZE] = {0};
static const struct fal_partition * dl_part = RT_NULL;
typedef struct cpu_info
{
union
{
u8 byte[12];
u32 id[3];
} v;
} cpu_t;
#define CPU_ID_BASE_ADDR (u32*)(0x1FFF7A10)
static cpu_t cpu_id;
int __cpu_id_init()
{
rt_memcpy(&cpu_id, CPU_ID_BASE_ADDR, sizeof(cpu_t));
return 0;
}
INIT_BOARD_EXPORT(__cpu_id_init);
u32 GET_CPU_ID(u8 index)
{
return index < 3 ? cpu_id.v.id[index] : 0;
}
u32 get_id_code(){
return GET_CPU_ID(0)^GET_CPU_ID(1)^GET_CPU_ID(2);
}
static rt_err_t check_mount(){
rt_device_t ret;
ret = rt_device_find("udisk0");
if(ret != RT_NULL){
return RT_EOK;
}
ret = rt_device_find("ud0-0");
if(ret != RT_NULL){
return RT_EOK;
}
return RT_ERROR;
}
static size_t get_file_size(FILE *f){
fseek(f, 0, SEEK_END);
size_t ret = ftell(f);
rewind(f);
return ret;
}
rt_err_t fw_load(const char* fw_path){
rt_err_t ret = RT_EOK;
char* recv_partition = DOWNLOAD_PART;
FILE *f = fopen(fw_path, "rd");
if(f == NULL){
LOG_E("Open file error!");
ret = RT_ERROR;
goto _exit;
}
size_t fw_file_size = get_file_size(f);
LOG_I("Firmware file size:%d byte", fw_file_size);
if ((dl_part = fal_partition_find(recv_partition)) == RT_NULL)
{
LOG_E("Partition (%s) find error!\n", recv_partition);
ret = RT_ERROR;
goto _exit;
}
if(fw_file_size > dl_part->len){
LOG_E("Firmware is too large! File size (%d), '%s' partition size (%d)", fw_file_size, dl_part->name, dl_part->len);;
ret = RT_ERROR;
goto _exit;
}
if (fal_partition_erase(dl_part, 0, fw_file_size) < 0)
{
LOG_E("Firmware download failed! Partition (%s) erase error!", dl_part->name);
ret = RT_ERROR;
goto _exit;
}
int buff_block_len = fw_file_size / FILE_BUFF_SIZE;
int buff_block_count = 0;
size_t update_file_cur_size = 0;
do{
int read_size = fw_file_size > FILE_BUFF_SIZE? FILE_BUFF_SIZE:fw_file_size;
fread(file_buf, 1, read_size, f);
if (fal_partition_write(dl_part, update_file_cur_size, (uint8_t*)file_buf, read_size) < 0)
{
LOG_E("Firmware download failed! Partition (%s) write data error!", dl_part->name);
ret = RT_ERROR;
goto _exit;
}
update_file_cur_size += read_size;
fw_file_size -= read_size;
rt_kprintf("\r[%d/%d] %s=>download, replicating firmware file...", buff_block_count, buff_block_len, fw_path);
buff_block_count ++;
}while(fw_file_size != 0);
_exit:
if(f != NULL)
fclose(f);
return ret;
}
rt_err_t src_load(const char* src_path){
rt_err_t ret = RT_EOK;
DIR *dir = opendir(src_path);
struct dirent *ent;
if(dir == NULL){
LOG_E("Not found dir: %s", src_path);
ret = RT_ERROR;
goto _exit;
}
while((ent = readdir(dir)) != NULL){
if(strcmp(ent->d_name, "System Volume Information") == 0)
continue;
rt_kprintf("copy %s ", ent->d_name);
FILE *f_in;
{
char path_buff[64] = {0};
strcat(path_buff,src_path);
strcat(path_buff,"/");
strcat(path_buff,ent->d_name);
f_in = fopen(path_buff, "rb");
}
FILE *f_out;
{
char path_buff[32] = {0};
strcat(path_buff,"/");
strcat(path_buff,ent->d_name);
f_out = fopen(path_buff, "wb");
}
size_t file_size = get_file_size(f_in);
rt_kprintf("size: %d byte\n", file_size);
do{
int read_size = file_size > FILE_BUFF_SIZE? FILE_BUFF_SIZE:file_size;
fread(file_buf, 1, read_size, f_in);
fwrite(file_buf, 1, read_size, f_out);
if(file_size > FILE_BUFF_SIZE)
fseek(f_out, read_size, SEEK_CUR);
file_size -= read_size;
}while(fseek(f_in, FILE_BUFF_SIZE, SEEK_CUR) == 0 && file_size != 0);
fclose(f_in);
fclose(f_out);
}
_exit:
if(dir != NULL)
closedir(dir);
return ret;
}
extern int access(const char *path, int amode);
int fw_from_udisk(){
if(check_mount() != RT_EOK){
return 0;
}
const char* fw_path = UDISK_MOUNTPOINT"/new.fw";
// const char* fw_path_old = UDISK_MOUNTPOINT"/old.fw";
const char* src_path = UDISK_MOUNTPOINT"/new_i18n";
// const char* src_path_old = UDISK_MOUNTPOINT"/old_i18n";
LOG_I("Check the firmware in the Udisk");
if(access(fw_path, 4)!= 0){
LOG_E("not found %s", fw_path);
}
else{
if(fw_load(fw_path) == RT_EOK){
// rename(fw_path, fw_path_old);
// remove(fw_path);
// LOG_I("success: %s -> %s", fw_path, fw_path_old);
LOG_I("success: %s", fw_path);
}
else{
LOG_E("load fw failed!");
}
}
if(access(src_path, 4)!= 0){
LOG_E("not found %s", src_path);
}
else{
if(src_load(src_path) == RT_EOK){
// rename(src_path, src_path_old);
// remove(src_path);
LOG_I("success: %s", src_path);
}
else{
LOG_E("load src failed!");
}
}
return 0;
}
int fw_copy_init(){
qbt_hook_register(fw_from_udisk);
return 0;
}
INIT_ENV_EXPORT(fw_copy_init);
int copy_fw(int argc, char **argv){
if(strcmp(argv[1], "src") == 0){
src_load(argv[2]);
}
else if(strcmp(argv[1], "fw") == 0) {
fw_load(argv[2]);
}
else{
rt_kprintf("src [dir_path]\n");
rt_kprintf("fw [fw_path]\n");
}
return 0;
}
MSH_CMD_EXPORT(copy_fw, download firmware from disk);
其中 fw_load
函数是下载固件的流程, src_load
是复制一些资源文件的流程
其中 qbt_hook_register
函数是我自己加的, 为了让把下载流程加到qboot启动流程中
在 qboot.c
文件里
这里注意一下, 由于U盘挂载需要时间, 所以, 启动延时不能设置太低
好这就改完了, 在app固件里设置偏移地址
再设置中断向量表地址对齐就可以启动app了
#if 0
#include <rtthread.h>
#include <board.h>
#define RT_APP_PART_ADDR 0x08060000
static int app_vtor_reconfig(){
#define NVIC_VTOR_MASK 0xFFFFFF80
SCB->VTOR = RT_APP_PART_ADDR & NVIC_VTOR_MASK;
return 0;
}
INIT_BOARD_EXPORT(app_vtor_reconfig);
#endif
感谢分享👍