STM32H743-ARM例程40-U_DISK_IAP
目录
- 实验平台
- IAP(U盘存储介质)
- STM32CubeMX生成工程
- 实验代码
- 实验现象
实验平台
硬件:银杏科技GT7000双核心开发板-ARM-STM32H743XIH6,银杏科技iToolXE仿真器
软件:最新版本STM32CubeH7固件库,STM32CubeMX v6.10.0,开发板环境MDK v5.35,TCP&UDP测试工具,串口工具putty
网盘资料包:链接: https://pan.baidu.com/s/1Y3nwaY4SMxfoUsdqPm2R3w?pwd=inba 提取码: inba
IAP(U盘存储介质)
前面章节我们介绍了IAP相关知识,并且利用UART串口和SD卡传输APP文件,本章我们采用U盘来传输APP文件。
U盘相关知识可以参考文章STM32H743-ARM例程24-USB_MSC
STM32CubeMX生成工程
我们参考前面章节STM32H743-结合CubeMX新建HAL库MDK工程,打开CubeMX软件,重复步骤不再展示,我们来看配置USB部分如下图所示:
配置USB

配置FATFAS

实验代码
本章实验包含两个程序,APP和bootloader,APP程序可以参考STM32H743-ARM例程34-BootROM,把启动地址修改为0x8040000
bootloader程序
1. 主函数
int main(void)
{/* USER CODE BEGIN 1 *//* Enable the CPU Cache */CPU_CACHE_Enable();/* 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_USART6_UART_Init();MX_FATFS_Init();MX_USB_HOST_Init();/* USER CODE BEGIN 2 */uart6.initialize(115200);uart6.printf("\x0c");uart6.printf("\033[1;32;40m");uart6.printf("Hello,I am GT7000!\r\n\r\n"); LED_ON;/* USER CODE END 2 */if(ARM_KEY4_STATE == KEY_UP){ //按键松开状态直接跳向应用程序goto start;}/* Infinite loop *//* USER CODE BEGIN WHILE */while (1){/* USER CODE END WHILE */MX_USB_HOST_Process();/* USER CODE BEGIN 3 */}start:if (((*(__IO uint32_t*)APPLICATION_ADDRESS) & 0x2FFE0000 ) == 0x24000000){ //跳转至用户程序JumpAddress = *(__IO uint32_t*) (APPLICATION_ADDRESS + 4);JumpToApplication = (pFunction) JumpAddress;//初始化用户程序的堆栈指针__set_MSP(*(__IO uint32_t*) APPLICATION_ADDRESS);//跳转至应用程序JumpToApplication();}else{ led_trade();}/* USER CODE END 3 */
}
2. USBH_UserProcess 函数
static void USBH_UserProcess (USBH_HandleTypeDef *phost, uint8_t id)
{/* USER CODE BEGIN CALL_BACK_1 */static FRESULT res;unsigned char buffer[6144];unsigned char * p;unsigned long int ncounter = 0;unsigned int counter;switch(id){case HOST_USER_SELECT_CONFIGURATION:break;case HOST_USER_DISCONNECTION:Appli_state = APPLICATION_DISCONNECT;break;case HOST_USER_CLASS_ACTIVE:Appli_state = APPLICATION_READY;//f_mountres = f_mount(&fatfs,"0:",0);if(res != RES_OK){USBH_UsrLog("\r\nf_mount error!");led_trade();}else{USBH_UsrLog("\r\nf_mount successful!");}res = f_open(&fil,"0:/app.bin",FA_READ); //打开app.bin文件if(res != RES_OK){USBH_UsrLog("\r\nf_open error!");led_trade();}else{USBH_UsrLog("\r\nf_open successful!");}//f_lseekres = f_lseek(&fil,0);if(res != RES_OK){USBH_UsrLog("\r\nf_lseek error!");led_trade();}else{USBH_UsrLog("\r\nf_lseek successful!");}//获取文件信息f_stat("0:/app.bin",&finfo);while(ncounter < finfo.fsize){//读取6144Byte数据res = f_read(&fil,buffer,6144,&counter); //读文件if(res != RES_OK){led_trade();}//写入EXT FLASH中p =(unsigned char*)buffer; USBH_UsrLog("Read APP ...!\r\n"); iap_write_appbin(APPLICATION_ADDRESS,p ,6144);ncounter = ncounter + 6144;} USBH_UsrLog("Load APP finish!\r\n");break;case HOST_USER_CONNECTION:Appli_state = APPLICATION_START;break;default:break;}/* USER CODE END CALL_BACK_1 */
}
3.flash相关操作函数
#include "stmflash.h"
#include "uart6.h"
//读取指定地址的字(32位数据)
//faddr:读地址
//返回值:对应数据.
uint32_t STMFLASH_ReadWord(uint32_t faddr)
{return *(__IO uint32_t *)faddr;
}//获取某个地址所在的flash扇区,仅用于BANK1!!
//addr:flash地址
//返回值:0~11,即addr所在的扇区
uint16_t STMFLASH_GetFlashSector(uint32_t addr)
{if(addr >= FLASH_BANK2_ADDR) //BANK2{if(addr<ADDR_FLASH_SECTOR_1_BANK2)return FLASH_SECTOR_0;else if(addr<ADDR_FLASH_SECTOR_2_BANK2)return FLASH_SECTOR_1;else if(addr<ADDR_FLASH_SECTOR_3_BANK2)return FLASH_SECTOR_2;else if(addr<ADDR_FLASH_SECTOR_4_BANK2)return FLASH_SECTOR_3;else if(addr<ADDR_FLASH_SECTOR_5_BANK2)return FLASH_SECTOR_4;else if(addr<ADDR_FLASH_SECTOR_6_BANK2)return FLASH_SECTOR_5;else if(addr<ADDR_FLASH_SECTOR_7_BANK2)return FLASH_SECTOR_6;}else //BANK1{ if(addr<ADDR_FLASH_SECTOR_1_BANK1)return FLASH_SECTOR_0;else if(addr<ADDR_FLASH_SECTOR_2_BANK1)return FLASH_SECTOR_1;else if(addr<ADDR_FLASH_SECTOR_3_BANK1)return FLASH_SECTOR_2;else if(addr<ADDR_FLASH_SECTOR_4_BANK1)return FLASH_SECTOR_3;else if(addr<ADDR_FLASH_SECTOR_5_BANK1)return FLASH_SECTOR_4;else if(addr<ADDR_FLASH_SECTOR_6_BANK1)return FLASH_SECTOR_5;else if(addr<ADDR_FLASH_SECTOR_7_BANK1)return FLASH_SECTOR_6;}return FLASH_SECTOR_7; }//从指定地址开始写入指定长度的数据
//特别注意:因为STM32H7的扇区实在太大,没办法本地保存扇区数据,所以本函数
// 写地址如果非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; uint32_t bankFlag = 1;if(WriteAddr<STM32_FLASH_BASE||WriteAddr%4)return; //非法地址
// bankFlag = WriteAddr >= FLASH_BANK2_ADDR ? FLASH_BANK_2:FLASH_BANK_1;HAL_FLASH_Unlock(); //解锁 addrx=WriteAddr; //写入的起始地址endaddr=WriteAddr+NumToWrite*4; //写入的结束地址if(addrx<0X1FF00000){while(addrx<endaddr) //扫清一切障碍.(对非FFFFFFFF的地方,先擦除){if(STMFLASH_ReadWord(addrx)!=0XFFFFFFFF)//有非0XFFFFFFFF的地方,要擦除这个扇区{ FlashEraseInit.Banks=bankFlag; //操作BANK1或者2FlashEraseInit.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) {uart6.printf("flash set operation err...\r\n");break;//发生错误了 }SCB_CleanInvalidateDCache(); //清除无效的D-Cache}else addrx+=4;FLASH_WaitForLastOperation(FLASH_WAITETIME,bankFlag); //等待上次操作完成}}FlashStatus=FLASH_WaitForLastOperation(FLASH_WAITETIME,bankFlag); //等待上次操作完成if(FlashStatus==HAL_OK){while(WriteAddr<endaddr)//写数据{if(HAL_FLASH_Program(FLASH_TYPEPROGRAM_FLASHWORD,WriteAddr,(uint64_t)pBuffer)!=HAL_OK)//写入数据{ uart6.printf("flash write operation err...\r\n");break; //写入异常}WriteAddr+=32;pBuffer+=8;}}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个字节. }
}#define FLASH_SECTOR 256*128 //256*4 就是1KB,*128就是128K
uint32_t iapbuf[FLASH_SECTOR]; //1K*128字节缓存
uint8_t binBuf[1024*200];
//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==FLASH_SECTOR){i=0; STMFLASH_Write(fwaddr,iapbuf,FLASH_SECTOR);fwaddr+=FLASH_SECTOR*4;//偏移2048 512*4=2048}} if(i){STMFLASH_Write(fwaddr,iapbuf,i);//将最后的一些内容字节写进去. }}
实验现象
我们将app.bin 文件拷贝到U盘中,运行程序,按下 ARM-KEY 将烧写 Bootloader(IAP)程序烧写程序到GT7000上,通过读取U盘中的app.bin文件,重新上电 ARM-LED 灯闪烁,即 ARM 更新升级成功。
注:GT7000上需要用tpye-c转接器连接U盘和USB-Hs接口。
