STM32F105实现U盘IAP升级程序

1、理论分析
理论依据很重要,这里稍微介绍一下原理和数据的操作过程。实际实现U盘的IAP升级一共分为两个部分,一部分是bootloader程序,这部分需要使用烧录器或者串口烧写进芯片。另一部分是用户的APP程序,这部分是真正的设备需要执行的程序。实际操作过程就是芯片连接U盘后从U盘读取一部分bin文件的数据到RAM再写入到芯片的flash用户app区,然后循环这个写入过程直到将整个bin文件写入到芯片flash中。然后执行一条指令跳转到用户app程序区开始执行。另外用户app区执行时在systemInit初始化完成后需要将中断表偏移一个地址。实现U盘升级程序需要移植两部分代码,一个是U盘驱动代码,一个是FAT文件系统代码。同时将U盘格式化为FAT32格式,4k对齐。
2、bootloader关键代码
bootloader部分的关键代码如下,U盘的状态机和读取文件并写入到芯片flash的过程。IAP部分代码参考了原子哥的串口IAP代码,但发现直接使用跳转时无法执行。经过测试跳转之前必须要增加__ASM(“CPSID I”);这句关闭中断。但是很奇怪我曾经做过一个串口IAP的bootloader,不加这句是完全没问题的。这个很是让人费解啊。

//跳转到应用程序段
//appxaddr:用户代码起始地址.
void iap_load_app(u32 appxaddr)
{
	if(((*(vu32*)appxaddr)&0x2FFE0000)==0x20000000)	//检查栈顶地址是否合法.
	{ 
	    __ASM("CPSID I");
		jump2app=(iapfun)*(vu32*)(appxaddr+4);		//用户代码区第二个字为程序开始地址(复位地址)					
		__set_MSP(*(__IO uint32_t*) appxaddr);//初始化APP堆栈指针(用户代码区的第一个字用于存放栈顶地址)
		jump2app();									//跳转到APP.
	}
}	


int USBH_USR_Application(void)
{
    UINT Len = 0;
    u16 FramesLen = 0;//最后帧长度
    char FilePath[] = "0:/PROGRAM.bin";
    
    if(HCD_IsDeviceConnected(&USB_OTG_Core) != 0)//判断U盘连接
    {
        switch(UDiskState)
        {
            case FileSysInit:    //文件系统初始化
            {
                if(f_mount(&fatfs, "", 0)!=FR_OK)//判断是否挂载
                {
                    return 1;
                }
                UDiskState = OpenFile;
                break;
            }
            case OpenFile:      //打开文件
            {
                f_mount(&fatfs, "", 0);//挂载
                if(f_open(&file,FilePath,FA_OPEN_EXISTING|FA_READ) != FR_OK )//打开文件,如果不存在则失败
                {
                    f_close(&file);      //关闭文件
                    f_mount(NULL, "", 0);   //卸载
                    USBDiskSig = USBDiskBreak;
                    RunNumber = 0;
                    return 1;
                }
                USBDiskSig = USBDiskLink;//U盘已连接,确认能够打开文件以后才认为U盘有效连接
                FileSize = f_size(&file);//获取文件大小
                WriteInteger = FileSize/WriteUSBLen;
                WriteRemSIG = FileSize%WriteUSBLen;
                Write_Count = 0;//清空次数
                
                UDiskState = UDiskWriteData;
                break;
            }
            case UDiskWriteData://读取数据并执行IAP
            {
                if((WriteInteger > 0)&&(Write_Count < WriteInteger))
                {
                    
                    
                    if(f_read(&file,FileReadBuf,WriteUSBLen,&Len) != FR_OK)//读取指定长度的数据到RAM
                    {
                        f_close(&file);
                        f_mount(NULL, "", 0);
                        USBDiskSig = USBDiskBreak;
                        RunNumber = 0;
                        return 1;
                    }
                    iap_write_appbin(((WriteUSBLen*Write_Count)+FLASH_APP1_ADDR),FileReadBuf,WriteUSBLen);//更新FLASH代码
                    
                    Write_Count++;
                }else
                {
                    if(WriteRemSIG != 0)//有剩余数据
                    {
                        FramesLen = FileSize-(Write_Count*WriteUSBLen);//得到最后一次写入的长度
                        
                        if(f_read(&file,FileReadBuf,FramesLen,&Len) != FR_OK)//读取指定长度的数据到RAM
                        {
                            f_close(&file);
                            f_mount(NULL, "", 0);
                            USBDiskSig = USBDiskBreak;
                            RunNumber = 0;
                            return 1;
                        }
                        iap_write_appbin(((WriteUSBLen*Write_Count)+FLASH_APP1_ADDR),FileReadBuf,FramesLen);//更新FLASH代码
                    }
                    
                    f_close(&file);//关闭文件
                    f_mount(NULL, "", 0);//断开连接
                    Write_Count = 0;
                    UDiskState = FileSysInit;
                    
					if(((*(vu32*)(FLASH_APP1_ADDR+4))&0xFF000000)==0x08000000)//判断是否为0X08XXXXXX.
					{	 
//                        RCC_DeInit();//关闭外设
//                        __set_PRIMASK(1); //关闭总中断
						iap_load_app(FLASH_APP1_ADDR);//执行FLASH APP代码
					}else //非FLASH应用程序,无法执行!
					{
                        USBDiskSig = USBDiskBreak;
                        RunNumber = 0;
					}
                }
                
                
            }
            default:break;
        }
    }else
    {
        f_close(&file);
        f_mount(NULL, "", 0);
        
        return 1;
    }
    
    return 0;
}

3、用户APP程序关键代码
此部分是用户APP的初始化代码,代码中必须要添加中断向量表偏移和打开中断。偏移量需要根据自己定义的用户APP地址修改。本人程序使用IAR编译器编译,所以用户APP程序配置如下图:

void ALL_Init(void)
{
    SystemInit();  //系统时钟源配置 
    NVIC_SetVectorTable(NVIC_VectTab_FLASH, 0x6400);//设置中断向量表偏移
    __ASM("CPSIE I");//打开中断
    ChipDeInit();//芯片外设复位
    NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);	//设置NVIC中断分组2:2位抢占优先级,2位响应优先级
}

STM32F105实现U盘IAP升级程序_第1张图片STM32F105实现U盘IAP升级程序_第2张图片实际这里配置就是修改了stm32f105xC.icf文件中的以下部分:

/*-Specials-*/
define symbol __ICFEDIT_intvec_start__ = 0x08006400;
/*-Memory Regions-*/
define symbol __ICFEDIT_region_ROM_start__ = 0x08006400;
define symbol __ICFEDIT_region_ROM_end__   = 0x0803FFFF;
define symbol __ICFEDIT_region_RAM_start__ = 0x20000000;
define symbol __ICFEDIT_region_RAM_end__   = 0x2000FFFF;

整个程序的源码在我的资源中可以找到地址如下。
https://download.csdn.net/download/u010552215/12016457

你可能感兴趣的:(代码,算法)