296|0

14

帖子

0

资源

一粒金砂(中级)

极海APM32E103VET6测评之IAP升级 [复制链接]

本帖最后由 Zachary_yo 于 2022-9-21 22:58 编辑

        本期分享APM32E103VET5的串口 IAP 在线升级程序(Bootloader),使用了Ymodem协议,本协议源自ST官方程序。因Flash页或扇区大小的不同(如F4系列是扇区,F1系列是页擦除),对其进行了更改。下面是APM32E103VET6的Flash存储结构,如下图所示,每页2K,共256页,合计512K。我们对其中的前8页,也就是前16K(0x08000000-0x08003FFF)用于存放Bootloader程序,后面的Flash空间用于存放App程序。在这里我仅分享Bootloader+App的在线升级方式,初次之外,为了防止升级过程中的突发失败,还可以采用Bootloader+App+App备份的方式来升级。
image.png         接着,我们来说一说本升级程序的原理。首先,需要对App程序区(0x08004000-0807FFFF)进行擦除(Flash特性是擦除后,才能写入新字节);擦除完成后,通过串口每接收1K App数据后,写入对应的Flash地址中,写入成功后,Flash地址自增,如此往复,直到上位机完全发送完数据(协议控制)。

        下面是程序内容,从ST官方程序中复制Ymodem所需文件并添加到工程中,并对其中的commom.c、flash_if.c、menu.c做相应的修改,程序片段如下。 image.png        以下是 common.c文件内容,只修改uint32_t SerialKeyPressed( uint8_t *key )函数中内容,该函数的作用是串口查询接收数据。

/*
*********************************************************************************************************
*	函 数 名: 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;
    }
}

         以下是 flash_if.c文件内容,该文件中,进本上所有函数都要做修改,修改内容需要根据不同单片机Flash存储结构进行更改,以下是根据APM32E103VET6进行更改的。

/**************************************************************
  * @name  FLASH_If_Init
  * @brief  * @param   None
  * @retval
  * @author  Zachary
  * @data  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;
}

        以下是 menu.c文件内容,该文件中,只更改Main_Menu()函数,更改程序从Bootloader跳转到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" );
            }
        }
    }
}

          下面是main.c中的重要内容,单片机上电首先是从Bootloader开始的,再主程序中,判断是需要进行升级还是跳转到App中运行程序。main程序中,使用UpgradeFlag来判断是否需要进行在线升级。如果不需要升级,则检查App区中是否有程序,如果没有,则进行在线升级。升级标志UpgradeFlag我们定义如下uint16_t UpgradeFlag __attribute__ ((at(0x2001FC00)))。并且将0x2001FC00后的RAM空间设置为不初始化,作用是使单片机软件复位,数据不丢失,方便从App设置标志位跳转到Bootloader进行升级。

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 );
    }
}

        以下是Bootloader程序和App程序中UpgradeFlag定义区域的设置,uint16_t UpgradeFlag __attribute__ ((at(0x2001FC00)))。

image.png          测试App程序代码,当接收到0x31('1')时,进行设置标志位,并软件复位,复位后Bootloader检测到UpgradeFlag标志后,进入升级程序。

/**************************************************************
  * @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 );
    }
}

       Bin文件生成方式如下,输入C:\Keil_v5\ARM\ARMCC\bin\fromelf.exe --bin --output =BIN\@L.bin !L

image.png  

        测试视频

WeChat_20220921224952

 

        最后附上App和Bootloader的程序代码,使用APM32E103VET6 MINIBOARD开发板测试通过,如有问题,请留言。

本程序仅供测试。

3_APM32E103VET6_IAP.7z (606.1 KB, 下载次数: 0, 售价: 10 分芯积分)

回复
您需要登录后才可以回帖 登录 | 注册

相关帖子
查找数据手册?

EEWorld Datasheet 技术支持

相关文章 更多>>
    关闭
    站长推荐上一条 1/10 下一条

    About Us 关于我们 客户服务 联系方式 器件索引 网站地图 最新更新 手机版

    站点相关: 安防电子 汽车电子 手机便携 工业控制 家用电子 医疗电子 测试测量 网络通信 物联网

    北京市海淀区知春路23号集成电路设计园量子银座1305 电话:(010)82350740 邮编:100191

    电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2022 EEWORLD.com.cn, Inc. All rights reserved
    快速回复 返回顶部 返回列表