- if(LL_GPIO_IsInputPinSet(KEY_GPIO_Port,KEY_Pin))
- {
- LL_mDelay(100);
- if(LL_GPIO_IsInputPinSet(KEY_GPIO_Port,KEY_Pin))
- {
- LL_GPIO_SetOutputPin(LED_GPIO_Port,LED_Pin);
- BL_StartAPP();
- }
- }
- LL_GPIO_ResetOutputPin(LED_GPIO_Port,LED_Pin);if(LL_GPIO_IsInputPinSet(KEY_GPIO_Port,KEY_Pin))
- {
- LL_mDelay(100);
- if(LL_GPIO_IsInputPinSet(KEY_GPIO_Port,KEY_Pin))
- {
- LL_GPIO_SetOutputPin(LED_GPIO_Port,LED_Pin);
- BL_StartAPP();
- }
- }
接下来需要虚拟一个文件系统,这里如果对文件系统不熟悉也没关系,可以使用分区工具DiskGenius分出一个最小容量的分区,没有空闲分区的可以在虚拟机里操作,格式化后顺便放进一个提示文件,选中这个分区,记录下这两个数值
打开扇区编辑,全选然后另存为一个文件
用winhex打开刚才保存的文件,找到这几个不为0的数据段
使用winhex的复制为C源码的选项将数据粘贴到代码中,其中DBR中有一大段是启动代码可以忽略,FAT1和FAT2的数据一样
- #define BL_FAT_CLUSTER_SIZE 0x200 //簇大小
- #define BL_FAT_INDEX_START_ADDR 0x11000 //目录起始地址
- #define BL_FAT_INDEX_BIN_START_ADDR 0x11080 //写入的BIN文件起始地址
- #define BL_FAT_DATA_START_ADDR 0x15000 //数据起始地址
- //以下是一个8M的FAT16分区的数据结构
- //DBR 0x00-0x1FF 以55 AA结尾,引导程序代码部分省略
- const uint8_t BL_FAT_dbr[70] = {
- 0xEB, 0x3C, 0x90, 0x4D, 0x53, 0x44, 0x4F, 0x53, 0x35, 0x2E, 0x30, 0x00, 0x02, 0x01, 0x08, 0x00,
- 0x02, 0x00, 0x02, 0x00, 0x40, 0xF8, 0x40, 0x00, 0x3F, 0x00, 0xFF, 0x00, 0x00, 0x08, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x29, 0x23, 0x48, 0x00, 0x00, 0x20, 0x20, 0x20, 0x20, 0x20,
- 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x46, 0x41, 0x54, 0x31, 0x36, 0x20, 0x20, 0x20, 0x59, 0x55,
- 0x59, 0x59, 0x31, 0x39, 0x38, 0x39
- };
- //FAT1 0x1000 FAT2 0x9000
- const uint8_t BL_FAT_fat1_fat2[4]=
- {
- 0xF8,0xFF,0xFF,0xFF
- };
- //根目录 0x11000
- const uint8_t BL_FAT_root_dir[128] = {
- 0x53, 0x54, 0x4D, 0x33, 0x32, 0x42, 0x4F, 0x4F, 0x54, 0x20, 0x20, 0x08, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x75, 0x95, 0xCC, 0x5A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x42, 0x68, 0x00, 0x65, 0x00, 0x72, 0x00, 0x65, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x64, 0xFF, 0xFF,
- 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF,
- 0x01, 0x70, 0x00, 0x75, 0x00, 0x74, 0x00, 0x20, 0x00, 0x62, 0x00, 0x0F, 0x00, 0x64, 0x69, 0x00,
- 0x6E, 0x00, 0x20, 0x00, 0x66, 0x00, 0x69, 0x00, 0x6C, 0x00, 0x00, 0x00, 0x65, 0x00, 0x20, 0x00,
- 0x50, 0x55, 0x54, 0x42, 0x49, 0x4E, 0x7E, 0x31, 0x20, 0x20, 0x20, 0x20, 0x00, 0xB3, 0x93, 0x95,
- 0xCC, 0x5A, 0xCC, 0x5A, 0x00, 0x00, 0x94, 0x95, 0xCC, 0x5A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
- };
接下来实现USB读取方法,将这些数据发送给电脑
- void BL_FAT_ReadBlocks512(uint32_t block_addr,uint8_t *data,uint16_t block_len)
- {
- uint32_t data_index = block_addr * 512;
- uint32_t data_len = block_len * 512;
- memset(data,0,data_len);
- if(data_index == 0)//DBR
- {
- memcpy(data,BL_FAT_dbr,70);
- data[510] = 0x55;
- data[511] = 0xAA;
- }
- else if(data_index == 0x1000 || data_index == 0x9000) //FAT1 FAT2
- {
- memcpy(data,BL_FAT_fat1_fat2,4);
- }
- else if(data_index == 0x11000)//根目录
- {
- memcpy(data,BL_FAT_root_dir,128);
- }
- }void BL_FAT_ReadBlocks512(uint32_t block_addr,uint8_t *data,uint16_t block_len)
- {
- uint32_t data_index = block_addr * 512;
- uint32_t data_len = block_len * 512;
- memset(data,0,data_len);
- if(data_index == 0)//DBR
- {
- memcpy(data,BL_FAT_dbr,70);
- data[510] = 0x55;
- data[511] = 0xAA;
- }
- else if(data_index == 0x1000 || data_index == 0x9000) //FAT1 FAT2
- {
- memcpy(data,BL_FAT_fat1_fat2,4);
- }
- else if(data_index == 0x11000)//根目录
- {
- memcpy(data,BL_FAT_root_dir,128);
- }
- }
在usbd_storage_if.c中的STORAGE_Read_FS调用这个方法,编译烧录后拉低PB9通过USB连接电脑和核心板,电脑上会出现这个盘符
打开也能看到提示文件
接下来实现通过U盘接收固件数据,原本想用固定地址接收的,但是有些系统接入U盘后会写入一些系统文件,这样就占用了原本的地址,虽然并没有成功写入但是可能是系统缓存的原因继续添加文件的话文件数据的起始地址就变了,在0x11000这个地址存储着U盘中文件的索引,数据格式如图
向U盘添加文件后会在后面顺序添加这个文件的索引,对于长文件名的文件最后也会追加一个短文件名的索引,因此只需要判断最后一个文件索引的扩展名是不是BIN就行了。
虚拟数据已经占用了前4个索引,因此从第5个索引开始查找,找到最后一个索引,判断文件是否为BIN格式,如果是BIN格式,获取到数据的簇偏移号和大小,成功获取到簇偏移号后就能计算出数据的写入地址,然后等待USB写入数据并将固件的数据写入内部FLASH
- void BL_FAT_WriteBlocks512(uint32_t block_addr,uint8_t *data,uint16_t block_len)
- {
- uint32_t data_index = block_addr * 512;
- uint32_t data_len = block_len * 512;
- uint16_t sum = 0;
- if(data_index < BL_FAT_INDEX_START_ADDR)
- return;
- if(find_bin_data_addr == 0) //还未找到固件起始地址,寻找最后一个不为0的文件索引
- {
- if(data_index < BL_FAT_DATA_START_ADDR)
- {
- if(data_index < BL_FAT_INDEX_BIN_START_ADDR)
- {
- data_index += 0x80;
- data += 0x80;
- data_len -= 0x80;
- find_bin_data_addr = 0;
- current_fat_file_addr = BL_FAT_INDEX_BIN_START_ADDR;
- current_bin_data_addr = 0;
- current_bin_data_size = 0;
- current_flash_addr = APP_ADDR;
- }
- if(data_index > current_fat_file_addr + 31)
- return;
- while(data_len > 0)
- {
- sum = 0;
- for(uint8_t i = 0;i<32;i++)
- {
- sum += data[i];
- }
- if(sum == 0)
- {
- if(current_bin_data_addr != 0 && current_bin_data_size != 0)
- {
- find_bin_data_addr = 1;
- }
- break;
- }
- else
- {
- current_fat_file_addr = data_index;
- if(data[11] == 0x20 && data[8] == 'B' && data[9] == 'I' && data[10] == 'N' && (data[26] > 1 || data[27] > 0))
- {
- current_bin_data_addr = BL_FAT_DATA_START_ADDR + (data[26]+(data[27]<<8)-2)*BL_FAT_CLUSTER_SIZE; //减2才是真正的簇号
- current_bin_data_size = data[28] + (data[29]<<8) + (data[30]<<16) + (data[31]<<24);
- if(current_bin_data_size + BOOT_SIZE> (*(uint16_t*)(FLASHSIZE_BASE))*1024) //固件过大
- {
- current_bin_data_size = 0;
- led_delay = 200;
- }
- }
- }
- data_index += 32;
- data += 32;
- data_len -= 32;
- }
- }
- }
- else
- {
- if(data_index < current_bin_data_addr)
- return;
- if(data_len > current_bin_data_size)
- {
- BL_FlashApp(data,current_bin_data_size);//写入FLASH的方法省略,网上例程很多
- current_bin_data_size = 0;
- }
- else
- {
- BL_FlashApp(data,data_len);
- current_bin_data_size -= data_len;
- }
- if(current_bin_data_size == 0)
- {
- find_bin_data_addr = 0;
- led_delay = 1000;
- }
- }
- }
至此Bootloader部分就完成了,接下来实现APP部分,新建一个工程,在工程设置中修改起始地址和长度
在C/C++标签添加USER_VECT_TAB_ADDRESS到末尾
升级使用的是BIN文件,在User选项卡中添加fromelf --bin -o ".\@L\@L.bin" "#L"就能在编译后生成bin文件了
打开system_stm32f1xx.c修改偏移量
之后就能像平常一样进行编程了,编译出的bin文件就可以拖动到Bootloader的U盘中实现升级了,效果如下
在此基础上还可以增加文件校验加密传输防止降级等功能,有兴趣的可以自行尝试