Openblt process 2: code flow

Posted by aruns on Mon, 24 Jan 2022 09:02:34 +0100

The book goes on to say: next, we will take an example of mcu. Let's analyze the source code of Openblt

First of all, in the world of programs, main is king, so let's take a look at the emperor's ideas first

int main(void)
{
  /* initialize the microcontroller */
  Init();
  /* initialize the bootloader */
  BootInit();

  /* start the infinite program loop */
  while (1)
  {
    /* run the bootloader task */
    BootTask();
  }

  /* Program should never get here. */
  return 0;
} /*** end of main ***/

The main function is generally very simple. First of all, we need to know that this is a big loop program. After Init and bootInit enter while, we keep cycling the bootask task.

Init(): initializes the hardware and provides an environment for mcu code to run

static void Init(void)
{
  /* HAL library initialization */
  HAL_Init();
  /* configure system clock */
  SystemClock_Config();
} /*** end of Init ***/

BootInit(); Initialization of openblt

void BootInit(void)
{
  /* initialize the CPU */
  CpuInit(); //Non enable interrupt
  /* initialize the watchdog */
  CopInit(); /*Enable open door dog*/
  /* initialize the millisecond timer */
  TimerInit();/*systick initialization*/
  /* initialize the non-volatile memory driver */
  NvmInit();
#if (BOOT_FILE_SYS_ENABLE > 0)
  /* initialize the file system module */
  FileInit();
#endif
#if (BOOT_COM_ENABLE > 0)
  /* initialize the communication module */
  ComInit();/**If a serial port is used, the serial port is initialized/
#endif
#if (ADDON_GATEWAY_MOD_ENABLE > 0)
  /* initialize the gateway module */
  GatewayInit();
#endif
  /* initialize the backdoor entry */
  BackDoorInit();/*Back door initialization*/
} /*** end of BootInit ***/

The code here is also very clear, but some small partners may have some questions here: the code entered by CopInit

void CopInitHook(void)
{
  /* this function is called upon initialization. might as well use it to initialize
   * the LED driver. It is kind of a visual watchdog anyways.
   */
  LedBlinkInit(100);
} /*** end of CopInitHook ***/

Alas, it seems that we are initializing the led lamp. Why is it not a function such as watchdog()?

In addition, for some more strict scenarios, hardware watchdog will be used, similar to

This circuit is a screenshot from the chip manual. You can clearly see that the IO pin of MCU is connected to WDI. In other words, as long as the IO pin level changes (square wave), the chip will not output reset signal. This is the principle of hardware watchdog chip

In the following loop, you can see

 CopService();

Eventually called to

void LedBlinkTask(void)
{
  static blt_bool ledOn = BLT_FALSE;
  static blt_int32u nextBlinkEvent = 0;

  /* check for blink event */
  if (TimerGet() >= nextBlinkEvent)
  {
    /* toggle the LED state */
    if (ledOn == BLT_FALSE)
    {
      ledOn = BLT_TRUE;
      LL_GPIO_ResetOutputPin(GPIOC, LL_GPIO_PIN_13);
    }
    else
    {
      ledOn = BLT_FALSE;
      LL_GPIO_SetOutputPin(GPIOC, LL_GPIO_PIN_13);
    }
    /* schedule the next blink event */
    nextBlinkEvent = TimerGet() + ledBlinkIntervalMs;
  }
} /*** end of LedBlinkTask ***/

Use systick to carry out a timer to realize the high and low level change of IO pin

Then let's look at the bootask function that compares the core

void BootTask(void)
{
  /* service the watchdog */
  CopService();
  /* update the millisecond timer */
  TimerUpdate();
#if (BOOT_FILE_SYS_ENABLE > 0)
  /* call worker task for updating firmware from locally attached file storage */
  FileTask();
#endif /* BOOT_FILE_SYS_ENABLE > 0 */
#if (BOOT_COM_ENABLE > 0)
  /* process possibly pending communication data */
  ComTask();
#endif
#if (ADDON_GATEWAY_MOD_ENABLE > 0)
  /* run the gateway */
  GatewayTask();
#endif
  /* control the backdoor */
  BackDoorCheck();
} /*** end of BootTask ***/

The CopService function has been mentioned above

Let's look at the Comtask() function first

