一、APP制作
1、APP的制作可以基于STM32CubeMX,具体可以参考网上的教程,只需配置基本的时钟树、SWD、调试串口功能即可(需要其他功能的可自行配置)。需要将工程编译成bin文件,这里可以准备多份bin文件(比如串口打印不同的信息),以便后续的BOOT跳转成功与否的观察。
2、修改link.sct文件以及VECT_TAB_OFFSET(这里是为了偏移APP起始位置)
; *************************************************************
; *** Scatter-Loading Description File generated by uVision ***
; *************************************************************
LR_IROM1 0x08010000 0x00080000 { ; load region size_region
ER_IROM1 0x08010000 0x00080000 { ; load address = execution address
*.o (RESET, +First)
*(InRoot$$Sections)
.ANY (+RO)
}
RW_IRAM1 0x20000000 0x00020000 { ; RW data
.ANY (+RW +ZI)
}
}
/*!< Uncomment the following line if you need to relocate your vector Table in
Internal SRAM. */
/* #define VECT_TAB_SRAM */
#define VECT_TAB_OFFSET 0x10000 /*!< Vector Table base offset field.
This value must be a multiple of 0x200. */
/******************************************************************************/
生成APP工程后,link.sct以及VECT_TAB_OFFSET的偏移都是基于片上flash基地址的,也就是0x8000000,直观的可以在keil魔术棒里看到。
由于我们需要把BOOT这一段小程序加载到0x8000000这段地址,所以就需要适当的更改APP的偏移地址(基于BOOT bin文件的大小),这里我们可以偏移0x10000(如前两张图)。红色框内的原始值可以不做更改,但为了不误导,尽量可以更改为0x8010000(APP在flash的起始加载位置)。到这里APP的制作就完成了。
二、BOOT制作
1、工程制作同APP。
2、需要移植一下drv_flash_f4.c里的一些flash操作函数(关于drv_flash_f4.c可以网上自行搜索怎样生成)。这里我们需要把该文件里的所有宏,GetSector、stm32_flash_read、stm32_flash_write、stm32_flash_erase这三个函数移植好。
/* Base address of the Flash sectors Bank 1 */
#define ADDR_FLASH_SECTOR_0 ((uint32_t)0x08000000) /* Base @ of Sector 0, 16 Kbytes */
#define ADDR_FLASH_SECTOR_1 ((uint32_t)0x08004000) /* Base @ of Sector 1, 16 Kbytes */
#define ADDR_FLASH_SECTOR_2 ((uint32_t)0x08008000) /* Base @ of Sector 2, 16 Kbytes */
#define ADDR_FLASH_SECTOR_3 ((uint32_t)0x0800C000) /* Base @ of Sector 3, 16 Kbytes */
#define ADDR_FLASH_SECTOR_4 ((uint32_t)0x08010000) /* Base @ of Sector 4, 64 Kbytes */
#define ADDR_FLASH_SECTOR_5 ((uint32_t)0x08020000) /* Base @ of Sector 5, 128 Kbytes */
#define ADDR_FLASH_SECTOR_6 ((uint32_t)0x08040000) /* Base @ of Sector 6, 128 Kbytes */
#define ADDR_FLASH_SECTOR_7 ((uint32_t)0x08060000) /* Base @ of Sector 7, 128 Kbytes */
#define ADDR_FLASH_SECTOR_8 ((uint32_t)0x08080000) /* Base @ of Sector 8, 128 Kbytes */
#define ADDR_FLASH_SECTOR_9 ((uint32_t)0x080A0000) /* Base @ of Sector 9, 128 Kbytes */
#define ADDR_FLASH_SECTOR_10 ((uint32_t)0x080C0000) /* Base @ of Sector 10, 128 Kbytes */
#define ADDR_FLASH_SECTOR_11 ((uint32_t)0x080E0000) /* Base @ of Sector 11, 128 Kbytes */
/* Base address of the Flash sectors Bank 2 */
#define ADDR_FLASH_SECTOR_12 ((uint32_t)0x08100000) /* Base @ of Sector 0, 16 Kbytes */
#define ADDR_FLASH_SECTOR_13 ((uint32_t)0x08104000) /* Base @ of Sector 1, 16 Kbytes */
#define ADDR_FLASH_SECTOR_14 ((uint32_t)0x08108000) /* Base @ of Sector 2, 16 Kbytes */
#define ADDR_FLASH_SECTOR_15 ((uint32_t)0x0810C000) /* Base @ of Sector 3, 16 Kbytes */
#define ADDR_FLASH_SECTOR_16 ((uint32_t)0x08110000) /* Base @ of Sector 4, 64 Kbytes */
#define ADDR_FLASH_SECTOR_17 ((uint32_t)0x08120000) /* Base @ of Sector 5, 128 Kbytes */
#define ADDR_FLASH_SECTOR_18 ((uint32_t)0x08140000) /* Base @ of Sector 6, 128 Kbytes */
#define ADDR_FLASH_SECTOR_19 ((uint32_t)0x08160000) /* Base @ of Sector 7, 128 Kbytes */
#define ADDR_FLASH_SECTOR_20 ((uint32_t)0x08180000) /* Base @ of Sector 8, 128 Kbytes */
#define ADDR_FLASH_SECTOR_21 ((uint32_t)0x081A0000) /* Base @ of Sector 9, 128 Kbytes */
#define ADDR_FLASH_SECTOR_22 ((uint32_t)0x081C0000) /* Base @ of Sector 10, 128 Kbytes */
#define ADDR_FLASH_SECTOR_23 ((uint32_t)0x081E0000) /* Base @ of Sector 11, 128 Kbytes */
/**
* @brief Gets the sector of a given address
* @param None
* @retval The sector of a given address
*/
static uint8_t GetSector(uint32_t Address)
{
uint32_t sector = 0;
if((Address < ADDR_FLASH_SECTOR_1) && (Address >= ADDR_FLASH_SECTOR_0))
{
sector = FLASH_SECTOR_0;
}
else if((Address < ADDR_FLASH_SECTOR_2) && (Address >= ADDR_FLASH_SECTOR_1))
{
sector = FLASH_SECTOR_1;
}
else if((Address < ADDR_FLASH_SECTOR_3) && (Address >= ADDR_FLASH_SECTOR_2))
{
sector = FLASH_SECTOR_2;
}
else if((Address < ADDR_FLASH_SECTOR_4) && (Address >= ADDR_FLASH_SECTOR_3))
{
sector = FLASH_SECTOR_3;
}
else if((Address < ADDR_FLASH_SECTOR_5) && (Address >= ADDR_FLASH_SECTOR_4))
{
sector = FLASH_SECTOR_4;
}
else if((Address < ADDR_FLASH_SECTOR_6) && (Address >= ADDR_FLASH_SECTOR_5))
{
sector = FLASH_SECTOR_5;
}
else if((Address < ADDR_FLASH_SECTOR_7) && (Address >= ADDR_FLASH_SECTOR_6))
{
sector = FLASH_SECTOR_6;
}
else if((Address < ADDR_FLASH_SECTOR_8) && (Address >= ADDR_FLASH_SECTOR_7))
{
sector = FLASH_SECTOR_7;
}
#if defined(FLASH_SECTOR_8)
else if((Address < ADDR_FLASH_SECTOR_9) && (Address >= ADDR_FLASH_SECTOR_8))
{
sector = FLASH_SECTOR_8;
}
#endif
#if defined(FLASH_SECTOR_9)
else if((Address < ADDR_FLASH_SECTOR_10) && (Address >= ADDR_FLASH_SECTOR_9))
{
sector = FLASH_SECTOR_9;
}
#endif
#if defined(FLASH_SECTOR_10)
else if((Address < ADDR_FLASH_SECTOR_11) && (Address >= ADDR_FLASH_SECTOR_10))
{
sector = FLASH_SECTOR_10;
}
#endif
#if defined(FLASH_SECTOR_11)
else if((Address < ADDR_FLASH_SECTOR_12) && (Address >= ADDR_FLASH_SECTOR_11))
{
sector = FLASH_SECTOR_11;
}
#endif
#if defined(STM32F427xx) || defined(STM32F437xx) || defined(STM32F429xx)|| defined(STM32F439xx) || defined(STM32F469xx) || defined(STM32F479xx)
else if((Address < ADDR_FLASH_SECTOR_13) && (Address >= ADDR_FLASH_SECTOR_12))
{
sector = FLASH_SECTOR_12;
}
else if((Address < ADDR_FLASH_SECTOR_14) && (Address >= ADDR_FLASH_SECTOR_13))
{
sector = FLASH_SECTOR_13;
}
else if((Address < ADDR_FLASH_SECTOR_15) && (Address >= ADDR_FLASH_SECTOR_14))
{
sector = FLASH_SECTOR_14;
}
else if((Address < ADDR_FLASH_SECTOR_16) && (Address >= ADDR_FLASH_SECTOR_15))
{
sector = FLASH_SECTOR_15;
}
else if((Address < ADDR_FLASH_SECTOR_17) && (Address >= ADDR_FLASH_SECTOR_16))
{
sector = FLASH_SECTOR_16;
}
else if((Address < ADDR_FLASH_SECTOR_18) && (Address >= ADDR_FLASH_SECTOR_17))
{
sector = FLASH_SECTOR_17;
}
else if((Address < ADDR_FLASH_SECTOR_19) && (Address >= ADDR_FLASH_SECTOR_18))
{
sector = FLASH_SECTOR_18;
}
else if((Address < ADDR_FLASH_SECTOR_20) && (Address >= ADDR_FLASH_SECTOR_19))
{
sector = FLASH_SECTOR_19;
}
else if((Address < ADDR_FLASH_SECTOR_21) && (Address >= ADDR_FLASH_SECTOR_20))
{
sector = FLASH_SECTOR_20;
}
else if((Address < ADDR_FLASH_SECTOR_22) && (Address >= ADDR_FLASH_SECTOR_21))
{
sector = FLASH_SECTOR_21;
}
else if((Address < ADDR_FLASH_SECTOR_23) && (Address >= ADDR_FLASH_SECTOR_22))
{
sector = FLASH_SECTOR_22;
}
else /* (Address < FLASH_END_ADDR) && (Address >= ADDR_FLASH_SECTOR_23) */
{
sector = FLASH_SECTOR_23;
}
#endif
return sector;
}
/**
* Read data from flash.
* @note This operation's units is word.
*
* @param addr flash address
* @param buf buffer to store read data
* @param size read bytes size
*
* @return result
*/
int stm32_flash_read(uint32_t addr, uint8_t *buf, size_t size)
{
size_t i;
printf("flash size addr is (0x%p)", (void*)(addr + size));
if ((addr + size) > STM32_FLASH_END_ADDRESS)
{
printf("read outrange flash size! addr is (0x%p)", (void*)(addr + size));
return -1;
}
for (i = 0; i < size; i++, buf++, addr++)
{
*buf = *(uint8_t *) addr;
}
return size;
}
/**
* Write data to flash.
* @note This operation's units is word.
* @note This operation must after erase. @see flash_erase.
*
* @param addr flash address
* @param buf the write data buffer
* @param size write bytes size
*
* @return result
*/
int stm32_flash_write(uint32_t addr, const uint8_t *buf, size_t size)
{
int8_t result = 0;
uint32_t end_addr = addr + size;
if ((end_addr) > STM32_FLASH_END_ADDRESS)
{
printf("write outrange flash size! addr is (0x%p)", (void*)(addr + size));
return -1;
}
if (size < 1)
{
return -1;
}
HAL_FLASH_Unlock();
__HAL_FLASH_CLEAR_FLAG(FLASH_FLAG_EOP | FLASH_FLAG_OPERR | FLASH_FLAG_WRPERR | FLASH_FLAG_PGAERR | FLASH_FLAG_PGPERR | FLASH_FLAG_PGSERR);
for (size_t i = 0; i < size; i++, addr++, buf++)
{
//printf("1[%02x] [%02x] [%d]\n", *(uint8_t *)addr, *buf, i);
/* write data to flash */
if (HAL_FLASH_Program(FLASH_TYPEPROGRAM_BYTE, addr, (uint64_t)(*buf)) == HAL_OK)
{
if (*(uint8_t *)addr != *buf)
{
result = -1;
//printf("2[%02x] [%02x] [%d]\n", *(uint8_t *)addr, *buf, i);
break;
}
}
else
{
result = -1;
//printf("3[%02x] [%02x] [%d]\n", *(uint8_t *)addr, *buf, i);
break;
}
}
HAL_FLASH_Lock();
if (result != 0)
{
return result;
}
return size;
}
/**
* Erase data on flash.
* @note This operation is irreversible.
* @note This operation's units is different which on many chips.
*
* @param addr flash address
* @param size erase bytes size
*
* @return result
*/
int stm32_flash_erase(uint32_t addr, size_t size)
{
int8_t result = 0;
uint32_t FirstSector = 0, NbOfSectors = 0;
uint32_t SECTORError = 0;
if ((addr + size) > STM32_FLASH_END_ADDRESS)
{
printf("ERROR: erase outrange flash size! addr is (0x%p)\n", (void*)(addr + size));
return -1;
}
/*Variable used for Erase procedure*/
FLASH_EraseInitTypeDef EraseInitStruct;
/* Unlock the Flash to enable the flash control register access */
HAL_FLASH_Unlock();
__HAL_FLASH_CLEAR_FLAG(FLASH_FLAG_EOP | FLASH_FLAG_OPERR | FLASH_FLAG_WRPERR | FLASH_FLAG_PGAERR | FLASH_FLAG_PGPERR | FLASH_FLAG_PGSERR);
/* Get the 1st sector to erase */
FirstSector = GetSector(addr);
/* Get the number of sector to erase from 1st sector*/
NbOfSectors = GetSector(addr + size - 1) - FirstSector + 1;
/* Fill EraseInit structure*/
EraseInitStruct.TypeErase = FLASH_TYPEERASE_SECTORS;
EraseInitStruct.VoltageRange = FLASH_VOLTAGE_RANGE_3;
EraseInitStruct.Sector = FirstSector;
EraseInitStruct.NbSectors = NbOfSectors;
if (HAL_FLASHEx_Erase(&EraseInitStruct, (uint32_t *)&SECTORError) != HAL_OK)
{
result = -1;
goto __exit;
}
__exit:
HAL_FLASH_Lock();
if (result != 0)
{
return result;
}
printf("erase done: addr (0x%p), size %d", (void*)addr, size);
return size;
}
3、编写跳转APP函数。网上例程较多,可自行参考。
typedef void (*pFunction)(void);
uint8_t jump_to_app(uint32_t app_addr)
{
uint32_t JumpAddress;
pFunction Jump_To_Application;
/* 检查栈顶地址是否合法 */
if(((*(__IO uint32_t *)APP_FLASH_ADDR) & 0x2FFE0000) == 0x20000000)
{
/* 屏蔽所有中断,防止在跳转过程中,中断干扰出现异常 */
__disable_irq();
/* 用户代码区第二个 字 为程序开始地址(复位地址) */
JumpAddress = *(__IO uint32_t *) (app_addr + 4);
/* Initialize user application's Stack Pointer */
/* 初始化APP堆栈指针(用户代码区的第一个字用于存放栈顶地址) */
__set_MSP(*(__IO uint32_t *) app_addr);
/* 类型转换 */
Jump_To_Application = (pFunction) JumpAddress;
/* 跳转到 APP */
Jump_To_Application();
return 1;
}
return 0;
}
三、BOOT引导APP跳转
1、不借助上位机的文件发送功能,直接使用flash进行APP程序的擦写(这里需要JFlash烧写工具,网上自行下载即可,关于JFlash的使用也很简单,只需简单配置芯片型号,然后便可以进行烧写程序)。
1.1、将其中一份APP1 bin文件烧写至0x8040000(这里的地址根据APP bin文件自行判断大小,比如你的bin文件大小为64k,即0x10000,那么至少要将该bin文件烧写至0x8020000(0x8010000+0x10000)),然后可以将APP2 bin文件烧写至0x8060000的地址处。
1.2、编写一个简易的BOOT程序
int main(void)
{
HAL_Init();
SystemClock_Config();
MX_GPIO_Init();
MX_USART1_UART_Init();
printf("\r\n======================================\r\n");
stm32_flash_erase(APP_FLASH_ADDR, 0x30000); //APP_FLASH_ADDR = 0x8010000
/* 每次从FLASH APP区读取1024字节数据,并写入BOOT区 */ //592
int cnt = 0;
int read_start_addr = 0x8040000;
int write_start_addr = APP_FLASH_ADDR;
while (cnt < 97)
{
read_flash_test(read_start_addr, my_buf, 1024);
stm32_flash_write(write_start_addr, my_buf, 1024);
read_start_addr += 1024;
write_start_addr += 1024;
printf("\r\n=================== [%d] ===================\r\n", cnt);
cnt++;
}
read_flash_test(0x8078400, my_buf, 592);
stm32_flash_write(0x8028400, my_buf, 592);
jump_to_app(APP_FLASH_ADDR);
程序很简单,由于只是为了测试少了很多错误判断(各位客官自行加上),大体流程是板子上电后,从0x8000000加载并启动这段BOOT程序,程序按顺序先进行了一些初始化,然后擦除0x8010000处的flash数据(这也是我们要将APP加载并启动的地址),然后按照1K的数据大小进行flash数据搬运,具体搬运多少次看客官的APP bin文件大小了,搬运完成后调用jump_to_app进行跳转APP1。这里int read_start_addr = 0x8040000可以改成0x8060000,BOOT搬运并跳转APP2。
2、可以借助Xcom这个小软件作为上位机发送APP bin文件,此时BOOT需要实现数据的接收,这里采用串口中断接收:
uint8_t single_buff[1]; // 按字节保存APP程序
uint8_t app_buff[128 * 1024] = {0}; // 保存接收到的APP程序
volatile uint32_t app_buff_len = 0; // APP程序的长度(字节)
void start_uart_rx(void)
{
while(HAL_UART_Receive_IT(&huart1, single_buff, 1) != HAL_OK);
}
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
if(huart->Instance == USART1)
{
// 把接收到的数据,放到缓冲区中
app_buff[app_buff_len] = single_buff[0];
app_buff_len++;
}
// 重新开启中断接收
start_uart_rx();
}
main函数:
int main(void)
{
HAL_Init();
SystemClock_Config();
MX_GPIO_Init();
MX_USART1_UART_Init();
start_uart_rx();
printf("\r\n======================================\r\n");
HAL_Delay(15000);//延时需要足够长,此时上位机发送bin文件,中断接收
stm32_flash_erase(APP_FLASH_ADDR, 0x30000);
stm32_flash_write(APP_FLASH_ADDR, app_buff, 0x18650);
jump_to_app(APP_FLASH_ADDR);
是不是很简单!当然这样难免出现数据丢包等问题,这里做个简易测试可不作考虑。BOOT程序启动后,通过Xcom发送APP bin文件即可。