目录
实验平台
硬件:银杏科技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. 主函数
c
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 函数
c
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_mount
res = 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_lseek
res = 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相关操作函数
c
#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或者2
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)
{
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接口。