void ComTask(void)
{
  blt_int8u xcpPacketLen;
  /* make xcpCtoReqPacket static for runtime efficiency */
  static blt_int8u xcpCtoReqPacket[BOOT_COM_RX_MAX_DATA];

#if (BOOT_COM_CAN_ENABLE > 0)
  if (CanReceivePacket(&xcpCtoReqPacket[0], &xcpPacketLen) == BLT_TRUE)
  {
    /* make this the active interface */
    comActiveInterface = COM_IF_CAN;
    /* process packet */
    XcpPacketReceived(&xcpCtoReqPacket[0], xcpPacketLen);
  }
#endif
#if (BOOT_COM_RS232_ENABLE > 0)
  if (Rs232ReceivePacket(&xcpCtoReqPacket[0], &xcpPacketLen) == BLT_TRUE)
  {
    /* make this the active interface */
    comActiveInterface = COM_IF_RS232;
    /* process packet */
    XcpPacketReceived(&xcpCtoReqPacket[0], xcpPacketLen);
  }
#endif
#if (BOOT_COM_USB_ENABLE > 0)
  if (UsbReceivePacket(&xcpCtoReqPacket[0], &xcpPacketLen) == BLT_TRUE)
  {
    /* make this the active interface */
    comActiveInterface = COM_IF_USB;
    /* process packet */
    XcpPacketReceived(&xcpCtoReqPacket[0], xcpPacketLen);
  }
#endif
#if (BOOT_COM_NET_ENABLE > 0)
  if (NetReceivePacket(&xcpCtoReqPacket[0], &xcpPacketLen) == BLT_TRUE)
  {
    /* make this the active interface */
    comActiveInterface = COM_IF_NET;
    /* process packet */
    XcpPacketReceived(&xcpCtoReqPacket[0], xcpPacketLen);
  }
#endif
} /*** end of ComTask ***/

What is mainly involved in this is that if in blt_ If the hardware or those hardware are defined in conf.h, it will keep receiving and writing to flash.

BackDoorCheck();

void BackDoorCheck(void)
{
#if (BOOT_BACKDOOR_HOOKS_ENABLE == 0)
#if (BOOT_COM_ENABLE > 0)
  /* check if a connection with the host was already established. in this case the
   * backdoor stays open anyway, so no need to check if it needs to be closed.
   */
  if (ComIsConnected() == BLT_TRUE)
  {
    return;
  }
#endif
#if (BOOT_FILE_SYS_ENABLE > 0)
  /* check if the file module is busy, indicating that a firmware update through the
   * locally attached storage is in progress. in this case the backdoor stays open
   * anyway, so no need to check if it needs to be closed.
   */
  if (FileIsIdle() == BLT_FALSE)
  {
    return;
  }
#endif

  /* when the backdoor is still open, check if it's time to close it */
  if (backdoorOpen == BLT_TRUE)
  {
    /* check if the backdoor entry time window elapsed */
    if (TimerGet() >= (BOOT_BACKDOOR_ENTRY_TIMEOUT_MS + backdoorExtensionTime + backdoorOpenTime))
    {
      /* close the backdoor */
      backdoorOpen = BLT_FALSE;
#if (BOOT_FILE_SYS_ENABLE > 0)
      /* during the timed backdoor no remote update request was detected. now do one
       * last check to see if a firmware update from locally attached storage is
       * pending.
       */
      if (FileHandleFirmwareUpdateRequest() == BLT_FALSE)
#endif
      {
        /* no firmware update requests detected, so attempt to start the user program.
         * this function does not return if a valid user program is present.
         */
        CpuStartUserProgram();
      }
    }
  }
#endif
} /*** end of BackDoorCheck ***/

Where, this line of code

 if (TimerGet() >= (BOOT_BACKDOOR_ENTRY_TIMEOUT_MS + backdoorExtensionTime + backdoorOpenTime))

It explains how long the MCU waits to enter the user program after power on reset. These parameters are macro definitions and can be adjusted according to the actual situation

This code means that if you do not receive the instruction to update the firmware of the upper computer, you will start to start the user program, and once the user program is effective, you will directly jump to the user program

if (FileHandleFirmwareUpdateRequest() == BLT_FALSE)
#endif
      {
        /* no firmware update requests detected, so attempt to start the user program.
         * this function does not return if a valid user program is present.
         */
        CpuStartUserProgram();
      }

The thing that the little partner may be most concerned about here is, how did boot jump from its own boot program to the user program?

Core code in

void CpuStartUserProgram(void)

Just use a function pointer

void (*pProgResetHandler)(void);
pProgResetHandler = (void(*)(void))(*((blt_addr *)CPU_USER_PROGRAM_STARTADDR_PTR));
pProgResetHandler();

In addition, you need to do it before jumping

Release the hardware port, re enable the interrupt, and re place the user vector table (SCB - > vtor). After setting, call

pProgResetHandler();

Just jump to our user process.

The general process is like this. Next, analyze the use of the upper computer

Topics: Embedded system