五、Bootloader实现实例
1.Flash配置框图
如果从主机下载flash到SRAM,则该flash将保存在0x20000000-0x200003FF中。
如果flash驱动器保存在flash中,而不是从主机下载到SRAM, 0x20000000-0x200003FF未被bootloader占用。
2.BootLoader完整流程
①系统初始化
static ResultStatus_t SysInit(void)
{
ResultStatus_t res = SUCC; // 初始化结果状态为成功
WatchdogInit(); // 初始化看门狗
if(ERR == ClockInit()) // 初始化时钟,如果失败则设置结果状态为错误
{
res = ERR;
}
else
{
SYSCTRL_EnableModule(SYSCTRL_REGFILE); // 启用系统控制模块
CrcInit(); // 初始化CRC
#if ((BL_INTERFACE == BL_INTERFACE_CAN_FD)||(BL_INTERFACE == BL_INTERFACE_CAN))
if(ERR == CanInit()) // 如果接口是CAN或CAN FD,初始化CAN
{
res = ERR;
}
#elif (BL_INTERFACE == BL_INTERFACE_LIN)
if(ERR == LinInit()) // 如果接口是LIN,初始化LIN
{
res = ERR;
}
#else
/*nothing to do*/
#endif
PinmuxInit(); // 初始化引脚复用
#ifdef BL_FLASH_DRV_FROM_HOST
// 使闪存驱动标志无效
FlashSetFlashDrvFlag(RESET);
res = FlashInit(); // 初始化闪存
#else
res = FlashInit();
#endif
}
return res; // 返回结果状态
}
②看门狗初始化
static void WatchdogInit(void)
{
#ifdef WATCHDOG_ENABLE /*如果定义了 WATCHDOG_ENABLE,将执行看门狗初始化逻辑,否则禁用*/
volatile uint32_t uLoop = 0; /* 延迟循环变量*/
/* 检查看门狗控制状态寄存器(WDOG_CS)是否已经设置为所需的时钟、模式和中断配置。如果未设
置,则执行初始化步骤 */
if((WDOG_W->WDOG_CS & 0x00070371U) != 0x00040041U)
{
/* 解锁看门狗 */
WDOG->WDOG_CNT.CNT = 0xB0D9A1C4U;
WDOG->WDOG_CNT.CNT = 0x1A1E3B0FU;
WDOG_W->WDOG_CS = 0x00040041U; /*设置看门狗控制状态*/
WDOG->WDOG_TMO.TMO = 9600U; /*设置超时值*/
while (1U != WDOG->WDOG_CS.CFGUF)/*等待配置更新*/
{
if (uLoop > 0xFFFFFU)
{
break;
}
uLoop++;
}
}
else
{
/* about 300 ms */
if(SUCC != WDOG_SetTimeoutValue(9600U))
{
}
}
FeedWatchdog(); /*喂狗*/
#else
WDOG_Disable(); /*失能*/
#endif
}
③系统定时器初始化
static uint32_t SysTickConfigNoInt(uint32_t ticks)
{
uint32_t res = 0U;
#if (defined(DEV_Z20K118M))|| (defined(DEV_Z20K116M)) || (defined(DEV_Z20K114M)),
/*选择完设备后 检查 ticks 是否超出允许的重装载值。如果超出,设置 res 为 1,表示失败*/
if ((ticks - 1UL) > SysTick_LOAD_RELOAD_Msk)
{
res = 1U;
}
else/* 如果未超出,配置 SysTick 寄存器:设置重装载值、优先级、中断和定时器启用状态。*/
{
SysTick->LOAD = (uint32_t)(ticks - 1UL);
NVIC_SetPriority (SysTick_IRQn, (1UL << __NVIC_PRIO_BITS) - 1UL);
SysTick->VAL = 0UL;
SysTick->CTRL = SysTick_CTRL_CLKSOURCE_Msk |
SysTick_CTRL_ENABLE_Msk;
}
#else /* 对于其他设备型号,执行类似的检查和配置,但使用特定设备的 SysTick寄存器和优先级配置*/
if ((ticks - 1UL) > 0xFFFFFFUL)
{
res = 1U;
}
else
{
Z20_SYSTICK->LOAD = (uint32_t)(ticks - 1UL);
INT_SetPriority (SysTick_IRQn, (1UL << 2U) - 1UL);
Z20_SYSTICK->VAL = 0UL;
Z20_SYSTICK->CTRL = (1U << 2U) | 1UL;
}
#endif
return res;
}
④系统时钟初始化
static ResultStatus_t ClockInit(void)
{
ResultStatus_t res = SUCC; /*声明并初始化结果状态变量 res,默认值为成功(SUCC)。*/
#if (defined(DEV_Z20K118M))|| (defined(DEV_Z20K116M)) || (defined(DEV_Z20K114M))
/* 选择设备型号并选择40MHz作为系统时钟 */
if(CLK_OSC40MEnable(40U, ENABLE, CLK_OSC_XTAL) != SUCC)
{
return ERR;
}
#else
if(CLK_OSC40MEnable(CLK_OSC_FREQ_MODE_HIGH, ENABLE, CLK_OSC_XTAL) != SUCC)
{
return ERR;
}
#endif
if(CLK_SysClkSrc(CLK_SYS_OSC40M) != SUCC)
{
res = ERR;
}
else
{
CLK_SetClkDivider(CLK_CORE, CLK_DIV_1);
CLK_SetClkDivider(CLK_BUS, CLK_DIV_1);
CLK_SetClkDivider(CLK_SLOW, CLK_DIV_5);
/*根据接口类型配置 CAN 或 LIN 的时钟源和分频器。如果是 CAN 接口,将 OSC40M 设置为 CAN 时钟源,并设置分频器为 1;如果是 LIN 接口,将 OSC40M 设置为 LIN 时钟源,并设置分频器为 2。*/
#if ((BL_INTERFACE == BL_INTERFACE_CAN_FD)||(BL_INTERFACE == BL_INTERFACE_CAN))
/* config CAN clock */
if(ERR == CLK_ModuleSrc(CLK_CAN_ID, CLK_SRC_OSC40M))
{
return ERR;
}
CLK_SetClkDivider(CLK_CAN_ID, CLK_DIV_1);
#elif (BL_INTERFACE == BL_INTERFACE_LIN)
/* config LIN clock */
if(ERR == CLK_ModuleSrc(CLK_LIN_ID, CLK_SRC_OSC40M))
{
return ERR;
}
CLK_SetClkDivider(CLK_LIN_ID, CLK_DIV_2);
#else
#endif
}
return res;
}
⑤CRC校验配置
static ResultStatus_t CrcInit(void)
{
ResultStatus_t res = SUCC; /*声明并初始化结果变量 res,默认值为 SUCC,表示函数成功执行*/
SYSCTRL_EnableModule(SYSCTRL_CRC); /*启用 CRC 模块*/
const CRC_Config_t crcCfg =
{
.seedValue = 0xffffffff, //CRC 的初始值,设置为 0xffffffff
.poly = 0x4C11DB7, //CRC 的多项式,设置为 0x4C11DB7
.complementRead = CRC_COMPREAD_INVERT_COMP, //读操作的补码设置
.dataMode = CRC_MODE_32BIT, //数据模式
.readType = CRC_READ_BIT_Y_BYTE_Y, //读类型
.writeType = CRC_WRITE_BIT_Y_BYTE_N //写类型
};
CRC_Init(&crcCfg); /* 初始化结构体内参数*/
return res;
}
⑥引脚复用配置
static void PinmuxInit(void)
{
/*初始化管脚复用配置,根据不同的接口类型(CAN 或 LIN)进行相应的配置*/
#if ((BL_INTERFACE == BL_INTERFACE_CAN_FD)||(BL_INTERFACE == BL_INTERFACE_CAN))
SYSCTRL_EnableModule(BL_CAN_SYS_PORT);
SYSCTRL_EnableModule(SYSCTRL_GPIO);
/*PULL DOWN STB*/
PORT_PinmuxConfig(BL_CAN_PORT, BL_CAN_STB_IO, BL_CAN_STB_PINMUX);
GPIO_SetPinDir(BL_CAN_PORT, BL_CAN_STB_IO, GPIO_OUTPUT);
GPIO_ClearPinOutput(BL_CAN_PORT, BL_CAN_STB_IO);
PORT_PinmuxConfig(BL_CAN_PORT, BL_CAN_TX_IO, BL_CAN_TX_PINMUX);
PORT_PinmuxConfig(BL_CAN_PORT, BL_CAN_RX_IO, BL_CAN_RX_PINMUX);
#elif (BL_INTERFACE == BL_INTERFACE_LIN)
SYSCTRL_EnableModule(BL_LIN_SYS_PORT);
SYSCTRL_EnableModule(SYSCTRL_GPIO);
#if (defined(DEV_Z20K118M))|| (defined(DEV_Z20K116M)) || (defined(DEV_Z20K114M))
/*nothing to do*/
#else
SYSCTRL_EnableModule(BL_LIN_SYS_SLB_PORT);
#endif
PORT_PinmuxConfig(BL_LIN_PORT, BL_LIN_RX_IO, BL_LIN_RX_PINMUX);
PORT_PinmuxConfig(BL_LIN_PORT, BL_LIN_TX_IO, BL_LIN_TX_PINMUX);
/* transciever slpeep pin */
PORT_PinmuxConfig(BL_LIN_SLB_PORT, BL_LIN_SLB_IO, BL_LIN_SLB_PINMUX);
GPIO_SetPinDir(BL_LIN_SLB_PORT, BL_LIN_SLB_IO, GPIO_OUTPUT);
/* pull up */
GPIO_SetPinOutput(BL_LIN_SLB_PORT, BL_LIN_SLB_IO);
#else
/* nothing to do */
#endif
}
⑦闪存初始化
ResultStatus_t FlashInit(void)
{
ResultStatus_t ret = SUCC;
#ifdef BL_FLASH_DRV_FROM_HOST /*如果定义了 BL_FLASH_DRV_FROM_HOST,则表示 Flash 驱动来自主机,此时只需检查 FLASH_FSTAT 寄存器的 FAIL 标志位是否为 1,如果是,则返回 ERR*/
if(1U == flsRegPtr->FLASH_FSTAT.FAIL)
{
ret = ERR;
}
#else /*如果未定义 BL_FLASH_DRV_FROM_HOST,则需要调用不同的 Flash 初始化函数,根据具体设备型号进行初始化。*/
#if (defined(DEV_Z20K118M))|| (defined(DEV_Z20K116M)) || (defined(DEV_Z20K114M))
if(ERR == Flash_Init())
{
ret = ERR;
}
#else
if(ERR == FLASH_Init())
{
ret = ERR;
}
#endif
#endif
return ret;
}
⑧APP检查和跳转
/*检查跳转到应用程序的标志*/
FlagStatus_t CheckJumpToAppFlag(void)
{
uint32_t flag;
#if (defined(DEV_Z20K118M))|| (defined(DEV_Z20K116M)) || (defined(DEV_Z20K114M))
REGFILE_ReadByRegID(BL_STAY_IN_BOOT_FLAG_REGFILE_ID, &flag);
#else
REGFILE_ReadByRegId(BL_STAY_IN_BOOT_FLAG_REGFILE_ID, &flag);
#endif
if(flag == BL_JUMP_TO_APP_FLAG_VALUE)
{
return SET;
}
return RESET;
}
/*检查应用程序是否可用*/
static FlagStatus_t CheckApp(void)
{
uint32_t cnt;
uint8_t *p;
FlagStatus_t res = SET;
for(cnt = 0U; cnt < APP_AVAILABLE_LEN; cnt++)
{
p = (uint8_t *)(uint32_t)(APP_AVAILABLE_ADDR+cnt);
if(*p != appAvailableFlag[cnt])
{
res = RESET;
}
}
return res;
}
/*检查无误跳转至APP*/
void JumpToApp(void)
{
AppEntry appEntryFunc;
/* MISRA 2012 11.1 */
appEntryFunc = (AppEntry)GetEntryPoint((uint8_t *)APP_ENTRY_ADDR);
appEntryFunc();
while(true)
{
}
}
跳转至APP后开始进行系统程序的执行,以及判断每次传输数据的有效性,完整代码如在资源中,有需要的自取。