文章目录[隐藏]
LPC1768学习笔记:IAP升级
1.需求:通过串口给主机升级,主机的通信串口与升级串口相同
2.方法:要完成APP与IAP程序的切换判断,我们需要一个flag_update单独标识地址的,当flag_update为0xFF的时候,视为正常上电状态,程序由IAP(0x10000000)可以直接跳转到APP中执行;当flag_update为0x55的时候,视为远程烧写状态,程序由IAP开始之后,不跳转APP,而是执行自己的等待烧写流程。这边具体的流程和应用后面会具体介绍。
为了满足切换判断的功能,就需要该标志位不随APP程序的烧写而发生变化,所以我这边选择将IAP存储区之后的一个扇区,即地址0x10009000作为flag_update的存储地址,这样,在IAP程序不变的情况下,随便怎么烧写APP,只要目标地址满足上述表格中的要求,该标志位就不会发生变化。当然,使用用其他满足条件的存储方式(诸如存入外部Flash、存入EEPROM等)都可以。
一.芯片内存划分:芯片总内存为512K
(1)IAP程序内存 :0x0000 0000 - 0x0000 8FFF -36K(可根据自己调整)
(2)升级标志内存:0x0000 9000 - 0x0000 9FFF -4K
(3)数据缓冲内存:0x0000 B000 - 0x0000 BFFF -4k
(4)APP程序内存 :0x0000 C000 - 0x0007 FFFF -472k
芯片手册内存分布地址:
二.IAP流程图说明:
【IAP】:对于IAP程序来说,首先根据flag_update状态判断是否跳转APP,如果不跳转,则等待升级包。待升级包传输完毕,全部写到APP的地址中之后,再执行跳转到APP程序的起始地址执行(或者直接重启)。跳转或者重启之前需要将flag_update重新置为正常上电状态(0xFF),这样以避免下一次重启之后,程序仍然跑在IAP中。
【APP】:对于APP程序来说,需要实现的很简单,就是在串口收到某些外部指令的时候(可自由设置),将flag_update置为远程烧写状态(0x55),并重启。重启之后,MCU会重新从IAP程序开始执行,执行到flag_update判断,不跳转APP,进而执行IAP中等待升级文件的流程。
三:程序跳转与地址改写
APP:程序主要包括地址配置和跳转IAP两方面功能:
【1】地址配置:
将其地址配置为功能介绍中规划的地址,同时在系统初始化程序中,将SCB->VTOR的地址配置为APP程序的起始地址
在系统void SystemInit (void)改写
【2】跳转IAP:
APP程序需要修改的核心程序体如下所示,串口收数据处理逻辑中,如果收到满足条件的通信码,则改写flag_update所在地址上的值:
if(/*收到满足条件的通信码*/)
{
iap_init(BANK0);
Iap_Write_Config_Value(0x55); //改写标志地址值
//runIap(); // 跳转函数
__set_FAULTMASK(1); //直接重启
NVIC_SystemReset();
四:IAP部分程序:主要概括为以下几个点:地址配置、跳转APP、串口读写
【1】:将其地址配置为功能介绍中规划的地址,同时在系统初始化程序中,将SCB->VTOR的地址配置为APP程序的起始地址:
在系统void SystemInit (void)改写
【2】跳转APP:
IAP程序中需要修改的核心程序体如下所示,上电IAP程序自检,如果位于0X10009000的标志位值为0xFF,则进入APP程序地址运行,反之执行自身的烧写流程:
int main (void)
{
uint8_t APP_UPDATE_VALUE=0xFF;
uint8_t work_mode=0;
uint8_t *flag_update=(uint8_t *)FLAG_UPDATE_ADDRESS; // 0x00009000 ---用户升级标志区 0xFF跳转APP--0X55停留IAP等待升级程序
UARTInit() ; /* 初始化UART2---9600 */
GucRcvOver = 0;
GulRcvCount = 0;
memset(GucIapTmp, 0, 4096); /* 缓冲区清零 */
if((*flag_update)==APP_UPDATE_VALUE) //0xFF
{
work_mode=0;
RunApp();
}
else
{
UARTSend((uint8_t *)(" 等待升级程序BIN文件中 "), strlen(" 等待升级程序BIN文件中 "));
zyIrqDisable(); /* 禁止所有中断 */
u32IAP_PrepareSectors(12, 29); /* 选择12-29扇区 */
u32IAP_EraseSectors (12, 29); /* 擦除12-29扇区 */
zyIrqEnable();
work_mode=1;
}
while(work_mode==1)
{
if(GucRcvOver == 0) //接收完成标志
{
if (GulRcvCount != 0)
{
zyIrqDisable(); //禁止中断
userDataProgram(); /*****下载更新程序*****/
zyIrqEnable();
}
}
else
{
*flag_update=0x55; /*****修改程序跳转标志*****/
UARTSend((uint8_t *)(" 升级结束,等到重启中 "), strlen(" 升级结束,等到重启中 "));
myDelay (5); /* 延时等待稳定 */
memset(GucIapTmp, 0x00, 256); /* 临时缓冲区清空 */
/**刚烧录完跳转有时不稳,直接重启***/
__set_FAULTMASK(1);
NVIC_SystemReset();
}
}
}
其中跳转接口runApp逻辑如下所示:
void RunApp(void )
{
/* Change the Vector Table to the USER_FLASH_START
in case the user application uses interrupts
将矢量表更改为用户闪存启动 如果用户应用程序使用中断 */
// SCB->VTOR = USER_APP_START_ADD ;
JMP_Boot(USER_APP_START_ADD );
// iap_load_app(USER_APP_START_ADD );
}
__asm void JMP_Boot( uint32_t address ){
LDR SP, [R0] ;Load new stack pointer address
LDR PC, [R0, #4] ;Load new program counter address
}
【3】:串口收发就是正常串口收发配置即可;
void UART2_IRQHandler (void)
{
// uint8_t IIRValue, LSRValue;
uint8_t Dummy = Dummy;
volatile uint8_t i;
while ((UART2->IIR & 0x01) == 0)
{ /* 判断是否有中断挂起 */
switch (UART2->IIR & 0x0E) { /* 判断中断标志 */
case 0x04: /* 接收数据中断 A&E=0X04 */
for (i = 0; i < 8; i++) { /* 连续接收8个字节 */
puiRcvData[GulRcvCount++] = UART2->RBR; /* 接收缓冲寄存器 */
}
break;
case 0x0C: /* 字符超时中断 */
while ((UART2->LSR & 0x01) == 0x01) { /* 判断数据是否接收完毕 */
puiRcvData[GulRcvCount++] = UART2->RBR;
}
GucRcvOver = 1;
break;
default:
break;
}
}
}
【5】:将缓冲区的代码复制进去APP区代码:
void userDataProgram (void)
{
uint32_t ulAddr= 0; /* 字节偏移量清0*/ /* Addr:字节偏移量 */
uint32_t ulProgramCount; /* ProgramCount:编程到Flash扇 */
while (GulRcvCount != 0)
{
if (GulRcvCount > (1024 * 4))
{ /* 一次最多写入4K代码量 */
memcpy(GucIapTmp, puiRcvData + ulAddr, 1024 * 4);
GulRcvCount -= (1024 * 4); //清零
ulProgramCount = 1024 * 4;
UARTSend((uint8_t *)(" 写入数据错误,请重新发送BIN文件 "), strlen(" 写入数据错误,请重新发送BIN文件 "));
}
else
{
memcpy(GucIapTmp, puiRcvData + ulAddr, GulRcvCount); //puiRcvData --数据缓冲区
ulProgramCount = GulRcvCount;
GulRcvCount = 0;
if ((ulProgramCount == 256 )
||(ulProgramCount == 512 )
||(ulProgramCount == 1024)
||(ulProgramCount == 4096)) {
goto flashProgram;
}
/*
* 满足编程字节数的要求,256、512、1024等
*/
if (ulProgramCount < 256) {
ulProgramCount = 256;
goto flashProgram;
}
if (ulProgramCount < 512) {
ulProgramCount = 512;
goto flashProgram;
}
if (ulProgramCount < 1024) {
ulProgramCount = 1024;
goto flashProgram;
}
if (ulProgramCount < 4096) {
ulProgramCount = 4096;
goto flashProgram;
}
}
flashProgram:
/******* 升级用户程序空间 *******/
zyIrqDisable();
u32IAP_PrepareSectors(12, 29); /* 选择扇区 */
u32IAP_CopyRAMToFlash(USERLOW + ulAddr, (uint32_t)GucIapTmp, ulProgramCount); /* 写数据到FLASH */
zyIrqEnable();
ulAddr += ulProgramCount;
}
}
版权声明:本文为CSDN博主「weixin_45588193」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/weixin_45588193/article/details/122558875
LPC1768学习笔记:IAP升级
1.需求:通过串口给主机升级,主机的通信串口与升级串口相同
2.方法:要完成APP与IAP程序的切换判断,我们需要一个flag_update单独标识地址的,当flag_update为0xFF的时候,视为正常上电状态,程序由IAP(0x10000000)可以直接跳转到APP中执行;当flag_update为0x55的时候,视为远程烧写状态,程序由IAP开始之后,不跳转APP,而是执行自己的等待烧写流程。这边具体的流程和应用后面会具体介绍。
为了满足切换判断的功能,就需要该标志位不随APP程序的烧写而发生变化,所以我这边选择将IAP存储区之后的一个扇区,即地址0x10009000作为flag_update的存储地址,这样,在IAP程序不变的情况下,随便怎么烧写APP,只要目标地址满足上述表格中的要求,该标志位就不会发生变化。当然,使用用其他满足条件的存储方式(诸如存入外部Flash、存入EEPROM等)都可以。
一.芯片内存划分:芯片总内存为512K
(1)IAP程序内存 :0x0000 0000 - 0x0000 8FFF -36K(可根据自己调整)
(2)升级标志内存:0x0000 9000 - 0x0000 9FFF -4K
(3)数据缓冲内存:0x0000 B000 - 0x0000 BFFF -4k
(4)APP程序内存 :0x0000 C000 - 0x0007 FFFF -472k
芯片手册内存分布地址:
二.IAP流程图说明:
【IAP】:对于IAP程序来说,首先根据flag_update状态判断是否跳转APP,如果不跳转,则等待升级包。待升级包传输完毕,全部写到APP的地址中之后,再执行跳转到APP程序的起始地址执行(或者直接重启)。跳转或者重启之前需要将flag_update重新置为正常上电状态(0xFF),这样以避免下一次重启之后,程序仍然跑在IAP中。
【APP】:对于APP程序来说,需要实现的很简单,就是在串口收到某些外部指令的时候(可自由设置),将flag_update置为远程烧写状态(0x55),并重启。重启之后,MCU会重新从IAP程序开始执行,执行到flag_update判断,不跳转APP,进而执行IAP中等待升级文件的流程。
三:程序跳转与地址改写
APP:程序主要包括地址配置和跳转IAP两方面功能:
【1】地址配置:
将其地址配置为功能介绍中规划的地址,同时在系统初始化程序中,将SCB->VTOR的地址配置为APP程序的起始地址
在系统void SystemInit (void)改写
【2】跳转IAP:
APP程序需要修改的核心程序体如下所示,串口收数据处理逻辑中,如果收到满足条件的通信码,则改写flag_update所在地址上的值:
if(/*收到满足条件的通信码*/)
{
iap_init(BANK0);
Iap_Write_Config_Value(0x55); //改写标志地址值
//runIap(); // 跳转函数
__set_FAULTMASK(1); //直接重启
NVIC_SystemReset();
四:IAP部分程序:主要概括为以下几个点:地址配置、跳转APP、串口读写
【1】:将其地址配置为功能介绍中规划的地址,同时在系统初始化程序中,将SCB->VTOR的地址配置为APP程序的起始地址:
在系统void SystemInit (void)改写
【2】跳转APP:
IAP程序中需要修改的核心程序体如下所示,上电IAP程序自检,如果位于0X10009000的标志位值为0xFF,则进入APP程序地址运行,反之执行自身的烧写流程:
int main (void)
{
uint8_t APP_UPDATE_VALUE=0xFF;
uint8_t work_mode=0;
uint8_t *flag_update=(uint8_t *)FLAG_UPDATE_ADDRESS; // 0x00009000 ---用户升级标志区 0xFF跳转APP--0X55停留IAP等待升级程序
UARTInit() ; /* 初始化UART2---9600 */
GucRcvOver = 0;
GulRcvCount = 0;
memset(GucIapTmp, 0, 4096); /* 缓冲区清零 */
if((*flag_update)==APP_UPDATE_VALUE) //0xFF
{
work_mode=0;
RunApp();
}
else
{
UARTSend((uint8_t *)(" 等待升级程序BIN文件中 "), strlen(" 等待升级程序BIN文件中 "));
zyIrqDisable(); /* 禁止所有中断 */
u32IAP_PrepareSectors(12, 29); /* 选择12-29扇区 */
u32IAP_EraseSectors (12, 29); /* 擦除12-29扇区 */
zyIrqEnable();
work_mode=1;
}
while(work_mode==1)
{
if(GucRcvOver == 0) //接收完成标志
{
if (GulRcvCount != 0)
{
zyIrqDisable(); //禁止中断
userDataProgram(); /*****下载更新程序*****/
zyIrqEnable();
}
}
else
{
*flag_update=0x55; /*****修改程序跳转标志*****/
UARTSend((uint8_t *)(" 升级结束,等到重启中 "), strlen(" 升级结束,等到重启中 "));
myDelay (5); /* 延时等待稳定 */
memset(GucIapTmp, 0x00, 256); /* 临时缓冲区清空 */
/**刚烧录完跳转有时不稳,直接重启***/
__set_FAULTMASK(1);
NVIC_SystemReset();
}
}
}
其中跳转接口runApp逻辑如下所示:
void RunApp(void )
{
/* Change the Vector Table to the USER_FLASH_START
in case the user application uses interrupts
将矢量表更改为用户闪存启动 如果用户应用程序使用中断 */
// SCB->VTOR = USER_APP_START_ADD ;
JMP_Boot(USER_APP_START_ADD );
// iap_load_app(USER_APP_START_ADD );
}
__asm void JMP_Boot( uint32_t address ){
LDR SP, [R0] ;Load new stack pointer address
LDR PC, [R0, #4] ;Load new program counter address
}
【3】:串口收发就是正常串口收发配置即可;
void UART2_IRQHandler (void)
{
// uint8_t IIRValue, LSRValue;
uint8_t Dummy = Dummy;
volatile uint8_t i;
while ((UART2->IIR & 0x01) == 0)
{ /* 判断是否有中断挂起 */
switch (UART2->IIR & 0x0E) { /* 判断中断标志 */
case 0x04: /* 接收数据中断 A&E=0X04 */
for (i = 0; i < 8; i++) { /* 连续接收8个字节 */
puiRcvData[GulRcvCount++] = UART2->RBR; /* 接收缓冲寄存器 */
}
break;
case 0x0C: /* 字符超时中断 */
while ((UART2->LSR & 0x01) == 0x01) { /* 判断数据是否接收完毕 */
puiRcvData[GulRcvCount++] = UART2->RBR;
}
GucRcvOver = 1;
break;
default:
break;
}
}
}
【5】:将缓冲区的代码复制进去APP区代码:
void userDataProgram (void)
{
uint32_t ulAddr= 0; /* 字节偏移量清0*/ /* Addr:字节偏移量 */
uint32_t ulProgramCount; /* ProgramCount:编程到Flash扇 */
while (GulRcvCount != 0)
{
if (GulRcvCount > (1024 * 4))
{ /* 一次最多写入4K代码量 */
memcpy(GucIapTmp, puiRcvData + ulAddr, 1024 * 4);
GulRcvCount -= (1024 * 4); //清零
ulProgramCount = 1024 * 4;
UARTSend((uint8_t *)(" 写入数据错误,请重新发送BIN文件 "), strlen(" 写入数据错误,请重新发送BIN文件 "));
}
else
{
memcpy(GucIapTmp, puiRcvData + ulAddr, GulRcvCount); //puiRcvData --数据缓冲区
ulProgramCount = GulRcvCount;
GulRcvCount = 0;
if ((ulProgramCount == 256 )
||(ulProgramCount == 512 )
||(ulProgramCount == 1024)
||(ulProgramCount == 4096)) {
goto flashProgram;
}
/*
* 满足编程字节数的要求,256、512、1024等
*/
if (ulProgramCount < 256) {
ulProgramCount = 256;
goto flashProgram;
}
if (ulProgramCount < 512) {
ulProgramCount = 512;
goto flashProgram;
}
if (ulProgramCount < 1024) {
ulProgramCount = 1024;
goto flashProgram;
}
if (ulProgramCount < 4096) {
ulProgramCount = 4096;
goto flashProgram;
}
}
flashProgram:
/******* 升级用户程序空间 *******/
zyIrqDisable();
u32IAP_PrepareSectors(12, 29); /* 选择扇区 */
u32IAP_CopyRAMToFlash(USERLOW + ulAddr, (uint32_t)GucIapTmp, ulProgramCount); /* 写数据到FLASH */
zyIrqEnable();
ulAddr += ulProgramCount;
}
}
版权声明:本文为CSDN博主「weixin_45588193」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/weixin_45588193/article/details/122558875
暂无评论