我前面介紹過RT-thread的一種OTA方案,可以通過串口或者網(wǎng)絡(luò)進(jìn)行遠(yuǎn)程升級(jí),那今天在原來的這套方案的基礎(chǔ)上做一些修改,實(shí)現(xiàn)U盤升級(jí)。
相比于串口和網(wǎng)絡(luò),U盤升級(jí)在某些方面有很大的優(yōu)勢(shì),首先它不需要網(wǎng)絡(luò),對(duì)于那些不具備上網(wǎng)功能的設(shè)備來說很方便,其次它不需要連接數(shù)據(jù)線,在距離上有優(yōu)勢(shì),說到這,可能有人會(huì)說,u盤要插到設(shè)備上才能用,距離怎么就有優(yōu)勢(shì)了呢?
是的,U盤升級(jí)確實(shí)需要有人到設(shè)備這邊手動(dòng)操作,但是因?yàn)閁盤是很通用的設(shè)備,即使是非研發(fā)人員也可以操作,不需要燒錄器,也不需要安裝一些驅(qū)動(dòng)和軟件,只需要找一個(gè)U盤把升級(jí)固件放進(jìn)去,然后插到設(shè)備上升級(jí)即可。
比如有個(gè)客戶安裝了我們的設(shè)備,但是后來軟件要進(jìn)行升級(jí)優(yōu)化,如果這個(gè)設(shè)備是支持U盤升級(jí)的那么我就可以把升級(jí)的固件發(fā)給客戶,讓他自己用一個(gè)U盤來升級(jí)設(shè)備,這樣一來,我們就不需要跑到現(xiàn)場(chǎng)去燒錄固件了。
好了,廢話不多說,馬上開整。
一、掛載U盤
使用U盤升級(jí),第一步要先把U盤的讀寫調(diào)試好,關(guān)于U盤的使用,可以看下我之前的博客,有很詳細(xì)的介紹,這里就不多說了。
RT-thread應(yīng)用講解——U盤(usb host)
二、使能OTA
要想遠(yuǎn)程升級(jí),那肯定先得有OTA的功能,關(guān)于OTA的使用我在前面的博客里面也詳細(xì)介紹過了,不知道的同學(xué)可以先去看下。
RT-thread應(yīng)用講解——OTA
OTA部分只要要完成bootloader的制作和app的制作即可。這兩部分都調(diào)試好了之后就可以加入U(xiǎn)盤升級(jí)的代碼。
三、U盤升級(jí)代碼
前面把U盤的掛載和OTA都調(diào)試好了之后就可以使用U盤來進(jìn)行OTA了。
示例代碼如下:
#include <rtthread.h>
#include <stdio.h>
#include <stdbool.h>
#include <finsh.h>
#include <fal.h>
#include <dfs_posix.h>
#define DBG_SECTION_NAME "ota_usb"
#define DBG_LEVEL DBG_LOG
#define DBG_COLOR
#include <rtdbg.h>
/* 固件版本號(hào) */
#define APP_VERSION "1.0.0"
/* 固件名稱 */
#define USBH_UPDATE_FN "/rtthread.rbl"
/* 固件下載分區(qū)名稱 */
#define DEFAULT_DOWNLOAD_PART "download"
static char* recv_partition = DEFAULT_DOWNLOAD_PART;
rt_sem_t ota_sem = RT_NULL;
static void print_progress(size_t cur_size, size_t total_size)
{
static unsigned char progress_sign[100 + 1];
uint8_t i, per = cur_size * 100 / total_size;
if (per > 100)
{
per = 100;
}
for (i = 0; i < 100; i++)
{
if (i < per)
{
progress_sign[i] = '=';
}
else if (per == i)
{
progress_sign[i] = '>';
}
else
{
progress_sign[i] = ' ';
}
}
progress_sign[sizeof(progress_sign) - 1] = '?';
LOG_I("?33[2A");
LOG_I("Download: [%s] %d%%", progress_sign, per);
}
static int ota_usb_download_entry(void* parameter)
{
DIR *dirp;
static size_t update_file_total_size, update_file_cur_size;
static const struct fal_partition * dl_part = RT_NULL;
static int fd;
static rt_uint8_t buf[1024];
static rt_err_t result;
struct stat file;
rt_kprintf("The current version of APP firmware is %sn", APP_VERSION);
while(1)
{
result = rt_sem_take(ota_sem, RT_WAITING_FOREVER);
if(result == RT_EOK)
{
rt_kprintf("Default save firmware on download partition.n");
rt_kprintf("Warning: usb has started! This operator will not recovery.n");
/* 查詢固件大小 */
result = stat(USBH_UPDATE_FN, &file);
if(result == RT_EOK)
{
LOG_D(""USBH_UPDATE_FN" file size is : %dn", file.st_size);
}
else
{
LOG_E(""USBH_UPDATE_FN" file not fonud.");
goto __exit;
}
if(file.st_size <= 0)
{
LOG_E(""USBH_UPDATE_FN" file is empty.");
goto __exit;
}
/* 獲取"download"分區(qū)信息并清除分區(qū)數(shù)據(jù) */
if ((dl_part = fal_partition_find(recv_partition)) == RT_NULL)
{
LOG_E("Firmware download failed! Partition (%s) find error!", recv_partition);
goto __exit;
}
if (file.st_size > dl_part->len)
{
LOG_E("Firmware is too large! File size (%d), '%s' partition size (%d)", file.st_size, recv_partition, dl_part->len);
}
if (fal_partition_erase(dl_part, 0, file.st_size) < 0)
{
LOG_E("Firmware download failed! Partition (%s) erase error!", dl_part->name);
}
/* 以只讀模式打開固件 */
fd = open(USBH_UPDATE_FN, O_RDONLY);
if (fd >= 0)
{
do
{
/* 讀取u盤的固件,一次讀1024字節(jié) */
update_file_cur_size = read(fd, buf, sizeof(buf));
/* 把固件寫入片外flash的download分區(qū) */
if (fal_partition_write(dl_part, update_file_total_size, buf, update_file_cur_size) < 0)
{
LOG_E("Firmware download failed! Partition (%s) write data error!", dl_part->name);
close(fd);
goto __exit;
}
update_file_total_size += update_file_cur_size;
print_progress(update_file_total_size, file.st_size);
}
while (update_file_total_size != file.st_size);
close(fd);
}
else
{
LOG_E("check: open file for read failedn");
goto __exit;
}
rt_kprintf("Download firmware to flash success.n");
rt_kprintf("System now will restart...rn");
/* wait some time for terminal response finish */
rt_thread_delay(rt_tick_from_millisecond(200));
/* Reset the device, Start new firmware */
extern void rt_hw_cpu_reset(void);
rt_hw_cpu_reset();
/* wait some time for terminal response finish */
rt_thread_delay(rt_tick_from_millisecond(200));
__exit:
LOG_E("download rtthread.rbl file failed");
}
}
}
void ota_usb_init(void)
{
fal_init();
ota_sem = rt_sem_create("otasem", 0, RT_IPC_FLAG_FIFO);
rt_thread_t thread1 = rt_thread_create("ota_usb", ota_usb_download_entry, RT_NULL, 4096, 26, 10);
if (thread1 != RT_NULL)
{
rt_thread_startup(thread1);
}
}
INIT_APP_EXPORT(ota_usb_init);
static rt_uint8_t ota_usb_start(void)
{
rt_sem_release(ota_sem);
return RT_EOK;
}
MSH_CMD_EXPORT(ota_usb_start, OTA start);
四、運(yùn)行測(cè)試
先做好一個(gè)要升級(jí)的固件(rtthread.rbl)放到U盤里,插入U(xiǎn)盤之后,查看U盤是否有該文件。
通過調(diào)用命令開始升級(jí)。
提示:用命令只是為了方便調(diào)試,實(shí)際使用可以通過實(shí)體按鍵或屏幕按鍵來觸發(fā)升級(jí)。
至此,整個(gè)升級(jí)的流程就走完了。
五、結(jié)束語(yǔ)
好了,關(guān)于U盤升級(jí)的介紹就到這里,整個(gè)流程看著挺復(fù)雜,但是知道原理之后回頭再看,其實(shí)結(jié)構(gòu)還是很清晰的,如果還有什么問題,歡迎在評(píng)論區(qū)留言。如果這篇文章能夠幫到你,就給我點(diǎn)個(gè)贊吧,如果想了解更多RT-thread和單片機(jī)的內(nèi)容,可以關(guān)注一下博主,后續(xù)我還會(huì)繼續(xù)分享更多的經(jīng)驗(yàn)給大家。
教程相關(guān)源碼:https://download.csdn.net/download/ShenZhen_zixian/12880749
RT-thread相關(guān)教程匯總:https://blog.csdn.net/ShenZhen_zixian/article/details/120563891