打印
[研电赛技术支持]

GD32F427之IAP Demo

[复制链接]
29|0
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
1.程序运行过程
IAP程序运行过程
1、 STM32复位后,从地址为0x8000004处取出复位中断向量的地址,并跳转执行复位中断服务程序,随后跳转至IAP程序的main函数。
2、 执行完IAP过程后(STM32内部多出了新写入的程序,地址始于0x8000004+N)跳转至新写入程序的复位向量表,取出新程序的复位中断向量的地址,并跳转执行新程序的复位中断服务程序,随后跳转至新程序的main函数。新程序的main函数应该也具有永不返回的特性。同时应该注意在STM32的内部存储空间在不同的位置上出现了2个中断向量表。
3、 在新程序main函数执行的过程中,一个中断请求来临,PC指针仍会回转至地址为0x8000004中断向量表处,而并不是新程序的中断向量表,注意到这是由STM32的硬件机制决定的。
4、 根据中断源跳转至对应的中断服务,注意此时是跳转至了新程序的中断服务程序中。
5、 中断服务执行完毕后,返回main函数。


关于启动过程的内存关系可看stm32启动过程解析startup启动文件
2.实现
程序分为两个部分,一个是平时运行的程序叫做APP,内容和普通程序一样,另一个程序是IAP程序,用于接收新程序并更新写入指定位置。
2.1 APP部分
APP程序内容和普通程序一样,仅仅在编译器中设置它存放的位置
ROM这里起始地址常规程序是0x0800000,芯片复位后就会从这个向量表地址使用Reset_Handler,最后调用main函数,但是这个地方需要用来放IAP程序向量表,所以将APP程序的向量表地址改为其它,IAP程序留个0x10000或者0x8000字节就够了,0x10000==1x256x256=64KB,根据芯片Flash内存是否有这么大决定。



平时使用ISP时通常需要的是elf或者hex文件,IAP中使用的是bin二进制文件格式,因此添加命令生成bin文件
//前面是keil安装路径下的fromelf.exe命令,OBJ是存放axf文件的目录
D:\promgram_exe\keil5\armmdk\ARM\ARMCC\bin\fromelf.exe  --bin -o  ..\OBJ\RTC.bin ..\OBJ\RTC.axf




编译生成bin文件即可,不可烧录
2.2 IAP部分
1. 使用uart串口接收bin文件,并保存在数组中,这个数组定义为:

u8 USART_RX_BUF[USART_REC_LEN] __attribute__ ((at(0X20001000)));//接收缓冲,最大USART_REC_LEN个字节,起始地址为0X20001000.位于sram中


2. 主函数main.c如下

#include "sys.h"
#include "delay.h"
#include "usart.h"
#include "led.h"
#include "key.h"
#include "lcd.h"
#include "usmart.h"
#include "gdflash.h"
#include "iap.h"
/************************************************
WKS GD32F427ZGT6核心板 实验37
串口IAP实验Bootloader-HAL库函数版
************************************************/

