使用的芯片为GD32F303VC
什么是IAP呢?有个博主写的很清楚;就是远程升级;
【单片机开发】单片机的烧录方式详解(ICP、IAP、ISP)_isp烧录-CSDN博客
我们需要写一个boot 和APP
通过 boot对APP的程序进行更新;操作;而不是像单个调试板子那样使用ISP 和 ICP进行烧录;升级过程:上位机和板子进行can通讯;上位机将APP的BIN文件给板子;Boot 进行 0x0801 0000处 的擦除和写入;进而完成APP的更新过程;
也可以看我上篇文章 如何进行在 0x0801 0000处进行擦除和写入的;
GD32F303----内部Flash读写-CSDN博客
IAP(In-Application Programming):在应用编程
还需要了解一些关于Flash的内内容;
在手册里面对应的就是:
GD32F303VCT6-MCU选择器-兆易创新 GigaDevice | 官方网站
前段时间 需要给电池系统升级 ,插上can线,还需要给电池断电,在开启的3s中进行上电;感觉很是麻烦;
实验过程
BOOT:
上电,LED1和LED2 同时闪烁200ms的间隔亮灭; 4s之内没有接收到更新固件的指定(can没有收到数据),那么如果进入can的函数,会更新这个全局变量 g_T_bootIdle ,即进入APP;
#include "gd32f30x.h"
#include "systick.h"
#include "Global.h"
#include "gpio.h"
#include "timer.h"
//#include "can.h"
#include "cancomm.h"
#include <stdbool.h>
#include <stdio.h>
#define DEV_VER 0x0200
CBL_CMD_LIST CMD_List;
u32 g_T_bootIdle = 0;//boot idle timeout.
void GlobeInit(void);
void Init_CMD_List(void);
/*****************************************************************************
* 函数名称: main
*
* 功能描述:
*
* 调用条件限定: None
*
* 输入参数:
*
* 返回结果: None
*
* 附属影响: None
*
* 其他: 主函数
*
*****************************************************************************/
int main(void)
{
uint32_t u32T_Led = 0;//led flash timeout.
uint8_t FlagLedToggle = 0;//flag of led toggle
// VECT_TAB_OFFSET-------0x10000 //Bootloader时需将system_stm32f10x.c文件更改此处为0x1000
//nvic_vector_table_set(NVIC_VECTTAB_FLASH, 0x00000);
GlobeInit();
while(1)
{
fnCan1DealProc();//receive data process.
if(Timer_GetMsDif(g_T_bootIdle, Timer_GetMs()) >= 4000)//
{
if((*((uint32_t *)APP_START_ADDRESS)!=0xFFFFFFFF)) //跳转地址可能是0x08010000(64KB) #define APP_START_ADDRESS ((uint32_t)0x08010000)
{
//Disable the TIM Counter
timer_disable(TIMER2);
CAN_BOOT_JumpToApplication(APP_START_ADDRESS);
} else {
g_T_bootIdle = Timer_GetMs();
}
}
if(Timer_GetMsDif(u32T_Led, Timer_GetMs()) >= 200)//
{
u32T_Led = Timer_GetMs();
if(FlagLedToggle == 0) {
FlagLedToggle = 1;
LED2 = 1;//灭
LED1 = 1;//灭
} else {
FlagLedToggle = 0;
LED2 = 0;//亮
LED1 = 0;//亮
}
}
}
}
/*****************************************************************************
* 函数名称: GlobeInit
*
* 功能描述:
*
* 调用条件限定: None
*
* 输入参数:
*
* 返回结果: None
*
* 附属影响: None
*
* 其他: 系统总初始化
*
*****************************************************************************/
void GlobeInit(void)
{
SystemInit();
systick_config();
TIMR2_Init(500,71); //72M/72=1000Khz的计数频率,计数到500为500us
Can_Init();
GPIOInit();
Init_CMD_List();
}
/*****************************************************************************
* 函数名称: TIM3_IRQHandler
*
* 功能描述:
*
* 调用条件限定: None
*
* 输入参数:
*
* 返回结果: None
*
* 附属影响: None
*
* 其他: 定时器2中断服务程序
*
*****************************************************************************/
void TIMER2_IRQHandler(void) //TIM2中断
{
if(timer_interrupt_flag_get(TIMER2, TIMER_INT_FLAG_UP) != RESET) //检查TIM2更新中断发生与否
{
g_u32HalfMsCnt++;//0.5毫秒奇数器
timer_interrupt_flag_clear(TIMER2, TIMER_INT_FLAG_UP); //清除TIMx更新中断标志
}
}
void Init_CMD_List(void)
{
CMD_List.OnlineCheck = 0x01;
CMD_List.EraseFlash = 0x03;
CMD_List.SetBaudRate = 0x04;
CMD_List.BlockWriteInfo = 0x05;
CMD_List.WriteBlockFlash = 0x06;
CMD_List.BlockReadInfo = 0x07;
CMD_List.ReadBlockFlash = 0x08;
CMD_List.ExcuteApp = 0x09;
CMD_List.CmdFaild = 0x00;
CMD_List.CmdSuccess = 0x0A;
}
/**
* @brief 控制程序跳转到指定位置开始执行 。
* @param Addr 程序执行地址。
* @retval 程序跳转状态。
*/
void CAN_BOOT_JumpToApplication(__IO u32 Addr)
{
pFunction Jump_To_Application;
__IO u32 JumpAddress;
/* Test if user code is programmed starting from address "ApplicationAddress" */
if (((*(__IO u32*)Addr) & 0x2FFE0000 ) == 0x20000000)
{
/* Jump to user application */
JumpAddress = *(__IO u32*) (Addr + 4);
Jump_To_Application = (pFunction) JumpAddress;
//__set_PRIMASK(1);//关闭所有中断
//CAN_ITConfig(CAN1,CAN_IT_FMP0, DISABLE);
/* Initialize user application's Stack Pointer */
__set_MSP(*(__IO u32*)Addr);
Jump_To_Application();
}
}
注意这里;
APP:
GPIO_LedWork(); LED1和LED2 1s进行反转一次;8s之后再次进入boot,因此 NVIC_SystemReset();相当于掉电 在上电的过程。
#include "gd32f30x.h"
#include "systick.h"
void GlobeInit(void);
/*****************************************************************************
* 函数名称: main
*
* 功能描述:
*
* 调用条件限定: None
*
* 输入参数:
*
* 返回结果: None
*
* 附属影响: None
*
* 其他: 主函数
*
*****************************************************************************/
int main(void)
{
// VECT_TAB_OFFSET-------0x10000 //Bootloader时需将system_stm32f10x.c文件更改此处为0x1000
//nvic_vector_table_set(NVIC_VECTTAB_FLASH, 0x10000);
GlobeInit();
while(1)
{
//GPIO_InputDetect();
fnCan1DealProc();
fnSampProc();//电压电流采样
// fnCheckProc();
fnBmsCtrlProc();//BMS控制
// fnDataSave();
//LEDDealCtrl();
GPIO_LedWork();
static uint32_t s_u32Time = 0;
if(Timer_Get5msDif(s_u32Time, Timer_Get5ms()) >= 1600) // 5*1600 = 8000ms 8s后进入boot,
{
s_u32Time = Timer_Get5ms();
__set_FAULTMASK(1);
NVIC_SystemReset();
}
}
}
void GPIO_LedWork(void)
{
static uint8_t s_u8LedVal = 0;
static uint32_t s_u32Time = 0;
if(Timer_Get5msDif(s_u32Time, Timer_Get5ms()) >= 200) // 5*200 = 1000ms 5ms增加一次 ,增加200次,
{
s_u32Time = Timer_Get5ms();
s_u8LedVal ^= 1; // turn
GPIO_LED1_WORK(s_u8LedVal); // on 1s,off 1s
GPIO_LED2_WORK(s_u8LedVal); // on 1s,off 1s
}
}
通过上位机和板子的can通信,可以发送一帧 让板子复位的命令; 进入BOOT 就是升级了 擦除APP的页地址;在写入;