要更行数据我们首先得知道:
1.谁让OTA_FLAG标志位置位允许OTA升级?什么时候置位?怎么复位?
flash中A区OTA接收数据完成后置位OTA_FLAG;接收完成后复位不仅仅是在RAM中OTA_FLAG=0;还要将OTA_FLAG写入24C02中保存状态
2.最新版的程序下载到哪?
通过分片下载的方式下载到W25Q64中;什么叫分片下载?就是比如一个64K的文件,每次下载256字节,分数次下载
3.OTA时,最新版本的程序文件如何下载?下载多少?
服务器告知下载并将OTA_FLAG置位;下载的大小也是通过服务器告知,下载的长度这个变量也需要保存到24C02中
4.发生OTA事件时,B区如何更新A区?
根据保存在24C02中的下载长度,拿数据(一次拿1024字节),写到A区
main.h
#ifndef MAIN_H
#define MAIN_H
#include "stdint.h"
#define GD32_FLASH_SADDR 0x08000000 //FLASH起始地址
#define GD32_PAGE_SIZE 1024 //FLASH扇区大小
#define GD32_PAGE_NUM 64 //FLASH总扇区个数
#define GD32_B_PAGE_NUM 20 //B区扇区个数
#define GD32_A_PAGE_NUM GD32_PAGE_NUM - GD32_B_PAGE_NUM //A区扇区个数
#define GD32_A_START_PAGE GD32_B_PAGE_NUM //A区起始扇区编号
#define GD32_A_SADDR GD32_FLASH_SADDR + GD32_A_START_PAGE * GD32_PAGE_SIZE //A区起始地址
#define UPDATA_A_FLAG 0x00000001 //状态标志位,置位表明需要更新A了
#define OTA_SET_FLAG 0xAABB1122 //OTA_flag对勾状态对应的数值,如果OTA_flag等于该值,说明需要OTA更新A区
typedef struct{
uint32_t OTA_flag; //标志性的变量,等于OTA_SET_FLAG定义的值时,表明需要OTA更新A区
uint32_t Firelen[11]; //W25Q64中不同块中程序固件的长度,0号成员固定对应W25Q64中编码0的块,用于OTA
}OTA_InfoCB; //OTA相关的信息结构体,需要保存到24C02
#define OTA_INFOCB_SIZE sizeof(OTA_InfoCB) //OTA相关的信息结构体占用的字节长度
typedef struct{
uint8_t Updatabuff[GD32_PAGE_SIZE]; //更新A区时,用于保存从W25Q64中读取的数据
uint32_t W25Q64_BlockNB; //用于记录从哪个W25Q64的块中读取数据
}UpDataA_CB; //更新A区用的结构体
extern OTA_InfoCB OTA_Info; //外部变量声明
extern UpDataA_CB UpDataA; //外部变量声明
extern uint32_t BootStaFlag; //外部变量声明
#endif
Boot.c
#include "gd32f10x.h"
#include "boot.h"
#include "main.h"
#include "usart.h"
#include "delay.h"
#include "fmc.h"
#include "iic.h"
#include "m24c02.h"
load_a load_A; //函数指针load_A
/*-------------------------------------------------*/
/*函数名:BootLoader分支判断 */
/*参 数:无 */
/*返回值:无 */
/*-------------------------------------------------*/
void BootLoader_Brance(void)
{
if(OTA_Info.OTA_flag == OTA_SET_FLAG){ //判断OTA_flag是不是OTA_SET_FLAG定义的值,是的话进入if
u0_printf("OTA更新\r\n"); //串口0输出信息
BootStaFlag |= UPDATA_A_FLAG; //置位标志位,表明需要更新A区
UpDataA.W25Q64_BlockNB = 0; //W25Q64_BlockNB等于0,表明是OTA要更新A区
}else{ //判断OTA_flag是不是OTA_SET_FLAG定义的值,不是的话进入else
u0_printf("跳转A分区\r\n"); //串口0输出信息
LOAD_A(GD32_A_SADDR); //跳转到A区
}
}
/*-------------------------------------------------*/
/*函数名:设置SP */
/*参 数:addr:栈顶指针初始值 */
/*返回值:无 */
/*-------------------------------------------------*/
__asm void MSR_SP(uint32_t addr)
{
MSR MSP, r0 //addr的值加载到了r0通用寄存器,然后通过MSR指令,将通用寄存器r0的值写入到MSP主堆栈指针
BX r14 //返回调用MSR_SP函数的主函数
}
/*-------------------------------------------------*/
/*函数名:跳转到A区 */
/*参 数:addr:A区的起始地址 */
/*返回值:无 */
/*-------------------------------------------------*/
void LOAD_A(uint32_t addr)
{
if((*(uint32_t *)addr>=0x20000000)&&(*(uint32_t *)addr<=0x20004FFF)){ //判断sp栈顶指针的范围是否合法,在对应型号的RAM控件范围内
MSR_SP(*(uint32_t *)addr); //设置SP
load_A = (load_a)*(uint32_t *)(addr+4); //将函数指针load_A指向A区的复位向量
BootLoader_Clear(); //清除B区使用的外设
load_A(); //调用函数指针load_A,改变PC指针,从而转向A区的复位向量位置,完成跳转
}
}
/*-------------------------------------------------*/
/*函数名:清除B区使用的外设 */
/*参 数:无 */
/*返回值:无 */
/*-------------------------------------------------*/
void BootLoader_Clear(void)
{
usart_deinit(USART0); //复位串口0
gpio_deinit(GPIOA); //复位GPIOA
gpio_deinit(GPIOB); //复位GPIOB
}
main.c
#include "gd32f10x.h"
#include "main.h"
#include "usart.h"
#include "delay.h"
#include "fmc.h"
#include "iic.h"
#include "m24c02.h"
#include "boot.h"
#include "w25q64.h"
OTA_InfoCB OTA_Info; //保存在24C02内的OTA信息相关的结构体
UpDataA_CB UpDataA; //A区更新用到的结构体
uint32_t BootStaFlag; //记录全局状态标志位
OTA_Info.OTA_flag = 0xAABB1122;//允许OTA升级
int main(void)
{
uint8_t i; //用于for循环
Delay_Init(); //延时初始化
Usart0_Init(921600); //串口0初始化
IIC_Init(); //IIC初始化
M24C02_ReadOTAInfo(); //从24C02读取数据到OTA_Info结构体
BootLoader_Brance(); //分支判断
/*--------------------------------------------------*/
/*--------------------主循环------------------------*/
/*--------------------------------------------------*/
while(1){
/*--------------------------------------------------*/
/* UPDATA_A_FLAG置位,表明需要更新A区 */
/*--------------------------------------------------*/
if(BootStaFlag&UPDATA_A_FLAG){
u0_printf("长度%d字节\r\n",OTA_Info.Firelen[UpDataA.W25Q64_BlockNB]); //串口0输出信息
if(OTA_Info.Firelen[UpDataA.W25Q64_BlockNB] % 4 == 0){ //判断长度是否是4的整数倍,是的话进入if
GD32_EraseFlash(GD32_A_START_PAGE,GD32_A_PAGE_NUM); //擦除A区FLASH
for(i=0;i<OTA_Info.Firelen[UpDataA.W25Q64_BlockNB]/GD32_PAGE_SIZE;i++){ //每次读写一个扇区数据,使用for循环,写入整数个扇区
W25Q64_Read(UpDataA.Updatabuff,i*1024 + UpDataA.W25Q64_BlockNB*64*1024 ,GD32_PAGE_SIZE); //先从W25Q64读取一个单片机扇区的数据
GD32_WriteFlash(GD32_FLASH_SADDR + i*GD32_PAGE_SIZE,(uint32_t *)UpDataA.Updatabuff,GD32_PAGE_SIZE); //写入到单片机A区相应的扇区
}
if(OTA_Info.Firelen[UpDataA.W25Q64_BlockNB] % 1024 != 0){ //判断是否还有不足一个完整扇区的数据,有的话进入if
W25Q64_Read(UpDataA.Updatabuff,i*1024 + UpDataA.W25Q64_BlockNB*64*1024 ,OTA_Info.Firelen[UpDataA.W25Q64_BlockNB] % 1024); //从W25Q64读取不足一个完整扇区的数据
GD32_WriteFlash(GD32_FLASH_SADDR + i*GD32_PAGE_SIZE,(uint32_t *)UpDataA.Updatabuff,OTA_Info.Firelen[UpDataA.W25Q64_BlockNB] % 1024); //然后写入单片机A区相应的扇区
}
if(UpDataA.W25Q64_BlockNB == 0){ //如果W25Q64_BlockNB是0,表示是OTA更新A区,进入if
OTA_Info.OTA_flag = 0; //设置OTA_flag,只要不是OTA_SET_FLAG定义的值即可
M24C02_WriteOTAInfo(); //写入24C02内保存
}
NVIC_SystemReset(); //重启
}else{ //判断长度是否是4的整数倍,不是的话进入else
u0_printf("长度错误\r\n"); //串口0输出信息
BootStaFlag &=~ UPDATA_A_FLAG; //清除UPDATA_A_FLAG标志位
}
}
}
}