int main(void)
{
        u8 t;
        u8 key;
        u16 oldcount=0;                                    //老的串口接收数据值
        u32 applenth=0;                                    //接收到的app代码长度
        u8 clearflag=0;
       
    HAL_Init();                           //初始化HAL库   
    GD32_Clock_Init(336,8,2,7);          //设置时钟,168Mhz
        delay_init(168);                       //初始化延时函数
        uart_init(256000);                     //初始化USART
        LED_Init();                                                      //初始化LED       
        KEY_Init();                                                      //初始化KEY
        LCD_Init();                                   //初始化LCD
        POINT_COLOR=RED;//设置字体为红色
        LCD_ShowString(30,50,200,16,16,"GD32F427 Core Board");       
        LCD_ShowString(30,70,200,16,16,"IAP TEST");       
        LCD_ShowString(30,90,200,16,16,"2023/7/18");  
        LCD_ShowString(30,110,200,16,16,"KEY_UP:Copy APP2FLASH");
        LCD_ShowString(30,150,200,16,16,"KEY0:Run FLASH APP");
        //显示提示信息
        POINT_COLOR=BLUE;//设置字体为蓝色          
        while(1)
        {
                 if(USART_RX_CNT)
                {
                        if(oldcount==USART_RX_CNT)//新周期内,没有收到任何数据,认为本次数据接收完成.
                        {
                                applenth=USART_RX_CNT;
                                oldcount=0;
                                USART_RX_CNT=0;
                                printf("用户程序接收完成!\r\n");
                                printf("代码长度:%dBytes\r\n",applenth);
                        }else oldcount=USART_RX_CNT;                       
                }
                t++;
                delay_ms(10);
                if(t==30)
                {
                        LED0=!LED0;
                        t=0;
                        if(clearflag)
                        {
                                clearflag--;
                                if(clearflag==0)LCD_Fill(30,170,240,190,WHITE);//清除显示
                        }
                }                   
                key=KEY_Scan(0);
                if(key==WKUP_PRES)        //WK_UP按键按下
                {
                        if(applenth)
                        {
                                printf("开始更新固件...\r\n");       
                printf("flash addr :%x \r\n",(*(volatile uint32_t *)(FLASH_APP1_ADDR + 4)) & 0xFF000000);
                                LCD_ShowString(30,170,200,16,16,"Copying APP2FLASH...");
                                if(((*(vu32*)(0X20001000+4))&0xFF000000)==0x08000000 || ((*(vu32*)(0X20001000+4))&0xFF000000)==0x20000000 )//判断是否为flash或sram合法地址
                                {         
                                        iap_write_appbin(FLASH_APP1_ADDR,USART_RX_BUF,applenth);//将接收到的bin文件更新到指定FLASH地址中   
                                        LCD_ShowString(30,170,200,16,16,"Copy flash APP Successed!!");
                                        printf("固件更新完成!\r\n");       
                                }
                else
                                {
                                        LCD_ShowString(30,170,200,16,16,"Illegal FLASH APP!  ");          
                                        printf("非FLASH应用程序!\r\n");
                                }
                        }else
                        {
                                printf("没有可以更新的固件!\r\n");
                                LCD_ShowString(30,170,200,16,16,"No APP!");
                        }
                        clearflag=7;//标志更新了显示,并且设置7*300ms后清除显示                                                                         
                }
                if(key==KEY0_PRES)        //KEY0按下
                {
                        printf("开始执行FLASH用户代码!!\r\n");
                        if(((*(vu32*)(FLASH_APP1_ADDR+4))&0xFF000000)==0x08000000 || ((*(vu32*)(FLASH_APP1_ADDR+4))&0xFF000000)==0x20000000)//判断是否为0X08XXXXXX.
                        {         printf("开始执行FLASH用户代码++!!\r\n");
                                iap_load_app(FLASH_APP1_ADDR);//从指定地址加载、执行FLASH APP代码
                printf("执行FLASH用户代码结束!!\r\n");
                        }

            else
                        {
                                printf("非FLASH应用程序,无法执行!\r\n");
                                LCD_ShowString(30,170,200,16,16,"Illegal FLASH APP!");          
                        }                                                                         
                        clearflag=7;//标志更新了显示,并且设置7*300ms后清除显示          
                }   
                 
        }
}





3. iap.h

#ifndef __IAP_H__
#define __IAP_H__
#include "sys.h"  
typedef  void (*iapfun)(void);                                //定义一个函数类型的参数.   
#define FLASH_APP1_ADDR                0x08010000          //第一个应用程序起始地址(存放在FLASH)
//#define FLASH_APP1_ADDR                0x20001000          //第一个应用程序起始地址(存放在SRAM)
                                                                                        //保留0X08000000~0X0800FFFF的空间为Bootloader使用(共64KB)          
void iap_load_app(u32 appxaddr);                        //跳转到APP程序执行
void iap_write_appbin(u32 appxaddr,u8 *appbuf,u32 applen);        //在指定地址开始,写入bin
#endif



4. iap.c

