一直在忙别的事情,忘了更新。
今天再更新一下进展,上次只对GD32F405VG的Flash进行了划分:
BOOT_ADDRESS:0x08000000 - 0x08003FFF 16k(扇区0)
APP_ADDRESS:0x08004000 - 0x0807FFFF 512k-16k=496k(扇区1-7)
Buffer_ADDRESS:0x08080000 - 0x080FEFFF 512k-4k=508k(扇区8-11)
Flags_ADDRESS:0x80FF000 -0x80FFFFF 4k(扇区11的最后4k)
自己简单的绘制了一份流程图,上电初始1个串口,初始化成DMA的收发方式,用于打印信息和接收数据,初始化1个定时器,用于倒计时。上电先读Flags_ADDRESS的数据,Flags_ADDRESS的数据来源于APP中写入到Flash中的数据。
完成Bootloader,最关键的有3点:
1、串口的正常收发;
2、片上Flash的正确读写;
3、跳转函数;
Flash的读写主要是参考兆易创新官方提供的代码例程
/*************************************
* 函 数 名:uint32_t fmc_sector_get(uint32_t address)
* 描 述:根据地址确定扇区号
* 输 入:address:flash地址
* 输 出:无
* 引脚使用:无
************************************/
uint32_t fmc_sector_get(uint32_t address)
{
uint32_t sector = 0;
if((address < ADDR_FMC_SECTOR_1) && (address >= ADDR_FMC_SECTOR_0)){
sector = CTL_SECTOR_NUMBER_0;
}else if((address < ADDR_FMC_SECTOR_2) && (address >= ADDR_FMC_SECTOR_1)){
sector = CTL_SECTOR_NUMBER_1;
}else if((address < ADDR_FMC_SECTOR_3) && (address >= ADDR_FMC_SECTOR_2)){
sector = CTL_SECTOR_NUMBER_2;
}else if((address < ADDR_FMC_SECTOR_4) && (address >= ADDR_FMC_SECTOR_3)){
sector = CTL_SECTOR_NUMBER_3;
}else if((address < ADDR_FMC_SECTOR_5) && (address >= ADDR_FMC_SECTOR_4)){
sector = CTL_SECTOR_NUMBER_4;
}else if((address < ADDR_FMC_SECTOR_6) && (address >= ADDR_FMC_SECTOR_5)){
sector = CTL_SECTOR_NUMBER_5;
}else if((address < ADDR_FMC_SECTOR_7) && (address >= ADDR_FMC_SECTOR_6)){
sector = CTL_SECTOR_NUMBER_6;
}else if((address < ADDR_FMC_SECTOR_8) && (address >= ADDR_FMC_SECTOR_7)){
sector = CTL_SECTOR_NUMBER_7;
}else if((address < ADDR_FMC_SECTOR_9) && (address >= ADDR_FMC_SECTOR_8)){
sector = CTL_SECTOR_NUMBER_8;
}else if((address < ADDR_FMC_SECTOR_10) && (address >= ADDR_FMC_SECTOR_9)){
sector = CTL_SECTOR_NUMBER_9;
}else if((address < ADDR_FMC_SECTOR_11) && (address >= ADDR_FMC_SECTOR_10)){
sector = CTL_SECTOR_NUMBER_10;
}else{
sector = CTL_SECTOR_NUMBER_11;
}
return sector;
}
/*************************************
* 函 数 名:void fmc_erase_sector(uint32_t fmc_sector)
* 描 述:擦除指定扇区
* 输 入:fmc_sector:flash扇区号
* 输 出:无
* 引脚使用:无
************************************/
void fmc_erase_sector(uint32_t fmc_sector)
{
fmc_unlock(); /* unlock the flash program erase controller */
fmc_flag_clear(FMC_FLAG_END | FMC_FLAG_OPERR | FMC_FLAG_WPERR | FMC_FLAG_PGMERR | FMC_FLAG_PGSERR);/* clear pending flags */
if(FMC_READY != fmc_sector_erase(fmc_sector))
{
while(1); /* wait the erase operation complete*/
}
fmc_lock(); /* lock the flash program erase controller */
}
/*************************************
* 函 数 名:void my_fmc_erase_sector(void)
* 描 述:指定地址的扇区
* 输 入:StartAddr:起始地址,EndAddr:终止地址
* 输 出:无
* 引脚使用:无
************************************/
void my_fmc_erase_sector(uint32_t StartAddr, uint32_t EndAddr)
{
uint16_t StartSector, EndSector,i;
fmc_unlock();
fmc_flag_clear(FMC_FLAG_END | FMC_FLAG_OPERR | FMC_FLAG_WPERR | FMC_FLAG_PGMERR | FMC_FLAG_PGSERR);
StartSector = fmc_sector_get(StartAddr); /* 获取起始扇区号 */
EndSector = fmc_sector_get(EndAddr); /* 获取终止扇区号 */
for(i = StartSector; i <= EndSector; i += 8)
{
if(FMC_READY != fmc_sector_erase(i))
{
while(1);
}
}
fmc_lock();
}
/*************************************
* 函 数 名:void fmc_write_32bit_data(uint32_t address, uint16_t length, int32_t* data_32)
* 描 述:写入32位数据
* 输 入: uint32_t address 数据写入的flash地址
uint16_t length 数据个数
int32_t* data_32 数据存储地址
* 输 出:无
* 引脚使用:无
************************************/
void fmc_write_32bit_data(uint32_t address, uint16_t length, int32_t* data_32)
{
uint16_t StartSector, EndSector,i;
fmc_unlock();
fmc_flag_clear(FMC_FLAG_END | FMC_FLAG_OPERR | FMC_FLAG_WPERR | FMC_FLAG_PGMERR | FMC_FLAG_PGSERR);
StartSector = fmc_sector_get(address);
EndSector = fmc_sector_get(address + 4*length); /* */
for(i = StartSector; i <= EndSector; i += 8)
{
if(FMC_READY != fmc_sector_erase(i))
{
while(1);
}
}
for(i=0; i<length; i++)
{
if(FMC_READY == fmc_word_program(address, data_32[i]))
{
address = address + 4;
}
else
{
while(1);
}
}
fmc_lock();
}
void my_fmc_write_32bit_data(uint32_t address, uint16_t length, int32_t* data_32)
{
// uint16_t StartSector, EndSector,i;
fmc_unlock();
fmc_flag_clear(FMC_FLAG_END | FMC_FLAG_OPERR | FMC_FLAG_WPERR | FMC_FLAG_PGMERR | FMC_FLAG_PGSERR);
for(uint16_t i=0; i<length; i++)
{
if(FMC_READY == fmc_word_program(address, data_32[i]))
{
address = address + 4;
}
else
{
while(1);
}
}
fmc_lock();
}
void my_fmc_write_8bit_data(uint32_t address, uint16_t length, uint8_t* data_8)
{
fmc_unlock();
fmc_flag_clear(FMC_FLAG_END | FMC_FLAG_OPERR | FMC_FLAG_WPERR | FMC_FLAG_PGMERR | FMC_FLAG_PGSERR);
for(uint16_t i = 0; i<length; i++)
{
if(FMC_READY == fmc_byte_program(address, data_8[i]))
{
address += 1;
}
else
{
while(1);
}
}
fmc_lock();
}
void fmc_read_32bit_data(uint32_t address, uint16_t length, int32_t* data_32)
{
uint8_t i;
for(i=0; i<length; i++)
{
data_32[i] = *(__IO int32_t*)address;
address = address + 4;
}
}
跳转函数主要参考博主”嵌入式小黑屋“,经过实测,跳转函数可以正常跳转。
void vJumpToApplication(void)
{
uint32_t JumpAddress = *(__IO uint32_t*)(APP_ADDRESS + 4);
pFunction Jump_To_Application;
if(((*(__IO uint32_t*)APP_ADDRESS) & 0x2FFE0000) == 0x20000000) /* APP_ADDRESS是新程序的起始地址,检查栈顶地址是否合法,即栈顶地址是否为0x2000xxxx(内置SRAM) */
{
usart_disable(USART0); /* 关闭串口0 */
timer_disable(TIMER4); /* 关闭定时器4 */
rcu_deinit(); /* 复位系统RCU时钟 */
__set_MSP(*(__IO uint32_t*) APP_ADDRESS); /* 初始化APP堆栈指针(用户代码区的第一个字用存放栈顶地址 */
SCB->VTOR = APP_ADDRESS; /* 重定向中断向量表 */
JumpAddress = *(__IO uint32_t*) (APP_ADDRESS + 4); /* 用户代码区第二个字存储为新程序起始地址(新程序复位向量指针) */
Jump_To_Application = (pFunction) JumpAddress; /* 设置PC指针为新程序复位中断函数的地址 */
Jump_To_Application();
}
else
{
Bootloader_dma_printf("!!!ERROR:APP_ADDRESS SP ERROR!!!\r\n"); /* 打印错误信息 */
}
}