3843|3

36

帖子

1

TA的资源

一粒金砂(中级)

楼主
 

极海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备份的方式来升级。

        接着,我们来说一说本升级程序的原理。首先,需要对App程序区(0x08004000-0807FFFF)进行擦除(Flash特性是擦除后,才能写入新字节);擦除完成后,通过串口每接收1K App数据后,写入对应的Flash地址中,写入成功后,Flash地址自增,如此往复,直到上位机完全发送完数据(协议控制)。

        下面是程序内容,从ST官方程序中复制Ymodem所需文件并添加到工程中,并对其中的commom.c、flash_if.c、menu.c做相应的修改,程序片段如下。

       以下是 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进行更改的。

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

        以下是 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)))。

         测试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

 

        测试视频

WeChat_20220921224952

 

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

本程序仅供测试。

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

 

最新回复

看了一下移植的过程,非常不错! 非常棒,我这边用的F407,测试一下看看能不能移植过来。   详情 回复 发表于 2024-7-3 07:21
点赞 关注(1)
 
 

回复
举报

2

帖子

0

TA的资源

一粒金砂(初级)

沙发
 

非常棒,我这边用的F407,测试一下看看能不能移植过来

 
 
 

回复

3

帖子

0

TA的资源

一粒金砂(初级)

板凳
 
非常棒,我这边用的F103,测试一下看看能不能移植过来
 
 
 

回复

6968

帖子

11

TA的资源

版主

4
 

看了一下移植的过程,非常不错!

非常棒,我这边用的F407,测试一下看看能不能移植过来。

 
 
 

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

随便看看
查找数据手册?

EEWorld Datasheet 技术支持

相关文章 更多>>
推荐帖子
半导体供应商竞争焦点---下一代IP业务

宽带网络的广泛部署和VoIP应用的普及相辅相成。除了将企业应用作为主流市场外,VoIP还在政策缺失、运营商封杀的不利情况下逐渐向 ...

诺基亚N92(DVB-H手机).pdf

诺基亚N92(DVB-H手机).pdf

[DIY]二十四小时的感动(古典版I) 自制QS30-1辉光管电子时钟【附百张图】

辉光管是电子管的一种,属于很古老的数码显示器件,在没有LED的年代,显示数字和符号就靠它。这种管子现在应该算古董了,这次DI ...

MSP430单片机的开关电源

作者:Freedomz 1 引 言 MSP430系列单片机是美国TI公司生产的新一代16位单片机,是一种超低功耗的混合信号处理器(MixedSigna ...

基于msp430的触摸键盘到啦!!!!!拆箱焊板!

这是前几天画的基于MSP430G2553触摸键盘的板子,板子上共有20个触摸按键,通过I2C与外界通信,另外还有一个按键中断引脚,一个 ...

宝马冷却系统及电动冷却液泵部件(电子水泵)功能特性及标准

464772 宝马发动机的冷却系统由冷却液冷却系统和发动机机油冷却系统组成。根据宝马车辆规格,使用不同类型的发动机机油冷却系 ...

关于TPS5405的电路元件

TPS5405的手册有电路图,却没写元件型号,这是为什么?遇到这类芯片,应该怎么知道对应的元件型号? 538656

请问 MTK 2625 和 STM32 在硬件上相比,是不是除了基本的硬件之外,只多了基带部分

请问 (1) MTK 2625 和 STM32 在硬件上相比,是不是除了基本的硬件之外,只多了基带部分的功能 (2)移远的BC26 相 ...

传感器通过蓝牙链接 然后读出mpu的数据

545979545980

HooRii Console 开启内测报名,和众科技助力 Matter 开发者

近日,HooRii Technology(和众科技)发布上线综合型开发者平台 HooRii Console ,即日起对外接受内测申请。通过 HooRii Conso ...

关闭
站长推荐上一条 1/8 下一条

 
EEWorld订阅号

 
EEWorld服务号

 
汽车开发圈

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

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

北京市海淀区中关村大街18号B座15层1530室 电话:(010)82350740 邮编:100190

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