帶串口屏顯示的Bootloader
本程序編寫基于秉火霸道STM32F103ZET6運(yùn)行環(huán)境。
在實(shí)際的產(chǎn)品開發(fā)中,一般包含:
-
1、BootLoader 引導(dǎo)程序
-
2、APP_BAK 應(yīng)用程序備份恢復(fù)區(qū)
-
3、APP 應(yīng)用程序
網(wǎng)上很多講解這方面的知識(shí)感覺很高端,讓人覺得這是一個(gè)牛逼的東西,但我是這么來理解的,它們倆都是普通的應(yīng)用程序,只不過把這些普通的應(yīng)用程序分別在單片機(jī)的FLASH中分開三個(gè)區(qū)域來存儲(chǔ)而已,然后程序通過指針來實(shí)現(xiàn)偏移,跳轉(zhuǎn)到其中的某個(gè)區(qū)域,來執(zhí)行對(duì)應(yīng)區(qū)域的程序,僅此而已,再牛逼點(diǎn)的Linux,不也一樣是這樣么?
那這樣子做有什么好處呢?
打個(gè)比方,像很多公司,每次燒寫程序的時(shí)候都要把機(jī)子拆開再燒寫,再裝回去,這樣很麻煩,又影響生產(chǎn)效率,那我們?cè)趺磥韺?shí)現(xiàn)呢?
-
1、假設(shè)我的設(shè)備沒有USB口,但有wifi或者其它無線模塊,我就可以在bootloader端實(shí)現(xiàn)一個(gè)與云端通信的程序,用來接受云端的數(shù)據(jù)(APP),然后拷貝到APP的備份區(qū),這個(gè)時(shí)候做下檢驗(yàn),確保備份區(qū)的程序和云端的程序是一致的,然后就可以通過指針跳轉(zhuǎn)到備份區(qū)來運(yùn)行新的程序了,如果發(fā)現(xiàn)更新的程序有貓膩,還可以恢復(fù)回原來的應(yīng)用程序(程序員自己去實(shí)現(xiàn)這個(gè)邏輯)。
-
2、假設(shè)我的設(shè)備只有一個(gè)USB口,我既要讓它能下載程序,又要讓它能訪問存儲(chǔ)在外掛FLASH里的數(shù)據(jù)(比如是個(gè)EXCEL表格或者其它用戶數(shù)據(jù)),那怎么來做呢?不可能用戶程序在跑的過程中你給它下載程序吧?
最好的做法是這樣的,將它分成兩個(gè)程序來做,分別是BootLoader、APP
比如你的設(shè)備有好幾個(gè)按鍵,分別是左、右、確認(rèn)、返回、電源。
我們以第2點(diǎn)來說明:
一、BootLoader完成的功能
1.1、當(dāng)檢測(cè)到用戶同時(shí)按下左+確認(rèn)+電源按鍵時(shí),此時(shí)進(jìn)入DFU(Device Firmware Upgrade)固件更新模式,用戶可以通過USB線將設(shè)備和PC端連接起來,然后打開上位機(jī)將已經(jīng)制作好的應(yīng)用程序(xxx.dfu)燒寫到STM32芯片的APP區(qū)域。
1.2、正常啟動(dòng)模式下,沒有識(shí)別到左+確認(rèn)+電源按鍵,由BootLoader程序跳到到用戶APP執(zhí)行,此時(shí)進(jìn)入到APP。
二、用戶APP完成的功能
2.1、完成一系列驅(qū)動(dòng)的初始化,F(xiàn)atfs等等。。。
2.2、當(dāng)識(shí)別到有USB線將設(shè)備和PC端連接時(shí),掛載FLASH里的某個(gè)盤,然后讀取數(shù)據(jù)(用戶自己去實(shí)現(xiàn))。
這樣規(guī)劃起來感覺就簡單多了,邏輯也很清晰,接下來開始實(shí)現(xiàn)一個(gè)帶串口屏顯示的最簡單的BootLoader,功能只實(shí)現(xiàn)程序跳轉(zhuǎn),后續(xù)再慢慢增加新的功能,讓它更強(qiáng)大。
三、配置基本參數(shù)
3.1、配置一個(gè)LED
3.2、配置串口2(用來實(shí)現(xiàn)串口打印)和串口4(用來驅(qū)動(dòng)串口屏
3.3、配置時(shí)鐘然后生成工程
四、畫串口屏界面
這里采用的是大彩科技的串口屏,給大家看下效果:
然后一通胡畫得到下面這個(gè)界面。
如圖所示,在BootLoader頁面(屏幕ID為0)這里添加了一個(gè)進(jìn)度條(控件2)和一張圖片(控件3),圖片所示的二維碼是我的微信公眾號(hào)。控件1是標(biāo)題,控件4是我們用來做提示的。
五、編寫Bootloader程序
1、定義調(diào)試串口,用于看打印信息。
//定義printf的重定向函數(shù)fputc,滿足串口調(diào)試打印
int fputc(int ch, FILE* file)
{
return HAL_UART_Transmit(&huart2, (uint8_t*)&ch, 1, 100);
}
2、根據(jù)串口屏手冊(cè)定義串口屏操作接口
//串口屏頁面ID===>BootLoader在第0個(gè)頁面
#define BOOTLOADER_PAGE 0
//進(jìn)度條控件,為控件2(在BootLoader的第0個(gè)頁面)
#define PROCESS_BAR_CONTROLD_ID 2
//字符串顯示控件,為控件4(在BootLoader的第0個(gè)頁面)
#define TIP 4
void uart4_send_byte(uint8_t one_byte)
{
uint32_t ulReturn;
HAL_UART_Transmit(&huart4, &one_byte, 1, 1000);
while(__HAL_UART_GET_FLAG(&huart4, UART_FLAG_TXE) != SET);
}
#define TX_8(P1) uart4_send_byte((P1)&0xFF) //發(fā)送單個(gè)字節(jié)
#define TX_8N(P,N) SendNU8((u8 *)P,N) //發(fā)送N個(gè)字節(jié)
#define TX_16(P1) TX_8((P1)>>8);TX_8(P1) //發(fā)送16位整數(shù)
#define TX_16N(P,N) SendNU16((u16 *)P,N) //發(fā)送N個(gè)16位整數(shù)
#define TX_32(P1) TX_16((P1)>>16);TX_16((P1)&0xFFFF) //發(fā)送32位整數(shù)
#define BEGIN_CMD() TX_8(0XEE)
#define END_CMD() TX_32(0XFFFCFFFF)
//跳轉(zhuǎn)頁面
void SetScreen(uint16_t screen_id)
{
BEGIN_CMD();
TX_8(0xB1);
TX_8(0x00);
TX_16(screen_id);
END_CMD();
}
//設(shè)置進(jìn)度條
void SetProgressValue(uint16_t screen_id, uint16_t control_id, uint32_t value)
{
BEGIN_CMD();
TX_8(0xB1);
TX_8(0x10);
TX_16(screen_id);
TX_16(control_id);
TX_32(value);
END_CMD();
}
//發(fā)送字符串
void SendStrings(uint8_t *str)
{
while(*str)
{
TX_8(*str);
str++;
}
}
//設(shè)置文本
void SetTextValue(uint16_t screen_id, uint16_t control_id, uint8_t *str)
{
BEGIN_CMD();
TX_8(0xB1);
TX_8(0x10);
TX_16(screen_id);
TX_16(control_id);
SendStrings(str);
END_CMD();
}
3、編寫B(tài)ootLoader程序邏輯
程序主要存儲(chǔ)在STM32的內(nèi)部FLASH中,我們來看看這張圖,我們希望開機(jī)的第一個(gè)程序就是BootLoader,然后通過BootLoader再跳轉(zhuǎn)到APP,所以BootLoader的起始執(zhí)行地址不變,為0x8000000。
那我們的APP在放在哪里呢?我這里把APP放在0x8002000這個(gè)位置,所以在BootLoader程序中定義一個(gè)宏,代表APP的地址。
//APP在內(nèi)部FLASH中的位置
#define APP_RUNING_ADDRESS 0x8002000
實(shí)現(xiàn)主程序
typedef void (*pFunction)(void);
/**
* @brief The application entry point.
* @retval int
*/
int main(void)
{
/* USER CODE BEGIN 1 */
int timeout = 10 ;
int Process_Bar_Update = 0 ;
uint32_t JumpAddress;
pFunction Jump_To_Application;
/* USER CODE END 1 */
/* MCU Configuration--------------------------------------------------------*/
/* Reset of all peripherals, Initializes the Flash interface and the Systick. */
HAL_Init();
/* USER CODE BEGIN Init */
/* USER CODE END Init */
/* Configure the system clock */
SystemClock_Config();
/* USER CODE BEGIN SysInit */
/* USER CODE END SysInit */
/* Initialize all configured peripherals */
MX_GPIO_Init();
MX_USART2_UART_Init();
MX_UART4_Init();
/* USER CODE BEGIN 2 */
//解鎖內(nèi)部FLASH
HAL_FLASH_Unlock();
printf("進(jìn)入BootLoader.....\n");
SetTextValue(BOOTLOADER_PAGE,TIP,(uint8_t *)"正在啟動(dòng)中...");
SetScreen(0);
while(timeout--)
{
printf("................\n");
HAL_GPIO_TogglePin(LED_GPIO_Port,LED_Pin);
SetProgressValue(BOOTLOADER_PAGE,PROCESS_BAR_CONTROLD_ID,Process_Bar_Update);
Process_Bar_Update += 10;
HAL_Delay(200);
}
Process_Bar_Update = 0 ;
SetProgressValue(BOOTLOADER_PAGE,PROCESS_BAR_CONTROLD_ID,100);
printf("即將進(jìn)入用戶程序.....\n");
SetTextValue(BOOTLOADER_PAGE,TIP,(uint8_t *)"即將進(jìn)入用戶程序...");
if (((*(__IO uint32_t*)APP_RUNING_ADDRESS) & 0x2FFE0000 ) == 0x20000000)
{
//這一句一定要加上,否則跳轉(zhuǎn)到APP后程序會(huì)跑飛
__disable_irq();
//最好把使用到的外設(shè)失能
HAL_DeInit();
//最好把使用到的時(shí)鐘失能
HAL_RCC_DeInit();
//跳轉(zhuǎn)到用戶代碼
JumpAddress = *(__IO uint32_t*) (APP_RUNING_ADDRESS + 4);
Jump_To_Application = (pFunction) JumpAddress;
//初始化用戶程序的堆棧指針
__set_MSP(*(__IO uint32_t*) APP_RUNING_ADDRESS);
Jump_To_Application();
}
else
{
printf("當(dāng)前地址%p沒有用戶程序\n",(uint32_t *)APP_RUNING_ADDRESS);
HAL_DeInit();
}
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
}
/* USER CODE END 3 */
}
運(yùn)行結(jié)果:
接下來跳轉(zhuǎn)到我開發(fā)的APP程序:
指定APP程序向量表偏移
APP界面:打開報(bào)警燈:
案例程序以及UI工程下載
BootLoader+UI
鏈接:https://pan.baidu.com/s/1XBkneZOQqrDSo4JU27kkbQ
提取碼:b1m7
復(fù)制這段內(nèi)容后打開百度網(wǎng)盤手機(jī)App,操作更方便哦
應(yīng)用程序還不完善,后續(xù)再提供。
往期精彩分享
分享一個(gè)很好用的按鍵組件
分享一個(gè)好用的C語言.ini文件的解析庫
分享一個(gè)非常有用且簡單C語言測(cè)試框架
分享一個(gè)自己量產(chǎn)項(xiàng)目上的集成測(cè)試軟件MTTEST
免責(zé)聲明:本文內(nèi)容由21ic獲得授權(quán)后發(fā)布,版權(quán)歸原作者所有,本平臺(tái)僅提供信息存儲(chǔ)服務(wù)。文章僅代表作者個(gè)人觀點(diǎn),不代表本平臺(tái)立場(chǎng),如有問題,請(qǐng)聯(lián)系我們,謝謝!