STM32 Bootloader开发记录 2
在《stm32 bootloader开发记录.md》文档中,已经实现了Bootloader下的升级功能。可以在Bootloader启动时,进入升级模式,使用串口传输数据,来下载固件到flash中。
但是,在实际应用中,一般是在应用运行过程中进行升级,而不是在Bootloader中进行升级。一般只有在开发阶段才需要在Bootloader中进行升级。所以,接下来,我将实现在app中进行升级操作,并且添加签名验签功能,保证升级过程中固件的安全性和完整性。
1. 分区划分
分区划分使用前面文档所说的flash map。应用分区有App1和App2,这里使用App1作为我们的应用分区,应用只会在App1中启动。而App2是固件存储分区,用来在App1运行过程中存储升级固件,固件下载到App2完成后,重启到Bootloader,然后让Bootloader把固件复制到App1中运行。
(当然,也可以不拷贝App2到App1,可以直接从Bootloader启动App2,这种方式可以增加flash的使用寿命,并且升级过程较快。这里不使用这种方式的原因是,我们的固件有修改过中断向量表,如果可以在App2中启动,那么我们需要对每个固件是在哪个分区启动要提前知道,并设置正确的终端向量表地址。为了不这么麻烦,这里使用前面所述的复制固件到固定地址运行的方式。)
2. 升级原理
在App1中,我们依然使用串口接收数据,数据通信格式和Bootloader保持一致,这样,基本上可以不用修改dfu server程序的功能,就能完成升级了。在App1中接收固件,并将固件存储到App2分区,接收完成后,写入升级标致,然后重启。Bootloader在启动时,读取升级表示,识别到是App需要升级时,将App2分区的固件拷贝到App1分区中,如果复制过程没有问题了,清除写入标识,然后跳转到App1中运行新固件。
针对Bootloader,基本上也不需要修改什么,加上一个识别升级标识的代码,然后增加一个拷贝固件的功能就够了。
(签名验签功能, 需要使用到加密校验库,后面移植了mbedtls库再在拷贝固件的过程中加入验签即可。)
应用程序,在串口接收到"ota_start"
字符串后,进入升级模式,开始使用之前定义的通信格式进行通信。dfu server加入一个发送"ota_start"
的App 升级模式即可。
3. 代码
3.1 bootloader
拷贝固件
void ota_copy_app()
{
uint32_t page_num = ota_info.header.firmware_size / 2048 + 1;
uint8_t buffer_2k[2048] = {0};
int i = 0, j = 0;
uint32_t app2_addr = 0x8042000;
uint8_t is_first = 1;
flash_erase(FLASH_BANK_1, 32, 96);
flash_erase(FLASH_BANK_2, 256, 4);
for (; i < page_num; i++)
{
memcpy(buffer_2k, (uint32_t *)(app2_addr + i * 2048), 2048);
flash_write_fw(buffer_2k, 2048, is_first);
is_first = 0;
}
ota_info.status = OTA_NONE;
ota_info_write(&ota_info);
}
App升级模式
int main(void)
{
HAL_Init();
......
if (HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_12) == GPIO_PIN_SET) {
boot_to_ota = 1;
printf("start ota\r\n");
}
ota_info_read(&ota_info);
ota_info_print(&ota_info);
HAL_UART_Receive_IT(&huart1, uart_rx.data, 1);
printf("start uart IT receive\r\n");
if (boot_to_ota == 0 && ota_info.status == OTA_READY)
{
ota_copy_app();
goto_application();
}
......
}
3.2 Application
串口接收
void ota_uart_receive_it(UART_HandleTypeDef *huart)
{
HAL_UART_Receive_IT(&huart1, uart_rx.data + (++uart_rx.pos), 1);
if (!ota_start_flag)
{
if (uart_rx.pos == 8) {
uart_rx.pos = -1;
}
else if (uart_rx.pos == 0)
{
char ota_start[] = "ota_start";
if (strncmp((char *)uart_rx.data, ota_start, strlen(ota_start)) == 0)
{
ota_start_flag = 1;
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_12, GPIO_PIN_RESET);
}
}
return;
}
if (uart_rx.pos > 256) {
uart_rx.pos = -1;
}
else if (uart_rx.pos == 4) {
uart_rx.data_size = uart_rx.data[2] | uart_rx.data[3] << 8;
}
else if (uart_rx.data_size + 8 == uart_rx.pos) {
uart_rx_size = uart_rx.pos + 1;
is_recv_frame_ok = 1;
uart_rx.pos = -1;
}
}
uint8_t ota_start_flag_get(void)
{
return ota_start_flag;
}
主业务逻辑
int main()
{
......
while (1)
{
if (ota_start_flag_get() && start_ota() == 0) {
HAL_Delay(100);
HAL_NVIC_SystemReset();
}
}
}
3.3 dfu server
增加一个发送"ota_start"
字符串的步骤。
while (1)
{
if (app_ota) {
std::cout << "start app ota mode" <<std::endl;
boost::system::error_code ec;
char ota_start[] = "ota_start";
port.write_some(boost::asio::buffer(ota_start, strlen(ota_start)), ec);
}
size_t n = port.read_some(boost::asio::buffer(data, sizeof(data)));
if (n > 0 && !dfu_start)
{
std::cout << "recv: " << std::string(data) << std::endl;
dfu_start = true;
sleep(2); //等待MCU启动
4. 测试
下载Bootloader、Application固件到开发板中。下载前,对整个flash进行擦除,防止升级标识错误导致测试失败。将PB2引脚拉低,防止Bootloader进入到下载模式。下载完成后,应用正常启动,应用版本号是5
。
----------------------------------------
stm32l475 bootloader start
----------------------------------------
firmware size: -1
major: 0xff
minor: 0xff
status: OTA_RUNNING
start uart IT receive
haven't ota event
starting application
jump to application
----------------------------------------
stm32l475 application start
----------------------------------------
firmware version: 0x05
hardware version: 0x01
firmware size: -1
major: 0xff
minor: 0xff
status: OTA_RUNNING
start uart IT receive
这个开启dfu server,进行应用升级。开启前,先关闭串口助手的串口,防止串口占用导致dfu server打开串口失败。
.\dfu_server.exe G:\stm32\alentek_stm32l475_application\MDK-ARM\alentek_stm32l475_application\firmware6.bin COM4 app_ota
升级成功。
ff ff ff ff jump to application
----------------------------------------
stm32l475 application start
----------------------------------------
firmware version: 0x06
hardware version: 0x01