This post was last edited by Zachary_yo on 2022-9-21 22:58
This issue shares the serial port IAP online upgrade program (Bootloader) of APM32E103VET5, which uses the Ymodem protocol. This protocol is derived from the official ST program. Due to the different sizes of Flash pages or sectors (such as sectors for the F4 series and page erase for the F1 series), changes have been made. The following is the Flash storage structure of APM32E103VET6, as shown in the figure below. Each page is 2K, with a total of 256 pages, totaling 512K. We use the first 8 pages, that is, the first 16K (0x08000000-0x08003FFF) to store the Bootloader program, and the remaining Flash space is used to store the App program. Here I only share the online upgrade method of Bootloader+App. In addition to the first time, in order to prevent sudden failures during the upgrade process, you can also use the Bootloader+App+App backup method to upgrade.
Next, let's talk about the principle of this upgrade program. First, the App program area (0x08004000-0807FFFF) needs to be erased (the Flash feature is that new bytes can be written only after erasing); after the erasing is completed, each 1K App data received through the serial port is written to the corresponding Flash address. After the write is successful, the Flash address increments automatically, and this cycle repeats until the host computer has completely sent the data (protocol control).
The following is the program content. Copy the files required for Ymodem from the ST official program and add them to the project. Modify commom.c, flash_if.c, and menu.c accordingly. The program fragment is as follows.
The following is the content of the common.c file. Only the content of the uint32_t SerialKeyPressed( uint8_t *key ) function is modified. The function is to query the serial port to receive data.
/*
*********************************************************************************************************
* 函 数 名: SerialKeyPressed
* 功能说明: 获取整数输入
* 形 参: key 按键
* 返 回 值: 1 返回数据 2 没有返回
*********************************************************************************************************
*/
uint32_t SerialKeyPressed( uint8_t *key )
{
if ( USART_ReadStatusFlag( IAP_COM, USART_FLAG_RXBNE ) != RESET )
{
//*key = (uint8_t)USART_DATA(IAP_COM);
*key = USART_RxData( IAP_COM );
// USART_ClearStatusFlag( IAP_COM, USART_FLAG_RXBNE );
return 1;
}
else
{
return 0;
}
}
The following is the content of the flash_if.c file. In this file, all the functions mentioned above need to be modified. The modified content needs to be changed according to the Flash storage structure of different MCUs. The following changes are made according to APM32E103VET6.
/**************************************************************
* [url=home.php?mod=space&uid=32621]@name[/url] FLASH_If_Init
* [url=home.php?mod=space&uid=159083]@brief[/url] * @param None
* @retval
* [url=home.php?mod=space&uid=1315547]@author[/url] Zachary
* [url=home.php?mod=space&uid=34591]@data[/url] 2022-09-01
**************************************************************/
void FLASH_If_Init( void )
{
FMC_Unlock();
FMC_ClearStatusFlag( FMC_FLAG_OC );
FMC_ClearStatusFlag( FMC_FLAG_PE );
FMC_ClearStatusFlag( FMC_FLAG_WPE );
}
/**************************************************************
* @Name FLASH_If_Erase
* @brief
* @param StartSector: [输入/出]
* @retval
* @author Zachary
* @Data 2022-09-01
**************************************************************/
uint32_t FLASH_If_Erase( uint32_t StartSector )
{
uint32_t UserStartSector = 0, i = 0, EndSector = 0,pageaddr = 0x08000000;
EndSector = 255;
/* 用户flash区所在的扇区 */
UserStartSector = GetPage( StartSector );
pageaddr = pageaddr + ( UserStartSector * 0x800 );
for( i = UserStartSector; i <= EndSector; i++ )
{
if ( FMC_ErasePage( pageaddr ) != FMC_STATUS_COMPLETE )
{
return ( 1 );
}
FMC_ClearStatusFlag( FMC_FLAG_OC );
FMC_ClearStatusFlag( FMC_FLAG_PE );
FMC_ClearStatusFlag( FMC_FLAG_WPE );
pageaddr += 0x800;
}
return ( 0 );
}
/**************************************************************
* @Name FLASH_If_Write
* @brief
* @param FlashAddress: [输入/出]
** Data: [输入/出]
** DataLength: [输入/出]
* @retval
* @author Zachary
* @Data 2022-05-22
**************************************************************/
uint32_t FLASH_If_Write( __IO uint32_t* FlashAddress, uint32_t* Data, uint32_t DataLength )
{
uint32_t i = 0;
for ( i = 0; ( i < DataLength ) && ( *FlashAddress <= ( USER_FLASH_END_ADDRESS - 4 ) ); i++ )
{
/* Device voltage range supposed to be [2.7V to 3.6V], the operation will
be done by word */
if ( FMC_ProgramWord( *FlashAddress, *( uint32_t* )( Data + i ) ) == FMC_STATUS_COMPLETE )
{
/* 检查写入的数据 */
if ( *( uint32_t* )*FlashAddress != *( uint32_t* )( Data + i ) )
{
/* 读出的和写入的不相同 */
return( 2 );
}
/* 地址递增 */
*FlashAddress += 4;
}
else
{
return ( 1 );
}
}
return ( 0 );
}
/**************************************************************
* @Name FLASH_If_GetWriteProtectionStatus
* @brief
* @param None
* @retval
* @author Zachary
* @Data 2022-09-01
**************************************************************/
uint16_t FLASH_If_GetWriteProtectionStatus( void )
{
// uint32_t UserStartSector = 0;
// UserStartSector = GetPage( APPLICATION_ADDRESS );
// if ( FMC_GetReadProtectionStatus() != RESET )
// {
return 1;
// }
// else
// {
// return 0;
// }
}
/**************************************************************
* @Name FLASH_If_DisableWriteProtection
* @brief
* @param None
* @retval
* @author Zachary
* @Data 2022-09-01
**************************************************************/
uint32_t FLASH_If_DisableWriteProtection( void )
{
FMC_Unlock();
FMC_DisableReadOutProtection();
return ( 1 );
}
/**************************************************************
* @Name GetSector
* @brief
* @param Address: [输入/出]
* @retval
* @author Zachary
* @Data 2022-09-01
**************************************************************/
static uint32_t GetPage( uint32_t Address )
{
uint32_t sector = 0;
sector = ( Address - 0x08000000 ) / 0x800;
return sector;
}
The following is the content of the menu.c file. In this file, only the Main_Menu() function is changed to change the way the program jumps from the Bootloader to the App.
void Main_Menu( void )
{
uint8_t key = 0;
/* Test if any sector of Flash memory where user application will be loaded is write protected */
if ( FLASH_If_GetWriteProtectionStatus() == 0 )
{
FlashProtection = 1;
}
else
{
FlashProtection = 0;
}
while ( 1 )
{
SerialPutString( "\r\n================== Bootloader Menu =======================\r\n\n" );
SerialPutString( " Download Image To the GD32F470 Internal Flash -------- 1\r\n\n" );
SerialPutString( " Upload Image From the GD32F470 Internal Flash -------- 2\r\n\n" );
SerialPutString( " Execute The New Program ------------------------------ 3\r\n\n" );
if( FlashProtection != 0 )
{
SerialPutString( " Disable the write protection(Not Used) --------------- 4\r\n\n" );
}
SerialPutString( "==========================================================\r\n\n" );
/* Receive key */
key = GetKey();
if ( key == 0x31 )
{
/* Download user application in the Flash */
SerialDownload();
}
else if ( key == 0x32 )
{
//SerialUpload();
}
else if ( key == 0x33 )
{
if ( 0x20000000 == ( ( *( __IO uint32_t* )APPLICATION_ADDRESS ) & 0x2FFE0000 ) )
{
SysTick->VAL = 0;
SysTick->LOAD = 0;
SysTick->CTRL = 0;
USART_Disable( IAP_COM );
JumpAddress = *( __IO uint32_t* ) ( APPLICATION_ADDRESS + 4 );
/* Jump to user application */
Jump_To_Application = ( pFunction ) JumpAddress;
/* Initialize user application's Stack Pointer */
__set_MSP( *( __IO uint32_t* ) APPLICATION_ADDRESS );
Jump_To_Application();
}
}
else if ( ( key == 0x34 ) && ( FlashProtection == 1 ) )
{
/* Disable the write protection */
switch ( FLASH_If_DisableWriteProtection() )
{
case 1:
{
SerialPutString( "Write Protection disabled...\r\n" );
FlashProtection = 0;
break;
}
case 2:
{
SerialPutString( "Error: Flash write unprotection failed...\r\n" );
break;
}
default:
{
}
}
}
else
{
if ( FlashProtection == 0 )
{
SerialPutString( "Invalid Number ! ==> The number should be either 1, 2 or 3\r" );
}
else
{
SerialPutString( "Invalid Number ! ==> The number should be either 1, 2, 3 or 4\r" );
}
}
}
}
The following are the important contents in main.c. When the microcontroller is powered on, it starts with the Bootloader. In the main program, it is determined whether it needs to be upgraded or jump to the App to run the program. In the main program, UpgradeFlag is used to determine whether an online upgrade is required. If no upgrade is required, check whether there is a program in the App area. If not, perform an online upgrade. The upgrade flag UpgradeFlag is defined as uint16_t UpgradeFlag __attribute__ ((at(0x2001FC00))). And the RAM space after 0x2001FC00 is set to uninitialized. The purpose is to reset the microcontroller software and not lose data, so that it is convenient to jump from the App flag to the Bootloader for upgrading.
int main( void )
{
__disable_irq();
if( UpgradeFlag == 0x1234 )
{
UpgradeFlag = 0x0000;
FLASH_If_Init();
SysInit();
goto __IAP_START;
}
if ( 0x20000000 == ( ( *( __IO uint32_t * )APPLICATION_ADDRESS ) & 0x2FFE0000 ) )
{
SysTick->CTRL = 0;
SysTick->LOAD = 0;
SysTick->VAL = 0;
USART_Disable( USART1 );
JumpAddress = *( __IO uint32_t* ) ( APPLICATION_ADDRESS + 4 );
Jump_To_Application = ( pFunction ) JumpAddress;
__set_MSP( *( __IO uint32_t* ) APPLICATION_ADDRESS );
//__set_CONTROL(0);
Jump_To_Application();
}
else
{
FLASH_If_Init(); /* 初始化flash操作 */
SysInit();
goto __IAP_START;
}
__IAP_START:
Main_Menu();
while( 1 )
{
// GPIOB->ODATA_B.ODATA9 ^= 1;
// delay_ms( 1000 );
}
}
The following are the settings of the UpgradeFlag definition area in the Bootloader program and the App program, uint16_t UpgradeFlag __attribute__ ((at(0x2001FC00))).
Test the App program code. When 0x31 ('1') is received, the flag is set and the software is reset. After the reset, the Bootloader detects the UpgradeFlag flag and enters the upgrade program.
/**************************************************************
* @Name main
* @brief
* @param None
* @retval
* @author Zachary
* @Data 2022-09-13
**************************************************************/
int main( void )
{
SCB->VTOR |= 0x4000;
NVIC_ConfigPriorityGroup( NVIC_PRIORITY_GROUP_4 );
SysInit();
while( 1 )
{
if( comGetChar( COM1, &rxdata ) )
{
if( rxdata == 0x31 )
{
UpgradeFlag = 0x1234;
__disable_irq();
NVIC_SystemReset();
}
}
GPIOB->ODATA_B.ODATA9 ^= 1;
printf( "hello world\r\n" );
delay_ms( 1000 );
}
}
The Bin file is generated as follows: input C:\Keil_v5\ARM\ARMCC\bin\fromelf.exe --bin --output =BIN\@L.bin !L
Test Video
WeChat_20220921224952
Finally, the program code of App and Bootloader is attached. It has been tested using APM32E103VET6 MINIBOARD development board. If you have any questions, please leave a message.
This program is for testing purposes only.
|