珠海 网站 设计,老黄代码,logo生成器免费版,机械加工外协加工网目录
一、简单介绍
二、开发思路
三、实战开发
App
Boot 一、简单介绍
Bootloader 是嵌入式设备中负责引导和更新的关键程序。汽车的 ECU 在进行 OTA 升级时#xff0c;Bootloader 负责下载、校验和写入新固件#xff0c;确保出现异常时能回退到旧版本保障行车安全Bootloader 负责下载、校验和写入新固件确保出现异常时能回退到旧版本保障行车安全手机系统更新依赖 Bootloader来刷机、验证签名并启动系统内核电脑中的 BIOS 或 UEFI 同样是一类 Bootloader用于初始化硬件并加载操作系统。通过统一的下载接口和安全机制Bootloader 让设备可以远程维护、在线更新并防止恶意篡改从而在降低生产和维护成本的同时保证系统在整个生命周期中的稳定与安全。 归根结底bootloader也是一个工程和application性质相同
笔者实现的简易bootloader的作用主要有
擦除现存app接收新的app固件烧写到flash中跳转到新app
二、开发思路
笔者是基于STM32F103CBT6开发和常见的STM32F103C8T6相比flash变成128kb STM32单片机的flash启动模式下启动地址总是0x8000000这也是自己的bootloader的起始地址 bootloader大小为12616字节需要12个page多一点点0.32 page
因此把判断标志放在page 12的开头即地址0x08003400
App的起始地址为0x08003800page13的开头
内存分布为 boot检测flag信号为true表明需要升级擦除App并烧写新的固件否则就表明不需要升级直接跳转到App即可
三、实战开发
App
cubemx开启时钟UART1和UART2LED App工程需要设置起始地址使用keil的话如下设置 system_stm32f1xx.c文件中设置向量表偏移地址 App闪烁LED并在串口2发送App running作为演示 App的main函数代码如下
/* USER CODE END 0 */
/*** brief The application entry point.* retval int*/
int main(void)
{/* USER CODE BEGIN 1 *//* 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_USART1_UART_Init();MX_USART2_UART_Init();/* USER CODE BEGIN 2 */req.TypeErase FLASH_TYPEERASE_PAGES;req.Banks FLASH_BANK_1;req.PageAddress APP_REQUEST_ADDRESS;req.NbPages 1;/* go to wait state for getting next data from PC*/updateReq *(uint8_t*)(APP_REQUEST_ADDRESS);HAL_UART_Receive_IT(huart1, rx, 6);/* USER CODE END 2 *//* Infinite loop *//* USER CODE BEGIN WHILE */while (1){HAL_GPIO_TogglePin(LED_GPIO_Port, LED_Pin);HAL_UART_Transmit(huart2, tx, 14, 10);HAL_Delay(100);/* USER CODE END WHILE *//* USER CODE BEGIN 3 */}/* USER CODE END 3 */
}Boot
cubemx开启时钟串口 boot工程起始地址就是0x8000000就不需要偏移了
设置地址宏和升级请求标志
#define APP_START_ADDRESS 0x08003800
#define APP_REQUEST_ADDRESS 0x08003400updateReq *(uint8_t*)(APP_REQUEST_ADDRESS);
判断是否需要升级
if (updateReq 1 || updateReq 0xFF)
{state STATE_INIT;HAL_FLASH_Unlock();HAL_FLASHEx_Erase(req, error);
}
else
{state STATE_JUMP;
}
在主循环内进行状态的切换
笔者制作了一个配套的上位机 上位机按下boot则发送由帧头0xAA0x22222222CRC组成的升级请求App检测到后就设置标志位并复位 上位机按下flash则进行新固件的传输在boot中进行接收和flash的写入
case STATE_FLASH:
{/*** flash code*/HAL_FLASH_Program(FLASH_TYPEPROGRAM_WORD, APP_START_ADDRESS flashPackCnt * 4, *(uint32_t*) (prx 1));flashPackCnt;/* flash finish, notify PC by sending 0x77 */tx[0] 0x77;HAL_UART_Transmit_IT(huart1, tx, 1);/* go to wait state for getting next data from PC*/HAL_UART_Receive_IT(huart1, rx, 6);state STATE_WAIT;if ((flashPackCnt) * 4 dataSize){state STATE_END;HAL_FLASH_Program(FLASH_TYPEPROGRAM_WORD, APP_REQUEST_ADDRESS, 0);}break;
}
上位机发送0x20四字节的固件包CRC单片机接收后发送0x77作为应答信号这里笔者作为演示就直接明文传输了实际应用一般会有加密措施和握手等步骤 烧写完毕后跳转到新的App中
case STATE_JUMP:
{deinitEverything();uint32_t stacktop *((__IO uint32_t *)APP_START_ADDRESS);__set_MSP(stacktop);app_func_t app_func (app_func_t)(*((__IO uint32_t *)(APP_START_ADDRESS 4)));app_func();
}
boot的main函数代码如下
/* USER CODE END 0 */
/*** brief The application entry point.* retval int*/
int main(void)
{/* USER CODE BEGIN 1 *//* 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_USART1_UART_Init();/* USER CODE BEGIN 2 */updateReq *(uint8_t*)(APP_REQUEST_ADDRESS);erase.TypeErase FLASH_TYPEERASE_PAGES;erase.Banks FLASH_BANK_1;erase.PageAddress APP_START_ADDRESS;erase.NbPages 40;req.TypeErase FLASH_TYPEERASE_PAGES;req.Banks FLASH_BANK_1;req.PageAddress APP_REQUEST_ADDRESS;req.NbPages 1;uint32_t error;if (updateReq 1 || updateReq 0xFF){state STATE_INIT;HAL_FLASH_Unlock();HAL_FLASHEx_Erase(req, error);}else{state STATE_JUMP;}/* USER CODE END 2 *//* Infinite loop *//* USER CODE BEGIN WHILE */while (1){switch (state){case STATE_INIT:{/* prepare for size info by interrupt */HAL_UART_Receive_IT(huart1, rx, 6);/* tell host the chip is in boot now */tx[0] 0x77;HAL_UART_Transmit_IT(huart1, tx, 1);/* go to idle */state STATE_IDLE;break;}case STATE_IDLE:{break;}case STATE_SIZE:{break;}case STATE_ERASE_BEGIN:{break;}case STATE_WAIT:{break;}case STATE_FLASH:{/*** flash code*/HAL_FLASH_Program(FLASH_TYPEPROGRAM_WORD, APP_START_ADDRESS flashPackCnt * 4, *(uint32_t*) (prx 1));flashPackCnt;/* flash finish, notify PC by sending 0xAA */tx[0] 0x77;HAL_UART_Transmit_IT(huart1, tx, 1);/* go to wait state for getting next data from PC*/HAL_UART_Receive_IT(huart1, rx, 6);state STATE_WAIT;if ((flashPackCnt) * 4 dataSize){state STATE_END;HAL_FLASH_Program(FLASH_TYPEPROGRAM_WORD, APP_REQUEST_ADDRESS, 0);}break;}case STATE_END:{break;}case STATE_JUMP:{deinitEverything();uint32_t stacktop *((__IO uint32_t *)APP_START_ADDRESS);__set_MSP(stacktop);app_func_t app_func (app_func_t)(*((__IO uint32_t *)(APP_START_ADDRESS 4)));app_func();}default:break;}/* USER CODE END WHILE *//* USER CODE BEGIN 3 */}/* USER CODE END 3 */
}
调试读取内存0x8003800处的内容和编译的App的hex内容一致
由于升级请求被擦除下一次复位就直接跳转至App运行了