#include "sys.h"
#include "delay.h"
#include "usart.h"
#include "gdflash.h"
#include "iap.h"
iapfun jump2app;
u32 iapbuf[512];         //2K字节缓存  
//appxaddr:应用程序的起始地址
//appbuf:应用程序CODE.
//appsize:应用程序大小(字节).
void iap_write_appbin(u32 appxaddr,u8 *appbuf,u32 appsize)
{//在主程序中这样调用:iap_write_appbin(FLASH_APP1_ADDR,USART_RX_BUF,applenth)
//即将保存在USART_RX_BUF数组中bin文件写入指定的FLASH或SRAM地址
        u32 t;
        u16 i=0;
        u32 temp;
        u32 fwaddr=appxaddr;//当前写入的地址
        u8 *dfu=appbuf;
        for(t=0;t<appsize;t+=4)
        {                                                  
                temp=(u32)dfu[3]<<24;   
                temp|=(u32)dfu[2]<<16;   
                temp|=(u32)dfu[1]<<8;
                temp|=(u32)dfu[0];          
                dfu+=4;//偏移4个字节
                iapbuf[i++]=temp;            
                if(i==512)
                {
                        i=0;
                        GDFLASH_Write(fwaddr,iapbuf,512);
                        fwaddr+=2048;//偏移2048  512*4=2048
                }
        }
        if(i)GDFLASH_Write(fwaddr,iapbuf,i);//将最后的一些内容字节写进去.  
}
//跳转到应用程序段
//appxaddr:用户代码起始地址.
void iap_load_app(u32 appxaddr)
{
        if(((*(vu32*)appxaddr)&0x2FFE0000)==0x20000000 || ((*(vu32*)appxaddr)&0xFF000000)==0x08000000)         //检查栈顶地址是否合法.
        {
                jump2app=(iapfun)*(vu32*)(appxaddr+4);                //用户代码区第二个字为程序开始地址(复位地址)               
                MSR_MSP(*(vu32*)appxaddr);                                        //初始化APP堆栈指针(用户代码区的第一个字用于存放栈顶地址)
                jump2app();                                                                        //跳转到APP.
        }
}                 



5. 串口中断程序

//串口1中断服务程序
void USART1_IRQHandler(void)                       
{
        u8 Res;
#if SYSTEM_SUPPORT_OS                 //使用OS
        OSIntEnter();   
#endif
        if((__HAL_UART_GET_FLAG(&UART1_Handler,UART_FLAG_RXNE)!=RESET))  
        {
    HAL_UART_Receive(&UART1_Handler,&Res,1,1000);
                //Res=USART1->DR;
                if(USART_RX_CNT<USART_REC_LEN)
                {
                        USART_RX_BUF[USART_RX_CNT]=Res;//接收到的bin文件存放在这个数组中
                        USART_RX_CNT++;                                                                                                      
                }
        }
        HAL_UART_IRQHandler(&UART1_Handler);       
#if SYSTEM_SUPPORT_OS                 //使用OS
        OSIntExit();                                                                                           
#endif
}
#endif       



6. IAP程序的地址设置不变



2.3 串口发送bin文件
打开串口后点击发送文件,打开bin文件




发送文件延时设置这里需要改为连续发送,否则会分多次发送,则程序错误



注意需要一次性发完,如有中断则程序出错,重新发送,下图这样就是一次发送



下图就是被中断分两次发送,错误了,重新发送



2.4 运行
上电后IAP程序运行,这个过程中串口接收到bin数据保存到数组中,按下WK_UP按键开始将数组中的程序写到指定位置,按下KEY0按键后运行
1. 程序来源:惠勤志远GD32F427开发板实验37串口IAP实验
2.路径E:\document\gd32\GD32F427\3_GD32F427SourceCode\StandardCode-HAL\Task37-IAP_SerialPort
————————————————

                            版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。

原文链接:https://blog.csdn.net/tao_sc/article/details/143865644

9059467ad6837b3b8b.png (164.65 KB )

9059467ad6837b3b8b.png

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

16

主题

40

帖子

0

粉丝