泊头市有做网站的吗,团购网站建设方案,建立自己的网站需要多少钱,wordpress页面权限插件APP程序以及中断向量表的偏移设置
前言
通过之前的了解 之前的了解#xff0c;我们知道实现IAP升级需要两个条件#xff1a; 1.APP程序必须在 IAP 程序之后的某个偏移量为 x 的地址开始#xff1b; 2.APP程序的中断向量表相应的移动#xff0c;移动的偏移量为 x#xff…APP程序以及中断向量表的偏移设置
前言
通过之前的了解 之前的了解我们知道实现IAP升级需要两个条件 1.APP程序必须在 IAP 程序之后的某个偏移量为 x 的地址开始 2.APP程序的中断向量表相应的移动移动的偏移量为 x
1.APP 程序起始地址设置
默认条件下的起始地址
默认的条件下图中 IROM1 的起始地址Start一般为 0x08000000大小Size为 0x100000即从 0x08000000 开始的 1024K 空间为我们的程序存储区。
设置APP起始地址
存储在flash上的APP起始地址设置方法
设置起始地址Start为 0x08010000偏移量为 0x1000064K 字节即留给 BootLoader 的空间因而留给 APP 用的 FLASH 空间Size为 0x80000-0x100000x70000448KB。设置好 Start 和 Size就完成 APP 程序的起始地址设置。IRAM 是内存的地址APP 可以独占这些内存我们不需要修改。
需要确保 APP 起始地址在 Bootloader 程序结束位置之后并且偏移量为 0x200 的倍数即可
存储在SRAM上的APP起始地址设置方法
将 IROM1 的起始地址Start定义为0x20001000大小为 0x19000100K 字节即从地址 0x20000000 偏移0x1000 开始存放 SRAM APP 代码。 STM32F407ZGT6 只有一个 128K(不算 CCM)的片内 SRAM存放程序的位置与变量的加载位置不能重复所以我们需要设置 IRAM1 中的地址SRAM的起始地址变为 0x2001A000分配大小只有 0x6000(24K 字节)。整个 STM32F407ZGT6 的 SRAM不含 CCM分配情况为最开始的 4K 给 Bootloader 程序使用随后的 100K 存放 APP 程序最后 24K用作 APP 程序的内存。
2. 重新设置中断向量表的地址
SCB-VTOR寄存器存放的是中断向量表的起始地址。默认的情况它由 BOOT 的启动模式决定。重定向这个位置这样就可以从 Flash 区域的任意位置启动代码。
就是你跳转到app程序后你后续得跑app的中断向量表。要实现这个就必须在刚进入app的时候重定向中断向量表
/* 设置 NVIC 的向量表偏移寄存器,VTOR 低 9 位保留,即[8:0]保留 */SCB-VTOR baseaddr | (offset (uint32_t)0xFFFFFE00);baseaddr 为基地址即 APP 程序首地址Offset 为偏移量
比如 FLASH APP 设置中断向量表偏移量为 0x10000
SCB-VTOR FLASH_BASE | ( 0x10000 (uint32_t)0xFFFFFE00);SRAM APP 的情况可以参考正点原子探索者开发指南V1.1。
3.设置keil生成bin文件
MDK 默认生成的文件是.hex 文件并不方便用作 IAP更新我们希望生成的文件是.bin 文件.
hex和bin文件的区别
简单来说HEX文档是ascii码的文档。 是不能直接烧到单片机中的。 中间要有转换程序。 但是现在很多编程器都设计成直接可以导入hex文件烧录的其实这是做了设计的。 bin文件是二进制文件是可以直接烧到芯片中中间不用转换的。
设置keil生成bin文件
在 MDK 点击 Options for Target→User 选项卡在 After Build/Rebuild一栏中勾选 Run #1我们推荐使用相对地址在勾选的同一行后的输入框并写入命令行fromelf --bin -o …\OutputL.bin …\Output%L D:\Keil_v5\ARM\ARMCC\bin\fromelf.exe是MDK 自带的格式转换工具 fromelf.exe的路径。
通过这一步设置我们就可以在 MDK 编译成功之后调用 fromelf.exe ..\..\Output\%L 表示当前编译的链接文件…\是相对路径表示上级目录编译器默认从工程文件*.uvprojx 开始查找根据我的工程文件 Output 的位置就能明白路径的含义 指令–bin –o …\OutputL.bin表示在 Output 目录下生成一个.bin 文件 L 在 Keil 的下表示 Output 选项卡下的 Name of Executable 后面的字符串即在 Output 文件夹下生成一个 atk_f407.bin 文件。
在得到.bin 文件之后我们只需要将这个 bin 文件传送给单片机即可执行 IAP 升级。
Bootloader程序的实现
1、APP文件的编写与通常编写一致只需要设定好偏移地址 2、新的中断向量表的偏移设置好 这两点在前文已经写过接下来就是bootloader的实现了。
Bootloader 和 app 属于两个独立的工程不是一个工程 相关资料 1.写 STM32 内部 Flash 的功能用到 STM32 的 Flash操作。STM32片上Flash操作 2.设置bootloader的起始地址为复位之后第一个启动的工程。 这个例子中bootloader从0x800 0000开始APP从0x800 8000开始执行完bootloader之后再执行APP。
BootLoader 的编译器设置 App 的编译器设置
1.bootloader程序也是一个app程序
是我们专门用来搬运APP程序的工具所以也和是和普通程序一样的执行流程。 IAP升级程序(bootloader)启动流程 简单来说 初始化好时钟GPIO外设(依照升级方法来确定初始化对应的外设)那么这块MCU就有了可以和外部通信的能力。
例如使用串口升级那么步骤就是
bootloader的main之前
1.单片机复位从0x800 0000开始执行。 2.执行0x0800 0004处的Rest_Handler复位中断向量函数 3. 复位中断向量函数执行 调用SystemInit函数。这个函数里面开启了外部晶振设置了PLL除能了所有中断设置了时钟
_main 标号表示 C/C标准实时库函数里的一个初始化子程序 _main的入口地址。该程序的一个主要作用是初始化堆栈跳转_user_initial_stackheap标号进行初始化堆栈并初始化映像文件最后跳转到C程序中的main函数。这也正解释了为什么所有的C程序必须有一个main函数作为程序的起点因为这是由C/C标准实时库所规定的。 4.跳转到IAP升级程序bootloader的main函数。
bootloader的main之后
1.初始化
初始化和正常没有bootloader 的程序一下该咋样就咋样为了避免无效功耗损失一般是需要什么就初始化什么。时钟初始化串口初始化定时器初始化等如果有用到定时轮询或者定时器延时等可以初始化
2.检查APP 程序是否完整
若出现某些情况导致APP程序不全有可能导致程序跑飞进入硬件错误。
初始化完成后首先检测 APP 分区中最后地址的标志位是否符合如是否等于0x5a5a一般APP程序的末尾加入特殊字符以检测完整性如果符合启动倒计时时间大约几百毫秒就行根据实际情况决定等待进入 APP 程序在此期间也会等待接收升级的指令否则则一直等待接收升级的指令。 check收到的估计bin的最后四个字节是否为0x5A5A
int CheckIfAPPCanJump(void)
{uint32_t *pFlag (uint32_t *)(APP_FLASH_CODE_BASE APP_FLASH_CODE_SIZE - 4);if (*pFlag 0x5A5A){ return 1; }return 0;
}3.升级
接收到升级指令后则启动升级流程升级期间不允许进入 APP 程序。
4.跳转APP程序(IAP 如何实现跳转到用户程序)
升级完成后需要跳转到 APP 程序可以自动跳转也可以增加跳转指令手动跳转跳转前的准备 1.检测 APP 程序是否存在若存在则继续往下执行APP升级 2.关闭全局中断可选放置升级中被打断 3.清除所有中断标志位可选放置升级中被打断
void Disable_irq(void)
{uint8_t i;__disable_irq(); // 关闭总中断//__enable_irq();/* 清除所有中断标志 */for(i 0; i CRS_IRQn; i){NVIC_ClearPendingIRQ((IRQn_Type)i);}
}4.跳转 APP 启动程序地址当然也能直接跳转到 APP 程序的 main 函数但在跳转前需要重新初始化堆栈等内容所以最好还是交给启动文件处理跳转到resethandle
4.1 设定跳转地址
typedef void (*pFunction)(void); //定义一个pFunction类型 这是个函数指针
pFunction Jump_To_Application; //Jump_To_Application设置为pFunction类型/*设定跳转地址FLASH_USER_START_ADDR是APP程序的起始地址*/
JumpAddress *(__IO uint32_t*) (FLASH_USER_START_ADDR 4);
Jump_To_Application (pFunction) JumpAddress;//将APP程序的中断向量表的Rest_Handler地址赋值给Jump_To_Application
__set_MSP(*(__IO uint32_t*) FLASH_USER_START_ADDR); //这里是将把应用程序起始地址设为栈顶指针
Jump_To_Application(); //设置PC指针为复位地址你可以理解为跳转到应用程序的函数设定跳转地址FLASH_USER_START_ADDR是APP程序的起始地址4是因为中断向量表每四个字节代表一个中断函数的地址。所以JumpAddress指向APP中断向量表的Rest_Handler.
将跳转地址JumpAddress强制转换pFunction类型可以理解为编译器将其编译成一个函数。
2.官方的demo以stm32f10x为例
IAP/src/main.c int main(void)
{/* Flash unlock *//*flash解锁因为需要操作flash*/FLASH_Unlock();/* Initialize Key Button mounted on STM3210X-EVAL board *//*初始化按键demo中的升级触发条件为按键按下*/ STM_EVAL_PBInit(BUTTON_KEY, BUTTON_MODE_GPIO); /* Test if Key push-button on STM3210X-EVAL Board is pressed */if (STM_EVAL_PBGetState(BUTTON_KEY) 0x00){ /* If Key is pressed *//*如果按键按下即触发升级进行升级*//* Execute the IAP driver in order to re-program the Flash *//*初始化串口demo里面使用的是usart1 Y-MODe协议*/IAP_Init();SerialPutString(\r\n);SerialPutString(\r\n (C) COPYRIGHT 2010 STMicroelectronics );SerialPutString(\r\n );SerialPutString(\r\n In-Application Programming Application (Version 3.3.0) );SerialPutString(\r\n );SerialPutString(\r\n By MCD Application Team );SerialPutString(\r\n);SerialPutString(\r\n\r\n);/*升级菜单用户自己实现*//*升级中可选关闭所有中断防止升级被打断但是在跳转到APP程序后要第一时间打开总中断*/Main_Menu ();}/* Keep the user application running */else//不升级 进入APP{/* Test if user code is programmed starting from address ApplicationAddress *//*升级条件不满足跳转到用户程序处执行用户程序*/if (((*(__IO uint32_t*)ApplicationAddress) 0x2FFE0000 ) 0x20000000)//检测栈顶指针{ /* Jump to user application *//*ApplicationAddress为用户程序的栈地址4便为用户程序的复位中断向量地址*/ JumpAddress *(__IO uint32_t*) (ApplicationAddress 4);Jump_To_Application (pFunction) JumpAddress;/* Initialize user applications Stack Pointer */__set_MSP(*(__IO uint32_t*) ApplicationAddress);/*执行用户空间的复位中断向量函数里面主要是进行系统时钟配置执行用户空间的main函数数*/ Jump_To_Application();}}while (1){}
}检测栈顶指针
升级菜单demo更具需求裁剪
void Main_Menu(void)
{uint8_t key 0;/* Get the number of block (4 or 2 pages) from where the user program will be loaded *//*计算IAP占用的flash页数*/ BlockNbr (FlashDestination - 0x08000000) 12;/* Compute the mask to test if the Flash memory, where the user program will beloaded, is write protected */
#if defined (STM32F10X_MD) || defined (STM32F10X_MD_VL)UserMemoryMask ((uint32_t)~((1 BlockNbr) - 1));
#else /* USE_STM3210E_EVAL */if (BlockNbr 62){UserMemoryMask ((uint32_t)~((1 BlockNbr) - 1));}else{UserMemoryMask ((uint32_t)0x80000000);}
#endif /* (STM32F10X_MD) || (STM32F10X_MD_VL) *//* Test if any page of Flash memory where program user will be loaded is write protected *//*检测flash中用户空间的写保护锁是否开启*/ if ((FLASH_GetWriteProtectionOptionByte() UserMemoryMask) ! UserMemoryMask){FlashProtection 1;}else{FlashProtection 0;}while (1){SerialPutString(\r\n Main Menu \r\n\n);SerialPutString( Download Image To the STM32F10x Internal Flash ------- 1\r\n\n);SerialPutString( Upload Image From the STM32F10x Internal Flash ------- 2\r\n\n);SerialPutString( Execute The New Program ------------------------------ 3\r\n\n);if(FlashProtection ! 0){SerialPutString( Disable the write protection ------------------------- 4\r\n\n);}SerialPutString(\r\n\n);key GetKey();if (key 0x31){/* Download user application in the Flash *//*下载程序*/ SerialDownload();}else if (key 0x32){/* Upload user application from the Flash */SerialUpload();}else if (key 0x33){JumpAddress *(__IO uint32_t*) (ApplicationAddress 4);/* Jump to user application */Jump_To_Application (pFunction) JumpAddress;/* Initialize user applications Stack Pointer */__set_MSP(*(__IO uint32_t*) ApplicationAddress);Jump_To_Application();}else if ((key 0x34) (FlashProtection 1)){/* Disable the write protection of desired pages */FLASH_DisableWriteProtectionPages();}else{if (FlashProtection 0){SerialPutString(Invalid Number ! The number should be either 1, 2 or 3\r);}else{SerialPutString(Invalid Number ! The number should be either 1, 2, 3 or 4\r);} }}
}进入APP
APP 代码起始设置见前文 APP中断向量表需要偏移就是你跳转到app程序后你后续得跑app的中断向量表。要实现这个就必须在刚进入app的时候重定向中断向量表
注意在 mian 函数起始处重新设置中断向量表寄存器 SCB-VTOR的偏移量否则 APP 无法正常运行
void SetVectorTable(void)
{uint8_t i;SCB-VTOR APP_FLASH_CODE_BASE;/* 清除所有中断标志 */for(i 0; i CRS_IRQn; i){NVIC_ClearPendingIRQ((IRQn_Type)i);}/* 在BOOT中跳转之前若关闭了全局中断, 此需要重新打开 */__enable_irq();
}