文章目錄
- 概述
- 什么是bootloader?為什么用?
- bootloader啟動流程圖
- 步驟
- 下載過程
- 代碼
- 獲取本地配置信息
- 獲取主機傳過來的配置信息
- bootloader發送2給上位機,上位機發送文件給bootloader
- 根據網站復制CRC
- 燒寫flash
- erase
- 啟動
- 編譯問題
概述
用keil編寫app,來用bootloader更新app,完成燈閃爍指令,升級app的新版本就是燈閃爍的更快了;
通過usb直接傳輸更新的配置給開發板,然他自己啟動bootloder,擦除原來的app和配置信息,燒錄新的app和配置信息;
什么是bootloader?為什么用?
運行時,bootloader會先啟動,如果有程序升級,bootloader從RAM里面下數據燒錄到flash的一塊空間上面,就是app;
其中突然斷電沒燒錄完也沒事,bootloader等下次啟動又會重新燒錄,如果沒有bootloader,app不是完整的也不能直接啟動,就不能用了;
原本沒有bootloader怎么運行的:
RAM如果很小,里面不會去在復制一個app進去,問題就是此時上點時是flash的app程序在從RAM下載燒錄數據到flash的app,這直接把自己覆蓋了呀,那會出錯的;
RAM如果很大,那RAM存一個APP,運行是就用RAM的APP,然后把升級的app數據下載燒錄到flash上把他覆蓋,這樣就不會出現上面的問題了,但是斷電就會導致flash上的APP沒燒錄完整,下次通電先打開flash就打不開了;
通電先執行flash;
bootloader啟動流程圖
STM32H563RIV
cpu是ARM公司生產的,他們設定只能從0開始讀,因此要設置映射關系,讓cpu一開始在flash的地址0x0800 0000;
1、cpu從0開始讀4個字節存入sp寄存器;
2、cpu從4這個地址讀到4個字節存入pc寄存器
發生中斷時:
硬件會做的事:在異常向量表里面找到函數,執行
我們需要操作的是:告訴cpu異常向量表的基地址
下圖為有無bootloader的異常向量表的首地址
要把這個//,因為這個是異常向量表,是flash開頭的異常向量表
把flash開始燒錄的地址移到0804000,前面的給bootloader;
步驟
傳進來的參數是存在寄存器R0里面的;
把R0的數據寫入寄存器VTOR
把R0的數值存入R1所指向的位置;
VTOR保存的是異常向量表的基地址;
從R0這里讀取R1;
把R1的數值寫給棧SP寄存器;
跳轉到R0+4的位置
下載過程
1、bootloader發送“1”字符給上位機,請求固件信息;
2、上位機給bootloader發送固件信息:先發同步字符;
上位機發送的固件信息
a、長度信息
b、加載地址
c、校驗碼
d、文件名
e、版本號
3、bootloader決定升級
發送0x02給上位機;
4、上位機發送bin文件給bootloader
收到后bootloader會(1)計算CRC校驗碼,(2)與之前收到的固件信息比較;
5、bootloader開始燒寫,同時可以發送燒寫進度給上位機
生成設備信息
用這個create_firemware_info來生成腳本
代碼
獲取本地配置信息
#define CFG_OFFSET 0x081FE000//設備地址
file_len是判斷配置信息有沒有寫
獲取主機傳過來的配置信息
先是usb傳輸發送1;
接收上機回應信號5個0x5a;
接收上機設備配置信息32個寄存器;
bootloader發送2給上位機,上位機發送文件給bootloader
上位機發送文件時就會發送一系列的數據,超時時間*10,給上位機充分時間發送
根據網站復制CRC
bootloaderTask
這里任務要等待一會
因為usb插到電腦上,要等電腦識別出來usb,才能進行傳輸,
獲取固件失敗的原因?
數據丟失,上位機發送數據給bootloader,是不是數據沒有接收全,數據覆蓋了,數據丟失了;
先看是不是數據接收不全,怎么看呢,我們是創建隊列來接收的,隊列初始化時的大小看一下,能不能裝得下;
找到usb傳輸的隊列只有200bit,但是傳輸的數據有5kb ,5x1024bit,把隊列改成10kb=10240bit
BootTask整體流程:
#include "FreeRTOS.h"
#include "task.h"
#include "main.h"
#include "cmsis_os2.h"
/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include "stdio.h"
#include "draw.h"
#include "stdio.h"
#include "draw.h"
#include "ux_api.h"
#include "modbus.h"
#include "errno.h"
#include "uart_device.h"
#include "semphr.h"#include "bootloader.h"#define UPDATE_TIMEOUT 1000//超時時間#define CFG_OFFSET 0x081FE000static struct UART_Device *g_pUpdateUART;static uint32_t BE32toLE32(uint8_t *buf)
{return ((uint32_t)buf[0] << 24) | ((uint32_t)buf[1] << 16) | ((uint32_t)buf[2] << 8) | ((uint32_t)buf[3] << 0);
}static int GetLocalFirmwareInfo(PFirmwareInfo ptFirmwareInfo)
{PFirmwareInfo ptFlashInfo = (PFirmwareInfo)CFG_OFFSET;if (ptFlashInfo->file_len == 0xFFFFFFFF)return -1;*ptFirmwareInfo = *ptFlashInfo;return 0;
}static int GetServerFirmwareInfo(PFirmwareInfo ptFirmwareInfo)
{uint8_t data = '1';uint8_t buf[sizeof(PFirmwareInfo)];/*send 0x01 cmd to pc*/if(0 != g_pUpdateUART->Send(g_pUpdateUART, &data, 1, UPDATE_TIMEOUT))return -1;/*wait for response*/while(1){if(0 != g_pUpdateUART->RecvByte(g_pUpdateUART, &data, UPDATE_TIMEOUT*10))return -1;if(data != 0x5a){buf[0] = data;break;}}for(int i = 1; i < sizeof(PFirmwareInfo); i++){if(0 != g_pUpdateUART->RecvByte(g_pUpdateUART, &buf[i], UPDATE_TIMEOUT))return -1;}ptFirmwareInfo->version = BE32toLE32(&buf[0]);ptFirmwareInfo->file_len = BE32toLE32(&buf[4]);ptFirmwareInfo->load_addr = BE32toLE32(&buf[8]);ptFirmwareInfo->crc32 = BE32toLE32(&buf[12]);strncpy((char *)ptFirmwareInfo->file_name, (char *)&buf[16], 16);return 0;}static int GetServerFirmware(uint8_t *buf, uint32_t len)
{uint8_t data = '2';/*send 0x01 cmd to pc*/if(0 != g_pUpdateUART->Send(g_pUpdateUART, &data, 1, UPDATE_TIMEOUT))return -1;/*上位機發送文件給bootloader*/for(int i = 0; i < len; i++){if(0 != g_pUpdateUART->RecvByte(g_pUpdateUART, &buf[i], UPDATE_TIMEOUT*10))return -1;}return 0;
}/* https://lxp32.github.io/docs/a-simple-example-crc32-calculation/ */
static int GetCRC32(const char *s,size_t n)
{uint32_t crc=0xFFFFFFFF;for(size_t i=0;i<n;i++) {char ch=s[i];for(size_t j=0;j<8;j++) {uint32_t b=(ch^crc)&1;crc>>=1;if(b) crc=crc^0xEDB88320;ch>>=1;}}return ~crc;
}void BootLoaderTask( void *pvParameters )
{struct UART_Device *pUSBUART = GetUARTDevice("usb");FirmwareInfo tLocalInfo;FirmwareInfo tServerInfo;int err;int need_update = 0;uint8_t *firmware_buf;vTaskDelay(10000); /* wait for pc ready */pUSBUART->Init(pUSBUART, 115200, 'N', 8, 1);g_pUpdateUART = pUSBUART;while (1){/* read cfg info, to detect app's version */err = GetLocalFirmwareInfo(&tLocalInfo);if (err){/* update */need_update = 1;}else{pUSBUART->Send(pUSBUART, (uint8_t *)"GetLocalFirmwareInfo Failed\r\n", strlen("GetLocalFirmwareInfo Failed\r\n"), UPDATE_TIMEOUT);}err = GetServerFirmwareInfo(&tServerInfo);if (!err){/* compate version */if (tServerInfo.version > tLocalInfo.version){/* update */need_update = 1;}}else{need_update = 0;pUSBUART->Send(pUSBUART, (uint8_t *)"GetServerFirmwareInfo Failed\r\n", strlen("GetServerFirmwareInfo Failed\r\n"), UPDATE_TIMEOUT);}if (need_update){firmware_buf = pvPortMalloc(tServerInfo.file_len);if (!firmware_buf){/* error */pUSBUART->Send(pUSBUART, (uint8_t *)"Malloc Failed\r\n", strlen("Malloc Failed\r\n"), UPDATE_TIMEOUT);}err = GetServerFirmware(firmware_buf, tServerInfo.file_len);if (!err){/* calc CRC */ uint32_t crc = GetCRC32((const char *)firmware_buf, tServerInfo.file_len);if (crc == tServerInfo.crc32){/* OK *//* burn */pUSBUART->Send(pUSBUART, (uint8_t *)"Download OK\r\n", 13, UPDATE_TIMEOUT);}else{pUSBUART->Send(pUSBUART, (uint8_t *)"GetCRC32 Failed\r\n", strlen("GetCRC32 Failed\r\n"), UPDATE_TIMEOUT);}}else{pUSBUART->Send(pUSBUART, (uint8_t *)"GetServerFirmware Failed\r\n", strlen("GetServerFirmware Failed\r\n"), UPDATE_TIMEOUT);}}else{/* start app */}}
}
燒寫flash
對比flash上面的版本比主機發過來的版本小,或者flash上面的配置信息壞了,就啟動升級:
1、erase 擦除flash
2、燒寫
flash有兩個棧區,bank1和bank2 128個棧區x 每個棧區8k
把中間藍色圈APP擦除再燒錄,只是更新APP,以及把配置文件的信息改成對應的最新的;
erase
‘
1、擦除app并燒錄
擦除bank1和bank2的APP的,APP的長度可能延伸到了bank2有if進行判斷,傳入的地址是bootloader后的,也就是APP開頭地址
2、擦除配置文件并燒錄
3、bootloadertask
下載完成主機發送過來的配置文件就用1、2這兩個函數進行擦除燒錄;
啟動
觸發軟件復位
復位最終會調用main
如果是復位的話就會得到flash上面這個應用程序的基地址,然后調用這個匯編函數start_app來啟動程序
注意:
編譯問題
non-ASCLL表示引入了中文字符