directory
1. The concept of ICP, ISP and IAP
2. How IAP upgrades work
3. Process of IAP upgrade program
4. Implementation of IAP in IAR environment
4.1. BootLoader program design
4.2 User Application program design
4.3 IAR address configuration and file output
5. Expand: Parse HEX files
1. The concept of ICP, ISP and IAP
In the process of project development, SWD, JTAG and other tools are usually used for program burning and simulation. It is more convenient if the product node is less, but when the device node is in mass production, it is necessary to use IAP for program burning.
Briefly explain a few concepts ICP, ISP, and IAP.
ICP In-circuit programmer
ICP: in circuit programming, MCU does not need to have a program inside, power on the program storage area can be programmed, such as the usual use of JTAG, SWD and other ways.
ISP In-system programer
ISP: in system programming, programming is carried out through the serial programming interface dedicated to MCU. MCU needs external conditions for operation, such as crystal vibration, etc.
For example, STM32 sets the corresponding BOOT mode by setting the BOOT pin, and then upgrades the internal Flash through the serial port. It can be said that this way is the manufacturer solidified a BootLoader program inside the chip.
IAP In-application programer
IAP: In application programming, developers design BootLoader programs and upgrade programs through serial ports, CAN, Ethernet and other communication methods.
2. How IAP upgrades work
While there is usually only one User program in the Code area of an MCU chip, IAP splits the Code into two parts, one for the BootLoader and the other for the User Application.
The BootLoader is fixed before delivery. When the User Application needs to be changed, it only needs to trigger the BootLoader to erase and re-write the User Application to complete the replacement of the User Application.
After initialization, the program will first enter the BootLoader, in which it will detect whether the conditions are triggered (whether the key is pressed, whether the serial port receives specific data, whether the USB disk is inserted, etc.). If so, the User Application will be erased and re-written into the new program. If not, jump directly to the BootLoader to execute the User Application.
3. Process of IAP upgrade program
Assume that the device only has User Application. Take STM32F103ZET6 as an example, there are three startup modes: built-in FLASH startup, built-in SRAM startup, and system memory ROM startup. The BOOT0 and BOOT1 pins can be set to select the boot mode. In this case, the built-in FLASH is selected. The address of STM32F103ZET6 FLASH is 0x08000000-0x0807ffff, with a total of 512KB.
Generally, the interruption of STM32 occurs in the following five steps:
1. Interruption (interrupt request) occurs;
2. Search the interrupt function entry address in the interrupt vector table;
3. Jump to interrupt function;
4. Execute interrupt function;
5. Interrupt return.
In other words, there is an interrupt direction table in the built-in Flash of STM32 to store the entry address of each interrupt service function. The allocation of the built-in Flash is as shown in the figure below:
So when there is only one program (User Applicatio only), the program execution will go as follows:
Parse the picture above:
STM32F103ZET6 has an interrupt direction table, which is stored in the last 4 bytes at the beginning of the code (i.e. 0x08000004). The address at the top of the stack is stored in the first 4 bytes of the code. When the interrupt occurs, the program obtains the corresponding interrupt service program entry address by searching the table. Then jump to the corresponding interrupt service program execution.
After the device is powered on, it takes out the address of reset interrupt vector from 0x08000004 and jumps to the entrance of reset interrupt program (as shown in label ①). After execution, it jumps to main function (as shown in label ②). In the process of the implementation of the main function is interrupted, the STM32 pointer refers to force the PC to scale back to interrupt (grade 3), found in the interruption to the scale of the corresponding interrupt function entry address, jump to the corresponding interrupt service function (shown in label (4)), after the execution of the interrupt function return to the main function of (shown in label (5)).
Now, let’s get down to business.
If the User Application and BootLoader program are added to the built-in Flash of STM32F103ZET6, the Flash allocation is roughly as shown in the figure below:
At this point, the User Application and the BootLoader program each have an interrupt direction table. Assuming that the space occupied by the BootLoader program is N+M bytes, the direction of the program should be shown as follows:
Parse the picture above:
The initial program of device power-on still takes out the reset interrupt vector address from 0x08000004 and jumps to main(label ①) of IAP after executing the reset interrupt function. After the main function of IAP is executed (check whether the condition is triggered in BootLoader (whether the key is pressed, whether the serial port receives specific data, whether the USB disk is inserted, etc.), erase and re-write the User Application new program if so. If not, jump directly to BootLoader and execute User Application) to forcibly jump to 0x08000004+N+M (label ②), and finally jump to the new main function (label ③), when the interrupt request occurs, The program jumps to the new interrupt direction table to fetch the new interrupt function entry address, and then jumps to the new interrupt service function to execute (as shown in label ④⑤). After executing the interrupt function, it returns to the main function (as shown in label ⑥).
4. Implementation of IAP in IAR environment
Take the IAR environment as an example to briefly describe the implementation steps of IAP. The MCU here takes HC32L130 as an example. Because the MCU used is different, the implementation details are not consistent, but basically the official Demo routine will be provided.
In this example, Flash is allocated as follows: BootLoader address: 0x00000000 to 0x00000DFF, User Application address: 0x00001000 to 0x0000FFFF.
4.1. BootLoader program design
Step 1: Design the overall architecture, including three functions: detect BootLoader flag program, IAP configuration program and IAP burning function program.
/ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * \ \ brief IAP main function param None ** ** \retval int32_t Return value, if needed ** ******************************************************************************/
int32_t main(void)
{
IAP_UpdateCheck(a);IAP_Init(a);IAP_Main(a); }Copy the code
Step 2: Check the data value of the BootPara marker area to determine whether the APP needs to be upgraded. If the APP needs to be upgraded, the IAP_Init() and IAP_Main() functions will be executed; otherwise, the User Application program will be directly jumped.
/ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * \ brief check BootPara marked area data values, Check whether the APP needs to be upgraded. ** ** \param None ** ** \retval None ** * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * /
void IAP_UpdateCheck(void)
{
uint32_t u32AppFlag;
u32AppFlag = *(__IO uint32_t *)BOOT_PARA_ADDRESS; // Read the value of the BootLoader para marker
if(APP_FLAG ! = u32AppFlag)// If the flag value is not equal to APP_FLAG, the APP does not need to be upgraded
{
IAP_JumpToApp(APP_ADDRESS); // Jump directly to APP}}Copy the code
Step 3: implementation of IAP_Init() function, mainly including initialization of peripheral modules and initialization of IAP communication protocol flags.
/ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * \ brief IAP initialization * * * * \ param [in] None ** ** \retval None ** ******************************************************************************/
void IAP_Init(void)
{
PreiModule_Init(a);Modem_RamInit(a); }/ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * \ brief, CPU peripheral module initialization * * * * \ param [in] None ** ** \retval None ** ******************************************************************************/
void PreiModule_Init(void)
{
HC32_SetSystemClockToRCH22_12MHz(a);HC32_InitUart(a);HC32_InitCRC(a);HC32_InitTIM(a);HC32_InitFlash(FLASH_CONFIG_FREQ_22_12MHZ);
}
/ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * \ brief, modem parameters initialization file related variables * * * * \param [out] None ** \param [in] None ** ** \retval None ** * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * /
void Modem_RamInit(void)
{
uint32_t i;
enFrameRecvStatus = FRAME_RECV_IDLE_STATUS; // The frame state is initialized to idle
for (i=0; i<FRAME_MAX_SIZE; i++)
{
u8FrameData[i] = 0; // The frame data cache is initialized to zero
}
u32FrameDataIndex = 0; // The frame cache array index is initialized to zero
}
Copy the code
Step 4: implementation of the IAP_Main() function, mainly including the User Application program update processing.
/ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * \ brief IAP APP to upgrade the main function. * * * * \param None ** ** \retval None ** ******************************************************************************/
void IAP_Main(void)
{
en_result_t enRet;
while (1)
{
enRet = Modem_Process(a);//APP update processing
if (Ok == enRet)
{
IAP_ResetConfig(a);// Reset all peripheral modules
if (Error == IAP_JumpToApp(APP_ADDRESS)) // If the jump fails
{
while(1); }}}}/ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * \ brief PC data frame analytical and * * * * \ param [in] None ** ** \retval Ok And received to jump to APP command ** \retval OperationInProgress data processing ** \retval Error communication Error ** * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * /
en_result_t Modem_Process(void)
{
uint8_t u8Cmd, u8FlashAddrValid, u8Cnt, u8Ret;
uint16_t u16DataLength, u16PageNum, u16Ret;
uint32_t u32FlashAddr, u32FlashLength, u32Temp;
if (enFrameRecvStatus == FRAME_RECV_PROC_STATUS) // There is a data frame to be processed. EnFrameRecvStatus is adjusted in serial interrupt
{
u8Cmd = u8FrameData[PACKET_CMD_INDEX]; // Get the frame instruction code
if (PACKET_CMD_TYPE_DATA == u8FrameData[PACKET_TYPE_INDEX]) // If it is a data directive
{
u8FlashAddrValid = 0u;
u32FlashAddr = u8FrameData[PACKET_ADDRESS_INDEX] + // Read the address value
(u8FrameData[PACKET_ADDRESS_INDEX + 1] < <8) +
(u8FrameData[PACKET_ADDRESS_INDEX + 2] < <16) +
(u8FrameData[PACKET_ADDRESS_INDEX + 3] < <24);
if ((u32FlashAddr >= (FLASH_BASE + BOOT_SIZE)) && (u32FlashAddr < (FLASH_BASE + FLASH_SIZE))) // If the address value is in the valid range
{
u8FlashAddrValid = 1u; // Mark the address valid}}switch (u8Cmd) // Execute according to the instruction code jump
{
case PACKET_CMD_HANDSHAKE : // Handshake frame instruction
u8FrameData[PACKET_RESULT_INDEX] = PACKET_ACK_OK; // The return status is: correct
Modem_SendFrame(&u8FrameData[0], PACKET_INSTRUCT_SEGMENT_SIZE); // Send a reply frame to the host
break;
case PACKET_CMD_ERASE_FLASH : // Erase the flash script
if((u32FlashAddr % FLASH_SECTOR_SIZE) ! =0) // If the erased address is not the header address
{
u8FlashAddrValid = 0u; // Mark address invalid
}
if (1u == u8FlashAddrValid) // If the address is valid
{
u32Temp = u8FrameData[PACKET_DATA_INDEX] + // Get the flash size to be erased
(u8FrameData[PACKET_DATA_INDEX + 1] < <8) +
(u8FrameData[PACKET_DATA_INDEX + 2] < <16) +
(u8FrameData[PACKET_DATA_INDEX + 3] < <24);
u16PageNum = FLASH_PageNumber(u32Temp); // Calculate how many pages to erase
for (u8Cnt=0; u8Cnt<u16PageNum; u8Cnt++) // Erase the specified number of sectors as needed
{
u8Ret = Flash_EraseSector(u32FlashAddr + (u8Cnt * FLASH_SECTOR_SIZE));
if(Ok ! = u8Ret)// If the erasing fails, feedback the upper computer error code
{
u8FrameData[PACKET_RESULT_INDEX] = PACKET_ACK_ERROR;
break; }}if (Ok == u8Ret) // If all erasers are successful, feedback the upper computer is successful
{
u8FrameData[PACKET_RESULT_INDEX] = PACKET_ACK_OK;
}else // If the erasing fails, feedback the upper computer error timeout flag{ u8FrameData[PACKET_RESULT_INDEX] = PACKET_ACK_TIMEOUT; }}else // The address is invalid
{
u8FrameData[PACKET_RESULT_INDEX] = PACKET_ACK_ADDR_ERROR;
}
Modem_SendFrame(&u8FrameData[0], PACKET_INSTRUCT_SEGMENT_SIZE); // Send the reply frame to the host
break;
case PACKET_CMD_APP_DOWNLOAD : // Data download instruction code
if (1u == u8FlashAddrValid) // If the address is valid
{
u16DataLength = u8FrameData[FRAME_LENGTH_INDEX] + (u8FrameData[FRAME_LENGTH_INDEX + 1] < <8)
- PACKET_INSTRUCT_SEGMENT_SIZE; // Get the length of data in the packet (not including instruction code instruction type, etc.)
if (u16DataLength > PACKET_DATA_SEGMENT_SIZE) // If the data length is greater than the maximum length
{
u16DataLength = PACKET_DATA_SEGMENT_SIZE; // Set the maximum value of data
}
u8Ret = Flash_WriteBytes(u32FlashAddr, (uint8_t *)&u8FrameData[PACKET_DATA_INDEX], u16DataLength); // Write all data to flash
if(Ok ! = u8Ret)// Write data fails
{
u8FrameData[PACKET_RESULT_INDEX] = PACKET_ACK_ERROR; // Feedback the host error flag
}
else // Write data successfully
{
u8FrameData[PACKET_RESULT_INDEX] = PACKET_ACK_OK; // Feedback the host success sign}}else // If the address is invalid
{
u8FrameData[PACKET_RESULT_INDEX] = PACKET_ACK_ADDR_ERROR; // The host address is incorrect
}
Modem_SendFrame(&u8FrameData[0], PACKET_INSTRUCT_SEGMENT_SIZE); // Send the reply frame to the host
break;
case PACKET_CMD_CRC_FLASH : // Query the Flash parity instruction
if (1u == u8FlashAddrValid) // If the address is valid
{
u32FlashLength = u8FrameData[PACKET_DATA_INDEX] +
(u8FrameData[PACKET_DATA_INDEX + 1] < <8) +
(u8FrameData[PACKET_DATA_INDEX + 2] < <16) +
(u8FrameData[PACKET_DATA_INDEX + 3] < <24); // Obtain the size of the flash to be verified
if ((u32FlashLength + u32FlashAddr) > (FLASH_BASE + FLASH_SIZE)) // If the flash length exceeds the valid range
{
u8FrameData[PACKET_RESULT_INDEX] = PACKET_ACK_FLASH_SIZE_ERROR; // The size of the flash on the host is incorrect
}else
{
u16Ret = Cal_CRC16(((unsigned char *)u32FlashAddr), u32FlashLength);// Reads the value of the flash specified area and computes the CRC value
u8FrameData[PACKET_FLASH_CRC_INDEX] = (uint8_t)u16Ret; // Store the CRC value in the reply frame
u8FrameData[PACKET_FLASH_CRC_INDEX+1] = (uint8_t)(u16Ret>>8);
u8FrameData[PACKET_RESULT_INDEX] = PACKET_ACK_OK; // Feedback the host success sign}}else // If the address is invalid
{
u8FrameData[PACKET_RESULT_INDEX] = PACKET_ACK_ADDR_ERROR; // The host address is incorrect
}
Modem_SendFrame(&u8FrameData[0], PACKET_INSTRUCT_SEGMENT_SIZE+2); // Send the reply frame to the host
break;
case PACKET_CMD_JUMP_TO_APP : // Jump to APP script
Flash_EraseSector(BOOT_PARA_ADDRESS); // Erase the BOOT parameter sector
u8FrameData[PACKET_RESULT_INDEX] = PACKET_ACK_OK; // Feedback that the host is successful
Modem_SendFrame(&u8FrameData[0], PACKET_INSTRUCT_SEGMENT_SIZE); // Send the reply frame to the host
return Ok; //APP update completed, return OK, next execute jump function, jump to APP
case PACKET_CMD_APP_UPLOAD : // Data upload
if (1u == u8FlashAddrValid) // If the address is valid
{
u32Temp = u8FrameData[PACKET_DATA_INDEX] +
(u8FrameData[PACKET_DATA_INDEX + 1] < <8) +
(u8FrameData[PACKET_DATA_INDEX + 2] < <16) +
(u8FrameData[PACKET_DATA_INDEX + 3] < <24); // Read the length of the uploaded data
if (u32Temp > PACKET_DATA_SEGMENT_SIZE) // If the data length is greater than the maximum value
{
u32Temp = PACKET_DATA_SEGMENT_SIZE; // Set the data length to the maximum
}
Flash_ReadBytes(u32FlashAddr, (uint8_t *)&u8FrameData[PACKET_DATA_INDEX], u32Temp); // Read flash data
u8FrameData[PACKET_RESULT_INDEX] = PACKET_ACK_OK; // Feedback the host success sign
Modem_SendFrame(&u8FrameData[0], PACKET_INSTRUCT_SEGMENT_SIZE + u32Temp);// Send the reply frame to the host
}
else // If the address is invalid
{
u8FrameData[PACKET_RESULT_INDEX] = PACKET_ACK_ADDR_ERROR; // Feedback the host address error flag
Modem_SendFrame(&u8FrameData[0], PACKET_INSTRUCT_SEGMENT_SIZE); // Send the reply frame to the host
}
break;
case PACKET_CMD_START_UPDATE : // Start APP update (this command is normally called in APP)
u8FrameData[PACKET_RESULT_INDEX] = PACKET_ACK_OK; // Feedback the host success sign
Modem_SendFrame(&u8FrameData[0], PACKET_INSTRUCT_SEGMENT_SIZE); // Send the reply frame to the host
break;
}
enFrameRecvStatus = FRAME_RECV_IDLE_STATUS; // The frame data processing is complete, and the frame receiving state is restored to idle state
}
return OperationInProgress; // Return, APP update...
}
Copy the code
4.2 User Application program design
In this example User Application, the flag that triggers the BootLoader update is implemented in the serial port receiver.
//UART0 interrupt function
void Uart0_IRQHandler(void)
{
if(Uart_GetStatus(M0P_UART0, UartRC)) //UART0 data receive
{
Uart_ClrStatus(M0P_UART0, UartRC); // Clear the interrupt status bit
u8RxData[u8RxCnt] = Uart_ReceiveData(M0P_UART0); // Receive data bytes
u8RxCnt++;
if(u8RxCnt>=18)
{
u8RxCnt = 0;
if ((u8RxData[0] = =0x6D)&&(u8RxData[1] = =0xAC)&&(u8RxData[6] = =0x26)&&(u8RxData[16] = =0xA6)&&(u8RxData[17] = =0xDA)) // Is the APP update frame
{
for(uint32_t i=0; i<18; i++) {Uart_SendDataPoll(M0P_UART0,u8TxData[i]); // Send data in query mode
}
// The boot para field writes the flag value to inform the BootLoader that the program needs to be updated
Flash_SectorErase(0xF00);
Flash_WriteWord(0xF00.0x12345678);
NVIC_SystemReset(a);// Reset the MCU}}}if(Uart_GetStatus(M0P_UART0, UartTC)) //UART0 sends data
{
Uart_ClrStatus(M0P_UART0, UartTC); // Clear the interrupt status bit}}Copy the code
4.3 IAR address configuration and file output
Finally, you need to briefly configure the IAR environment.
Step 1: Determine the Linker configuration address for the output, because this is where the program changes the address.
Step 2: Find the Linker configuration file and change the BootLoader address: 0x00000000~0x00000DFF and User Application address: 0x00001000~0x0000FFFF.
Step 3: Find the User Application configuration file (file with suffix.s) and add the interrupt vector offset length: 0x00001000. There are two differences from the BootLoader configuration file, as shown below:
Step 4: After the two programs are burned in ICP mode (SWD, JTAG, etc.), the HEX file program or BIN file program can be burned in IAP mode through serial port. Output and burn HEX file program or BIN file program as shown below:
5. Expand: Parse HEX files
HEX files can be opened using UltraEdit, Notepad++, notepad and other tools. After opening the HEX file with Notepad++, you will see the following data:
When opened with Notepad++, data with different meanings will have different colors. Each line of data begins with a colon, followed by data length, address, identifier, valid data, parity data, and so on. The first behavior in the above figure is analyzed:
The first byte, 10, indicates that the line has 0x10 data, or 16 bytes of data;
The second and third bytes, 3E00, indicate that the start address of the line is 0x3E00.
The fourth byte, 00, indicates that this line records data;
Bytes 5-20, representing valid data;
The 21st byte EB represents the checksum of the previous data. The checksum method is 0x100- summation of the previous bytes.
Where, the fourth byte has five types: 00-05, meaning as follows:
field | meaning |
---|---|
00 | Indicates that data is recorded |
01 | End of file |
02 | Indicates the extended segment address |
03 | Indicates the start segment address |
04 | Represents extended linear addresses |
05 | Represents the starting linear address |
Single-chip HEX files with 00 in the majority, are used to represent data. The end of the HEX file looks like this:
The 01 in the last line indicates the end of the file, and the FF in the last line indicates the parity data obtained from 0x100-0x01=0xFF.
Implementation of STM32+IAP scheme in IAR environment