1.Flash分区
2.实现逻辑
3.固件传输协议
固件由主板通过串口发送给MCU,通讯协议如下:
共103字节
4. Bootloader
main.c
#include "gd32f3x0.h"
#include "systick.h"
#define OTA_FLAG_ADDR 0x0800FFF8
#define OTA_FLAG_VALUE 0xA5A5A5A5
#define OTA_CACHE_ADDR 0x08009000
#define APP_ADDR 0x08002000
#define APP_MAX_SIZE (27 * 1024) // 不为28K,避免flash末尾的ota_flag也被写入,影响app
#define APP_SIZE (27 * 1024)
typedef void (*app_func)(void);
void jump_to_app(void) {
uint32_t sp = *(uint32_t *)APP_ADDR;
__set_MSP(sp);
uint32_t pc = *(uint32_t *)(APP_ADDR + 4);
((app_func)pc)();
}
//void jump_to_app(void) {
//
// uint32_t sp = *(uint32_t *)OTA_CACHE_ADDR;
// __set_MSP(sp);
// uint32_t pc = *(uint32_t *)(OTA_CACHE_ADDR + 4);
// ((app_func)pc)();
//}
//void copy_ota_to_app(void) {
// fmc_unlock();
// for (uint32_t i = 0; i < APP_MAX_SIZE; i += 4) {
// uint32_t data = *(uint32_t *)(OTA_CACHE_ADDR + i);
// fmc_word_program(APP_ADDR + i, data);
// }
// fmc_page_erase(OTA_FLAG_ADDR); // 清除升级标志
// fmc_word_program(OTA_FLAG_ADDR, 0xFFFFFFFF);
// fmc_lock();
//}
void copy_ota_to_app(void) {
fmc_unlock();
// 擦除 APP 区域 每页1KB
for (uint32_t addr = APP_ADDR; addr < (APP_ADDR + APP_SIZE); addr += 1024) {
fmc_page_erase(addr);
while (fmc_flag_get(FMC_FLAG_END) == RESET);
}
// 写入新数据
for (uint32_t i = 0; i <= APP_MAX_SIZE; i += 4) {
uint32_t data = *(uint32_t *)(OTA_CACHE_ADDR + i);
fmc_word_program(APP_ADDR + i, data);
while (fmc_flag_get(FMC_FLAG_END) == RESET);
}
// 清除 OTA_FLAG
// fmc_page_erase(OTA_FLAG_ADDR);
fmc_page_erase(OTA_FLAG_ADDR & 0xFFFFFC00); // 页对齐(0x400)
while (fmc_flag_get(FMC_FLAG_END) == RESET);
fmc_word_program(OTA_FLAG_ADDR, 0xFFFFFFFF);
while (fmc_flag_get(FMC_FLAG_END) == RESET);
fmc_lock();
}
int main(void) {
rcu_periph_clock_enable(RCU_GPIOA);
gpio_mode_set(GPIOA, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, GPIO_PIN_0);
gpio_output_options_set(GPIOA, GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_0);
gpio_bit_set(GPIOA, GPIO_PIN_0);
if (*(uint32_t *)OTA_FLAG_ADDR == OTA_FLAG_VALUE) {
copy_ota_to_app();
}
SCB->VTOR = APP_ADDR;
jump_to_app(); // 跳转APP
while (1); // 不应运行到此
}
烧录位置配置:
5.APP
main.c
#include "gd32f3x0.h"
#include "systick.h"
#include <stdio.h>
#include "main.h"
#include "gd32f350r_eval.h"
uint8_t uart_rx_buf[PACKET_TOTAL_LEN];
uint8_t uart_rx_index = 0;
uint8_t in_packet = 0;
volatile uint8_t packet_ready = 0;
uint8_t packet_buf[PACKET_TOTAL_LEN];
//bootloader 8k app 28k ota_cache 28k-8bit ota_flag 8bit
#define APP_ADDR 0x08002000
#define OTA_CACHE_ADDR 0x08009000
#define OTA_FLAG_ADDR 0x0800FFF8
#define OTA_FLAG_VALUE 0xA5A5A5A5 //有固件写入
uint32_t ota_offset = 0;
void flash_write_word(uint32_t address, uint32_t data) {
fmc_word_program(address, data);
}
void flash_erase_pages(uint32_t start_addr, uint32_t length) {
for (uint32_t addr = start_addr; addr < start_addr + length; addr += 1024) {
fmc_page_erase(addr);
}
}
void ota_start(void) {
ota_offset = 0;
usart_interrupt_disable(USART0, USART_INT_RBNE); // 关闭串口中断 中断干扰flash擦写
fmc_unlock();
flash_erase_pages(OTA_CACHE_ADDR, 27 * 1024); //ota_cache 27k 避免擦除ota_flag
fmc_lock();
usart_interrupt_enable(USART0, USART_INT_RBNE); // 重新开启串口中断
}
void ota_write_bin_chunk(uint8_t *data, uint32_t len) {
fmc_unlock();
//4字节写入
for (uint32_t i = 0; i < len; i += 4) {
uint32_t word = *(uint32_t *)(data + i);
flash_write_word(OTA_CACHE_ADDR + ota_offset + i, word);
while (fmc_flag_get(FMC_FLAG_END) == RESET);
}
fmc_lock();
ota_offset += len;
}
void ota_finish(void) {
fmc_unlock();
flash_erase_pages(OTA_FLAG_ADDR, 1024);
flash_write_word(OTA_FLAG_ADDR, OTA_FLAG_VALUE);
fmc_lock();
delay_1ms(1000);
NVIC_SystemReset(); //软复位MCU reset后bootloader检查ota_flag
}
void handle_uart_ota_packet(uint8_t *buf, uint32_t len) {
uint8_t cmd = buf[0];
switch(cmd) {
case 0x01: ota_start(); break; //擦除ota_cache
case 0x02: ota_write_bin_chunk(&buf[1], len - 1); break; //写入ota_cache
case 0x03: ota_finish(); break; //ota_flag赋值,reset MCU
}
}
static void _soft_delay_(uint32_t time)
{
__IO uint32_t i;
for(i=0; i<time*10; i++){
}
}
void USART0_IRQHandler(void)
{
if (usart_interrupt_flag_get(USART0, USART_INT_FLAG_RBNE)) {
uint8_t ch = usart_data_receive(USART0);
// gpio_bit_reset(GPIOA, GPIO_PIN_0);
if (!in_packet) {
if (uart_rx_index == 0 && ch == 0x4D) {
uart_rx_buf[uart_rx_index++] = ch;
} else if (uart_rx_index == 1 && ch == 0x4D) {
uart_rx_buf[uart_rx_index++] = ch;
in_packet = 1;
} else {
uart_rx_index = 0;
}
} else {
uart_rx_buf[uart_rx_index++] = ch;
if (uart_rx_index == 4) {
uint16_t length = uart_rx_buf[2] | (uart_rx_buf[3] << 8);
if (length != 0x62) {
uart_rx_index = 0;
in_packet = 0;
}
}
if (uart_rx_index == PACKET_TOTAL_LEN) {
for (int i = 0; i < PACKET_TOTAL_LEN; ++i){
packet_buf = uart_rx_buf;
}
packet_ready = 1;
uart_rx_index = 0;
in_packet = 0;
}
}
}
}
int main(void)
{
SCB->VTOR = APP_ADDR; //中断函数向量表重定向
systick_config();
gd_eval_com_init(EVAL_COM);
Fixed_Color_Display(0x000000);
usart_interrupt_enable(USART0, USART_INT_RBNE);
nvic_irq_enable(USART0_IRQn, 2, 0);
while (1) {
//串口接收数据后处理
if (packet_ready) {
packet_ready = 0;
uint8_t cmd = packet_buf[4];
uint8_t checksum = packet_buf[PACKET_TOTAL_LEN - 1];
uint8_t sum = cmd;
for (int i = 0; i < 97; ++i) sum += packet_buf[5 + i];
sum &= 0xFF; //checknum对比
if (sum == checksum && cmd == 0x30) {
uint8_t row = packet_buf[5];
const uint8_t* grb_data = &packet_buf[6];
Set_Row_Color_From_Payload(row, grb_data);
} else if(cmd == 0x40){
//OTA
handle_uart_ota_packet(&packet_buf[5], 97);
}
}
}
}
烧录位置及擦除配置:
选择Erase Sectors,避免第一次烧录APP时将Bootloader擦除。
由于新固件以.bin格式传输与接收,默认情况下,编译生成的项目文件为.hex格式,需要添加用户指令:
fromelf.exe --bin --output .\output\Project.bin .\output\Project.axf
如此编译后,output文件夹下会生成bin文件,用于后续的固件发送与更新:
6.主板发送新固件
#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include <fcntl.h>
#include <termios.h>
#include <unistd.h>
#include <stdlib.h>
#include <errno.h>
#define PACKET_SIZE 103
#define HEADER_BYTE 0x4D
#define CMD_OTA 0x40
int open_serial(const char *device) {
int fd = open(device, O_RDWR | O_NOCTTY | O_SYNC);
if (fd < 0) {
perror("open serial");
return -1;
}
struct termios tty;
if (tcgetattr(fd, &tty) != 0) {
perror("tcgetattr");
close(fd);
return -1;
}
cfsetospeed(&tty, B115200);
cfsetispeed(&tty, B115200);
tty.c_cflag = (tty.c_cflag & ~CSIZE) | CS8;
tty.c_iflag = IGNBRK;
tty.c_lflag = 0; // no signaling chars, no echo
tty.c_oflag = 0; // no remapping, no delays
tty.c_cc[VMIN] = 1; // read doesn't block
tty.c_cc[VTIME] = 1; // 0.1 seconds read timeout
tty.c_iflag &= ~(IXON | IXOFF | IXANY); // shut off xon/xoff ctrl
tty.c_cflag |= (CLOCAL | CREAD); // ignore modem controls
tty.c_cflag &= ~(PARENB | PARODD); // no parity
tty.c_cflag &= ~CSTOPB;
tty.c_cflag &= ~CRTSCTS;
if (tcsetattr(fd, TCSANOW, &tty) != 0) {
perror("tcsetattr");
close(fd);
return -1;
}
return fd;
}
void build_packet(uint8_t *packet, uint8_t ota_cmd, const uint8_t *payload, size_t payload_len) {
memset(packet, 0, PACKET_SIZE);
packet[0] = HEADER_BYTE;
packet[1] = HEADER_BYTE;
packet[2] = 0x62; // Length = 0x62 (98 bytes: Command + Payload[96] + Checksum)
packet[3] = 0x00;
packet[4] = CMD_OTA;
packet[5] = ota_cmd;
if (payload && payload_len > 0) {
memcpy(&packet[6], payload, payload_len);
}
uint8_t sum = CMD_OTA + ota_cmd;
for (int i = 0; i < 96; ++i)
sum += packet[6 + i];
packet[102] = sum & 0xFF;
}
int send_packet(int fd, uint8_t ota_cmd, const uint8_t *payload, size_t payload_len) {
uint8_t packet[PACKET_SIZE];
build_packet(packet, ota_cmd, payload, payload_len);
return write(fd, packet, PACKET_SIZE);
}
int main(int argc, char *argv[]) {
if (argc != 2) {
printf("Usage: %s firmware.bin\n", argv[0]);
return 1;
}
const char *bin_path = argv[1];
FILE *fp = fopen(bin_path, "rb");
if (!fp) {
perror("fopen bin");
return 1;
}
int fd = open_serial("/dev/ttyS4");
if (fd < 0) {
fclose(fp);
return 1;
}
// Step 1: Send ota_start (0x01)
printf("Sending OTA_START...\n");
send_packet(fd, 0x01, NULL, 0);
usleep(1000 * 1000);
// Step 2: Send ota_write_bin_chunk (0x02)
uint8_t buf[96];
size_t chunk = 0;
while (!feof(fp)) {
size_t read_bytes = fread(buf, 1, 96, fp);
if (read_bytes < 96) {
memset(buf + read_bytes, 0xFF, 96 - read_bytes); // pad with 0xFF
}
printf("Sending chunk %zu...\n", chunk++);
send_packet(fd, 0x02, buf, 96);
usleep(100 * 1000);
}
// Step 3: Send ota_finish (0x03)
usleep(1000*1000);
printf("Sending OTA_FINISH...\n");
send_packet(fd, 0x03, NULL, 0);
// send_packet(fd, 0x03, NULL, 0);
// send_packet(fd, 0x03, NULL, 0);
usleep(1000 * 1000);
fclose(fp);
close(fd);
printf("OTA completed.\n");
return 0;
}
————————————————
版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
原文链接:https://blog.csdn.net/m0_60013390/article/details/148873319
|
|