初级会员

- 积分
- 117
- 金钱
- 117
- 注册时间
- 2020-3-12
- 在线时间
- 24 小时
|
1金钱
本帖最后由 liyancao001 于 2023-1-11 17:28 编辑
硬件环境:STM32F767ZGT6
先用原子哥的例程改了下测试是可以的,boot程序运行10s后如果没有其他操作则直接跳转到app,app就是串口打印RTC数据。芯片也改成ZGT6了,APP起始地址改成0x0801800。但是我用STM32CUBE 6.3.0做了boot和app程序,并且使用了freeRTOS系统(理论系统不系统的跟IAP没关系吧)
我设计的boot程序初始化完成后打印个“boot初始化完成”,app程序初始化完成后打印个“app初始化完成”。
boot中复制了原子例程中的相关跳转代码,但是发现跳不过去。测试了4种情况:
①原子例程修改boot + 原子例程修改app ,这个没问题,效果就是串口10s内间隔打印固定字符,10s后提示开始跳转,然后打印RTC时间。
②原子例程修改boot + CUBE版app,这个不行,10s后提示开始跳转,然后就没任何消息了
③CUBE版boot + CUBE版app,这个不行,10s后提示开始跳转,然后就没任何消息了
④CUBE版boot + 原子例程修改app,这个不行,10s后提示开始跳转,然后就没任何消息了
CUBE的Boot添加IAP内容如下:
1)添加了一个iap的.c和.h
- #include "ecu_api_iap.h"
- #include "main.h"
- #include "ecu_api_can.h"
- uint8_t BOOT_RX_BUF[BOOT_REC_LEN] __attribute__ ((at(0X20021000)));//接收缓冲,最大USART_REC_LEN个字节,起始地址为0X20021000.
- uint8_t IAP_Status = 0;//bit0为1表示收到下载申请,需要回应 ,bit4=1表示收到结束标志
- uint16_t CRC_Receive=0;
- iapfun jump2app;
- uint32_t iapbuf[512]; //2K字节缓存
- uint32_t applenth=0;
- //设置栈顶地址
- //addr:栈顶地址
- __asm void MSR_MSP(uint32_t addr)
- {
- MSR MSP, r0 //set Main Stack value
- BX r14
- }
- uint16_t crc_verify(uint8_t *dat,uint32_t lenth)
- {
- uint32_t i,j;
- uint16_t temp;
- uint16_t CRC16=0xFFFF;
- uint8_t LSB;
- for(i=0;i<lenth;i++)
- {
- CRC16 ^= dat[i];
- for(j=0;j<=7;j++)
- {
- LSB=(uint8_t)(CRC16 & 0x0001 );
- CRC16=CRC16>>1;
- if(LSB==1)
- CRC16^=0xA001;
- }
- }
- temp=CRC16;
- CRC16=((temp<<8)&0xFF00)|((temp>>8)&0X00FF);
- return (CRC16);
- }
- //crc校验
- //读取指定地址的字(32位数据)
- //faddr:读地址
- //返回值:对应数据.
- uint32_t STMFLASH_ReadWord(uint32_t faddr)
- {
- return *(__IO uint32_t *)faddr;
- }
- //获取某个地址所在的flash扇区
- //addr:flash地址
- //返回值:0~11,即addr所在的扇区
- uint16_t STMFLASH_GetFlashSector(uint32_t addr)
- {
- if(addr<ADDR_FLASH_SECTOR_1)return FLASH_SECTOR_0;
- else if(addr<ADDR_FLASH_SECTOR_2)return FLASH_SECTOR_1;
- else if(addr<ADDR_FLASH_SECTOR_3)return FLASH_SECTOR_2;
- else if(addr<ADDR_FLASH_SECTOR_4)return FLASH_SECTOR_3;
- else if(addr<ADDR_FLASH_SECTOR_5)return FLASH_SECTOR_4;
- else if(addr<ADDR_FLASH_SECTOR_6)return FLASH_SECTOR_5;
- else if(addr<ADDR_FLASH_SECTOR_7)return FLASH_SECTOR_6;
- return FLASH_SECTOR_7;
- }
- //从指定地址开始写入指定长度的数据
- //特别注意:因为STM32F7的扇区实在太大,没办法本地保存扇区数据,所以本函数
- // 写地址如果非0XFF,那么会先擦除整个扇区且不保存扇区数据.所以
- // 写非0XFF的地址,将导致整个扇区数据丢失.建议写之前确保扇区里
- // 没有重要数据,最好是整个扇区先擦除了,然后慢慢往后写.
- //该函数对OTP区域也有效!可以用来写OTP区!
- //OTP区域地址范围:0X1FF0F000~0X1FF0F41F
- //WriteAddr:起始地址(此地址必须为4的倍数!!)
- //pBuffer:数据指针
- //NumToWrite:字(32位)数(就是要写入的32位数据的个数.)
- void STMFLASH_Write(uint32_t WriteAddr,uint32_t *pBuffer,uint32_t NumToWrite)
- {
- FLASH_EraseInitTypeDef FlashEraseInit;
- HAL_StatusTypeDef FlashStatus=HAL_OK;
- uint32_t SectorError=0;
- uint32_t addrx=0;
- uint32_t endaddr=0;
- if(WriteAddr<STM32_FLASH_BASE||WriteAddr%4)return; //非法地址
-
- HAL_FLASH_Unlock(); //解锁
- addrx=WriteAddr; //写入的起始地址
- endaddr=WriteAddr+NumToWrite*4; //写入的结束地址
-
- if(addrx<0X1FF00000)
- {
- while(addrx<endaddr) //扫清一切障碍.(对非FFFFFFFF的地方,先擦除)
- {
- if(STMFLASH_ReadWord(addrx)!=0XFFFFFFFF)//有非0XFFFFFFFF的地方,要擦除这个扇区
- {
- FlashEraseInit.TypeErase=FLASH_TYPEERASE_SECTORS; //擦除类型,扇区擦除
- FlashEraseInit.Sector=STMFLASH_GetFlashSector(addrx); //要擦除的扇区
- FlashEraseInit.NbSectors=1; //一次只擦除一个扇区
- FlashEraseInit.VoltageRange=FLASH_VOLTAGE_RANGE_3; //电压范围,VCC=2.7~3.6V之间!!
- if(HAL_FLASHEx_Erase(&FlashEraseInit,&SectorError)!=HAL_OK)
- {
- break;//发生错误了
- }
- SCB_CleanInvalidateDCache(); //清除无效的D-Cache
- }else addrx+=4;
- FLASH_WaitForLastOperation(FLASH_WAITETIME); //等待上次操作完成
- }
- }
- FlashStatus=FLASH_WaitForLastOperation(FLASH_WAITETIME); //等待上次操作完成
- if(FlashStatus==HAL_OK)
- {
- while(WriteAddr<endaddr)//写数据
- {
- if(HAL_FLASH_Program(FLASH_TYPEPROGRAM_WORD,WriteAddr,*pBuffer)!=HAL_OK)//写入数据
- {
- break; //写入异常
- }
- WriteAddr+=4;
- pBuffer++;
- }
- }
- HAL_FLASH_Lock(); //上锁
- }
- //从指定地址开始读出指定长度的数据
- //ReadAddr:起始地址
- //pBuffer:数据指针
- //NumToRead:字(32位)数
- void STMFLASH_Read(uint32_t ReadAddr,uint32_t *pBuffer,uint32_t NumToRead)
- {
- uint32_t i;
- for(i=0;i<NumToRead;i++)
- {
- pBuffer[i]=STMFLASH_ReadWord(ReadAddr);//读取4个字节.
- ReadAddr+=4;//偏移4个字节.
- }
- }
- //appxaddr:应用程序的起始地址
- //appbuf:应用程序CODE.
- //appsize:应用程序大小(字节).
- void iap_write_appbin(uint32_t appxaddr,uint8_t *appbuf,uint32_t appsize)
- {
- uint32_t t;
- uint16_t i=0;
- uint32_t temp;
- uint32_t fwaddr=appxaddr;//当前写入的地址
- uint8_t *dfu=appbuf;
- for(t=0;t<appsize;t+=4)
- {
- temp=(uint32_t)dfu[3]<<24;
- temp|=(uint32_t)dfu[2]<<16;
- temp|=(uint32_t)dfu[1]<<8;
- temp|=(uint32_t)dfu[0];
- dfu+=4;//偏移4个字节
- iapbuf[i++]=temp;
- if(i==512)
- {
- i=0;
- STMFLASH_Write(fwaddr,iapbuf,512);
- fwaddr+=2048;//偏移2048 512*4=2048
- }
- }
- if(i)STMFLASH_Write(fwaddr,iapbuf,i);//将最后的一些内容字节写进去.
- }
- //跳转到应用程序段
- //appxaddr:用户代码起始地址.
- void iap_load_app(uint32_t appxaddr)
- {
- if(((*(vu32*)appxaddr)&0x2FF00000)==0x20000000) //检查栈顶地址是否合法.
- {
- printf("boot:地址合法,开始跳转!\r\n");
- HAL_RCC_DeInit();
- jump2app=(iapfun)*(vu32*)(appxaddr+4); //用户代码区第二个字为程序开始地址(复位地址)
- MSR_MSP(*(vu32*)appxaddr); //初始化APP堆栈指针(用户代码区的第一个字用于存放栈顶地址)
- jump2app(); //跳转到APP.
- }
- }
- void IAP_CAN_Send(void)
- {
- CAN_SEND_MSG msg1;
- uint16_t CRC16=0;
- static uint16_t cycle_count=0;
-
- msg1.ID = 0xCF00081;
- msg1.DLC = 8;
- msg1.IDE = API_CAN_IDE_EXT;
-
- if((IAP_Status & 0x01) > 0){
- msg1.data[0]=0xFF;
- msg1.data[1]=0xFF;
- msg1.data[2]=0xFF;
- msg1.data[3]=0xFF;
- msg1.data[4]=0xFF;
- msg1.data[5]=0xF1;
- msg1.data[6]=0;
- msg1.data[7]=0;
- CAN_SendMessage(CAN_PORT_2,msg1);
- IAP_Status &= 0xFE;
- printf("boot收到下载申请!\r\n");
- cycle_count =0;
- vTaskDelay(1000-1);
- }
-
- if((IAP_Status & 0x10) > 0){ //收到完成标志
- cycle_count =0;
- printf("boot用户程序接收完成!\r\n");
- printf("boot代码长度:%dBytes\r\n",applenth);
-
- msg1.data[0]=0xFF;
- msg1.data[1]=0xFF;
- msg1.data[2]=0xFF;
- msg1.data[3]=0xFF;
- msg1.data[4]=0xFF;
- msg1.data[5]=0xF2;
- msg1.data[6]=0;
-
- CRC16 = crc_verify(BOOT_RX_BUF,applenth);
- if(CRC_Receive == CRC16){
- msg1.data[7]=0;
- printf("bootCRC成功!\r\n");
- CAN_SendMessage(CAN_PORT_2,msg1);
- vTaskDelay(1000-1);
- if(((*(vu32*)(0x20021000+4))&0xFF000000)==0x08000000)//判断是否为0X08XXXXXX.
- {
- iap_write_appbin(FLASH_APP1_ADDR,BOOT_RX_BUF,applenth);//更新FLASH代码
- printf("boot 固件更新完成!\r\n");
- }else
- {
- printf("非FLASH应用程序!\r\n");
- }
- printf("boot 开始执行FLASH用户代码!!\r\n");
- if(((*(vu32*)(FLASH_APP1_ADDR+4))&0xFF000000)==0x08000000)//判断是否为0X08XXXXXX.
- {
- iap_load_app(FLASH_APP1_ADDR);//执行FLASH APP代码
- }else{
- printf("boot非FLASH应用程序,无法执行!\r\n");
- }
- }
- else{
- msg1.data[7]=1;
- printf("bootCRC失败:%X,%X!\r\n", CRC16,CRC_Receive);
- CAN_SendMessage(CAN_PORT_2,msg1);
- }
- IAP_Status &= 0xEF;
- }
-
- if(cycle_count < 2000){
- cycle_count ++ ;
- }
- else{
- printf("启动超时,boot 开始执行FLASH用户代码!!\r\n");
- if(((*(vu32*)(FLASH_APP1_ADDR+4))&0xFF000000)==0x08000000)//判断是否为0X08XXXXXX.
- {
- iap_load_app(FLASH_APP1_ADDR);//执行FLASH APP代码
- }else{
- printf("boot非FLASH应用程序,无法执行!\r\n");
- }
- }
- }
复制代码 .h如下
- #ifndef __ECU_API_IAP_H__
- #define __ECU_API_IAP_H__
- /* Includes ------------------------------------------------------------------*/
- //#include "main.h"
- #include "usart.h"
- #include "stdint.h"
- #define BOOT_REC_LEN 240*1024//定义最大接收字节数 320K
- extern uint8_t BOOT_RX_BUF[BOOT_REC_LEN];//接收缓冲,最大USART_REC_LEN个字节,起始地址为0X20021000.
- extern uint8_t IAP_Status;//bie0为1表示收到下载申请,需要回应
- extern uint32_t applenth;
- extern uint16_t CRC_Receive;
- typedef __IO uint32_t vu32;
- typedef void (*iapfun)(void); //定义一个函数类型的参数.
- void MSR_MSP(uint32_t addr); //设置堆栈地址
- //FLASH起始地址
- #define STM32_FLASH_BASE 0x08000000 //STM32 FLASH的起始地址
- #define FLASH_WAITETIME 50000 //FLASH等待超时时间
- #define FLASH_APP1_ADDR 0x08018000 //第一个应用程序起始地址(存放在FLASH)
- //STM32F767 FLASH 扇区的起始地址
- #if defined(DUAL_BANK)
- #define ADDR_FLASH_SECTOR_0 ((uint32_t)0x08000000) //扇区0起始地址, 16 Kbytes
- #define ADDR_FLASH_SECTOR_1 ((uint32_t)0x08004000) //扇区1起始地址, 16 Kbytes
- #define ADDR_FLASH_SECTOR_2 ((uint32_t)0x08008000) //扇区2起始地址, 16 Kbytes
- #define ADDR_FLASH_SECTOR_3 ((uint32_t)0x0800C000) //扇区3起始地址, 16 Kbytes
- #define ADDR_FLASH_SECTOR_4 ((uint32_t)0x08010000) //扇区4起始地址, 64 Kbytes
- #define ADDR_FLASH_SECTOR_5 ((uint32_t)0x08020000) //扇区5起始地址, 128 Kbytes
- #define ADDR_FLASH_SECTOR_6 ((uint32_t)0x08040000) //扇区6起始地址, 128 Kbytes
- #define ADDR_FLASH_SECTOR_7 ((uint32_t)0x08060000) //扇区7起始地址, 128 Kbytes
-
- #define ADDR_FLASH_SECTOR_12 ((uint32_t)0x08080000) //扇区0起始地址, 16 Kbytes
- #define ADDR_FLASH_SECTOR_13 ((uint32_t)0x08084000) //扇区0起始地址, 16 Kbytes
- #define ADDR_FLASH_SECTOR_14 ((uint32_t)0x08088000) //扇区0起始地址, 16 Kbytes
- #define ADDR_FLASH_SECTOR_15 ((uint32_t)0x0808C000) //扇区0起始地址, 16 Kbytes
- #define ADDR_FLASH_SECTOR_16 ((uint32_t)0x08090000) //扇区4起始地址, 64 Kbytes
- #define ADDR_FLASH_SECTOR_17 ((uint32_t)0x080A0000) //扇区5起始地址, 128 Kbytes
- #define ADDR_FLASH_SECTOR_18 ((uint32_t)0x080C0000) //扇区5起始地址, 128 Kbytes
- #define ADDR_FLASH_SECTOR_19 ((uint32_t)0x080E0000) //扇区5起始地址, 128 Kbytes
- #else
- #define ADDR_FLASH_SECTOR_0 ((uint32_t)0x08000000) //扇区0起始地址, 32 Kbytes
- #define ADDR_FLASH_SECTOR_1 ((uint32_t)0x08008000) //扇区1起始地址, 32 Kbytes
- #define ADDR_FLASH_SECTOR_2 ((uint32_t)0x08010000) //扇区2起始地址, 32 Kbytes
- #define ADDR_FLASH_SECTOR_3 ((uint32_t)0x08018000) //扇区3起始地址, 32 Kbytes
- #define ADDR_FLASH_SECTOR_4 ((uint32_t)0x08020000) //扇区4起始地址, 128 Kbytes
- #define ADDR_FLASH_SECTOR_5 ((uint32_t)0x08040000) //扇区5起始地址, 256 Kbytes
- #define ADDR_FLASH_SECTOR_6 ((uint32_t)0x08080000) //扇区6起始地址, 256 Kbytes
- #define ADDR_FLASH_SECTOR_7 ((uint32_t)0x080C0000) //扇区7起始地址, 256 Kbytes
- #endif
- uint32_t STMFLASH_ReadWord(uint32_t faddr); //读出字
- void STMFLASH_Write(uint32_t WriteAddr,uint32_t *pBuffer,uint32_t NumToWrite); //从指定地址开始写入指定长度的数据
- void STMFLASH_Read(uint32_t ReadAddr,uint32_t *pBuffer,uint32_t NumToRead); //从指定地址开始读出指定长度的数据
- void IAP_CAN_Send(void);
- #endif /* __ECU_API_UART_H__ */
复制代码 2) 把iap.c中的IAP_CAN_Send()放到了一个freeFTOS任务中间隔5ms执行一次。
3)程序中可以看出boot运行10s后就执行 iap_load_app(FLASH_APP1_ADDR); 实际根据串口打印数据看也确实执行了。但是后面没有进入app
目前看我的app和boot程序都有问题。先说boot,这个就直接一个跳转函数应该也没啥啊,数据从ram复制到flash都还没测试呢。。 另外还听说需要在跳转前加上HAL_RCC_DeInit(); ,实测没变化。
请教下做IAP应该掌握哪些调试手段呢?flash中的关键数据都具体什么含义呢?
发现0x08018000和0x08018004的数据跟原子例程中下载后的数据不一样。
这是原子例程修改的,可以正常运行的
 
