HC32F460_BootLoader
目录
- 实现目标
- Flash划分
- Bootloader
- 串口接收关键代码
- 固件烧录和跳转关键代码
- APP
- 实验
- 潜在问题
实现目标
- Bootloader接收串口发送的固件,通过按键触发烧录到Flash里
- 通过按键触发BootLoader程序跳转到APP中
Flash划分
- 00扇区到31扇区 0x0000_0000 到 0x0001_FFFF (256KB)BootLoader
- 32扇区到63扇区 0x0002_0000 到 0x0003_FFFF (256KB)APP
注意:APP中63扇区末尾不可写,这里代码量不大不做处理。
Bootloader
串口接收关键代码
定义128KB的固件缓存数组,通过UART + DMA接受固件到数组之中。
/* DMAC */
#define DMA_UNIT (M4_DMA1)
#define DMA_CH (DmaCh0)
#define DMA_TRG_SEL (EVT_USART1_RI)
/* USART channel definition */
#define USART_CH (M4_USART1)
/* USART baudrate definition */
#define USART_BAUDRATE (38400ul)
/* USART RX Port/Pin definition */
#define USART_RX_PORT (PortA)
#define USART_RX_PIN (Pin03)
#define USART_RX_FUNC (Func_Usart1_Rx)
/* USART TX Port/Pin definition */
#define USART_TX_PORT (PortA)
#define USART_TX_PIN (Pin05)
#define USART_TX_FUNC (Func_Usart1_Tx)
/* USART interrupt */
#define USART_EI_NUM (INT_USART1_EI)
#define USART_EI_IRQn (Int001_IRQn)
uint8_t recv_buf[128*1024] = {0};
void BSP_DMA_Init(void)
{
stc_dma_config_t stcDmaInit;
/* Enable peripheral clock */
PWC_Fcg0PeriphClockCmd(PWC_FCG0_PERIPH_DMA1 | PWC_FCG0_PERIPH_DMA2,Enable);
/* Enable DMA. */
DMA_Cmd(DMA_UNIT,Enable);
/* Initialize DMA. */
MEM_ZERO_STRUCT(stcDmaInit);
stcDmaInit.u16BlockSize = 1u; /* 1 block */
stcDmaInit.u32SrcAddr = ((uint32_t)(&USART_CH->DR)+2ul); /* Set source address. */
stcDmaInit.u32DesAddr = (uint32_t)(recv_buf); /* Set destination address. */
stcDmaInit.stcDmaChCfg.enSrcInc = AddressFix; /* Set source address mode. */
stcDmaInit.stcDmaChCfg.enDesInc = AddressIncrease; /* Set destination address mode. */
stcDmaInit.stcDmaChCfg.enIntEn = Disable; /* Enable interrupt. */
stcDmaInit.stcDmaChCfg.enTrnWidth = Dma8Bit; /* Set data width 8bit. */
DMA_InitChannel(DMA_UNIT, DMA_CH, &stcDmaInit);
/* Enable the specified DMA channel. */
DMA_ChannelCmd(DMA_UNIT, DMA_CH, Enable);
/* Clear DMA flag. */
DMA_ClearIrqFlag(DMA_UNIT, DMA_CH, TrnCpltIrq);
/* Enable peripheral circuit trigger function. */
PWC_Fcg0PeriphClockCmd(PWC_FCG0_PERIPH_AOS,Enable);
/* Set DMA trigger source. */
DMA_SetTriggerSrc(DMA_UNIT, DMA_CH, DMA_TRG_SEL);
}
void BSP_UART_Init(void)
{
const stc_usart_uart_init_t stcInitCfg = {
UsartIntClkCkNoOutput,
UsartClkDiv_16,
UsartDataBits8,
UsartDataLsbFirst,
UsartOneStopBit,
UsartParityNone,
UsartSampleBit8,
UsartStartBitFallEdge,
UsartRtsEnable,
};
PWC_Fcg1PeriphClockCmd(PWC_FCG1_PERIPH_USART1, Enable);
/* Initialize USART IO */
PORT_SetFunc(USART_RX_PORT, USART_RX_PIN, USART_RX_FUNC, Disable);
PORT_SetFunc(USART_TX_PORT, USART_TX_PIN, USART_TX_FUNC, Disable);
/* Initialize USART */
uint8_t enRet = USART_UART_Init(USART_CH, &stcInitCfg);
if (enRet != Ok)
{
while (1)
{
}
}
else
{
}
/* Set baudrate */
enRet = USART_SetBaudrate(USART_CH, USART_BAUDRATE);
if (enRet != Ok)
{
while (1)
{
}
}
else
{
}
/*Enable TX && RX && RX interrupt function*/
USART_FuncCmd(USART_CH, UsartTx, Enable);
USART_FuncCmd(USART_CH, UsartRx, Enable);
}
固件烧录和跳转关键代码
将固件缓存数组中的数据烧录到Flash中去
#define FLASH_SECTOR_START_ADDR 0x00020000
while(1)
{
if(Get_KEY1() == Reset) //按下第一个按键烧录程序
{
EFM_Unlock();
EFM_FlashCmd(Enable);
while(Set != EFM_GetFlagStatus(EFM_FLAG_RDY));
uint8_t sector_num = sizeof(recv_buf)/0x2000; //计算扇区数量,一个扇区的存储空间是0x2000=8KB
for(uint8_t i = 0; i < sector_num; i++) //遍历所有扇区
{
EFM_SectorErase(FLASH_SECTOR_START_ADDR + 0x2000*i); //扇区擦除
for(uint32_t j = 0; j < 0x2000 / 4; j++) //遍历一个扇区中的所有字
{
EFM_SingleProgram(FLASH_SECTOR_START_ADDR + 0x2000*i + j*4, //写入数据
*((uint32_t *)(recv_buf + 0x2000*i) + j));
}
}
EFM_Lock();
LED0_TOGGLE(); //黄灯慢闪
vTaskDelay(200);
}
if(Get_KEY2() == Reset) //按下第一个按键跳转
{
EFM_InstructionCacheCmd(Disable);
JumpAddress = *(__IO uint32_t*) (0x20000 + 4);
Jump_To_Application = (pFunction) JumpAddress;
__set_MSP(*(__IO uint32_t*) 0x20000);
Jump_To_Application();
}
LED0_TOGGLE(); //黄灯快闪
vTaskDelay(100);
}
APP
APP代码要注意两件事情
- 修改Flash内存分布
- 修改SCB-VTOR
APP中只有一个蓝灯闪烁的程序
LED1_TOGGLE(); //蓝灯闪烁
vTaskDelay(100);
APP代码要先通过Keil fromelf.exe生成bin,再通过HexEdit转换为可发送的固件字符,
实验
1,BootLoader启动,黄灯快闪
2,通过串口助手将固件发送到固件缓存数组中
3,等待发送完成之后,按下按键,使得存放在ram中的固件缓存数组烧录到位于0x20000的Flash中,此时黄灯慢闪一次
4,等待黄灯恢复快闪,按下按键,跳转到APP中
5,蓝灯快闪,APP运行
潜在问题
只考虑测试固件烧录和跳转目的,上述测试没有问题。但是考虑到实际实现时候,以下问题都要解决:
1,由于DMA目的地指针没有复位手段,UART只能接收一次固件
2,UART接收到的固件未经校验完整性
3,单片机内部开辟128KB的完整固件缓存数组较为奢侈
4,烧录固件过程中断电,设备将会变砖