这是我用CUBE做的boot和app下载后的数据
 
再说APP:
我在mian开始位置就加了地址偏移了
int main(void)
{
/* USER CODE BEGIN 1 */
SCB->VTOR = FLASH_BASE | 0x18000;//设置偏移量
查阅了一些帖子,另外的一种方法也测试过,那就是解开USER_VECT_TAB_ADDRESS的定义,并且对应修改修改#define VECT_TAB_OFFSET 0x18000U
通过使用CUBEprogrammer查看了下实际下载到芯片的代码,下图位置的数据是一样的。

而且设置里面也对应设置了,跟我修改的原子例程app一样的:

main部分代码如下
- int main(void)
- {
- /* USER CODE BEGIN 1 */
- SCB->VTOR = FLASH_BASE | 0x18000;//设置偏移量
-
- SCB_EnableICache();//使能I-Cache
- SCB_EnableDCache();//使能D-Cache
- SCB->CACR|=1<<2; //强制D-Cache透写,如不开启,实际使用中可能遇到各种问题
- /* USER CODE END 1 */
- /* MCU Configuration--------------------------------------------------------*/
- /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
- HAL_Init();
- /* USER CODE BEGIN Init */
-
- /* USER CODE END Init */
- /* Configure the system clock */
- SystemClock_Config();
- /* USER CODE BEGIN SysInit */
- /* USER CODE END SysInit */
- /* Initialize all configured peripherals */
- MX_GPIO_Init();
- MX_ADC1_Init();
- MX_ADC3_Init();
- MX_SPI1_Init();
- MX_SPI5_Init();
- MX_CAN1_Init();
- MX_CAN2_Init();
- MX_CAN3_Init();
- MX_SPI2_Init();
- MX_SPI3_Init();
- MX_SPI4_Init();
- MX_SPI6_Init();
- MX_UART4_Init();
- MX_UART7_Init();
- MX_TIM6_Init();
- MX_TIM3_Init();
- /* USER CODE BEGIN 2 */
- printf("app初始化1完成\r\n");
复制代码 计划的IAP逻辑是,在app中收到CAN的刷写请求指令后,直接复位到boot中,boot开始接收数据到ram,接收完成后校验没有错误后把程序复制到0x8018000开始的flash中,然后跳转过去。boot开始运行10s没有收到can数据且app程序空间有数据时也自动跳转。
|
最佳答案
查看完整内容[请看2#楼]
谢谢,试了下没有效果啊,我看SystemInit实现的也是地址偏移,跟SCB->VTOR = FLASH_BASE | 0x18000;这句话重复了啊。
现在能跳转了,但是卡在HAL_Delay这里了,我的timebase默认设置为TIM1了,结果TIM1一直到MX_FREERTOS_Init才开始计数。但是不用IAP的时候HAL_Delay是正常的啊。怎么搞?
我在地址偏移那加了开启所有中断的INTX_ENABLE,但是死到异常中断了。看样子是跳转之前产生的中断在跳转后还想跳过去吧。
有个帖子说: ...
|