RISC-V架构单片机GD32VF103:IAP功能

        自从传闻ARM要出售给美国公司,本人内心的不安全感促使我寻找ARM架构单片机的替代产品。网上问了些网友,有推荐GD32VF103,于是花了些时间了解这个系列的MCU,发现其实也还行,这个MCU跟GD32F103很像,RAM, ROM,外设,封装都差不多。虽说开发环境有些不太熟,不过花些心思,也能使用,至少,比51单片机好用多了。

因为官方Firmware没有IAP方面的内容,所有这篇文章专门讲讲此单片机如何进行IAP,毕竟实际工作应用中会经常使用。

一.开发环境。

我使用的IDE是Nuclei Studio IDE,这个是芯莱科技基于 MCU Eclipse IDE 开发的一款针对芯来公司处理器核产品的集成开发环境工具。GD32VF103也是这家这家公司联合兆易创新推出的。

IDE下载地址为:https://www.nucleisys.com/download.php 

我使用的开发板是一款只需要30几块钱的带彩屏的开发板,非常小,MCU型号是GD32VF103CBT6。开发板型号叫longan nano

,资料下载地址为:https://dl.sipeed.com/LONGAN/Nano/

 

二.原理

回顾并参考STM32F1系列的IAP过程:

1.接收APP镜像数据,并将其写入对应Flash地址。

2.设置SP(堆栈指针)

3.设置PC(也就是跳转至APP特定地址)。

4.APP本身需要设置中断入口的地址偏移。

由于STM32F1XX复位后,硬件自动从特定地址获取SP和PC,所有启动文件不会再次设置SP。所有,当我们用C语言写IAP程序,反而需要模拟此过程,需要设置SP。

对于GD32VF103,复位后硬件本身并不会进行类似操作。也就是说,设置SP本身是启动文件必须的功能,无需C程序再次设置。其实不止SP,还有GP等。可以查看启动文件(一般命名为startup_xxx.s)和链接文件(.ld)了解其过程。

 

三.代码实现

1.IAP

由于我买的开发板有个TF卡座,所有我通过TF进行IAP。

#ifndef __IAP_H
#define __IAP_H

#define FMC_PAGE_SIZE     0x400
#define APP_STARTADDR    0x08010000
#define APPPATH_STR      "0:/SYS/APP.BIN"


uint8_t iap_all(void);

#endif  
#include "nuclei_sdk_soc.h"
#include "iap.h"
#include "lcd.h"
#include "./fatfs/ff.h"            /* Declarations of FatFs API */
#include <stdio.h>
#include <string.h>

//保存读到的文件数据
uint32_t filebuf[FMC_PAGE_SIZE/2];

/*!
    \brief    erase fmc pages in addr 
    \param    addr : fmc addr
    \retval   none
*/
void fmc_erase_onepage(uint32_t addr )
{
    uint32_t erase_counter;

    // unlock the flash program/erase controller 
    fmc_unlock();

    // clear all pending flags 
    fmc_flag_clear(FMC_FLAG_END);
    fmc_flag_clear(FMC_FLAG_WPERR);
    fmc_flag_clear(FMC_FLAG_PGERR);

    // erase the flash page 

    fmc_page_erase( addr );
    fmc_flag_clear(FMC_FLAG_END);
    fmc_flag_clear(FMC_FLAG_WPERR);
    fmc_flag_clear(FMC_FLAG_PGERR);
    
    // lock the main FMC after the erase operation 
    fmc_lock();
}

/*!
    \brief      program for one page
    \param      addr
    \param         buf
    \retval     none
*/
void fmc_program_onepage( uint32_t addr , uint32_t *buf )
{
    uint16_t i;
    
    for( i=0; i<FMC_PAGE_SIZE; i+=4 )
    {
        if( *(uint32_t*)(addr+i) != 0xFFFFFFFF )
        {
            break;
        }
    }
    
    if( i != FMC_PAGE_SIZE )
    {
        fmc_erase_onepage(  addr ) ;
    }
    
    // unlock the flash program/erase controller 
    fmc_unlock();

    //adjust addr
    addr-=(addr%FMC_PAGE_SIZE);

    // program flash 
    for( i=0; i<FMC_PAGE_SIZE; i+=4 )
    {
        fmc_word_program( addr+i, buf[i/4] );
        fmc_flag_clear(FMC_FLAG_END);
        fmc_flag_clear(FMC_FLAG_WPERR);
        fmc_flag_clear(FMC_FLAG_PGERR);
    }

    // lock the main FMC after the program operation
    fmc_lock();
}


/*!
    \brief        对比内容是否相同
    \param        appxaddr:flash地址
    \param        data:内存地址
    \param        size:要比较的大小
    \retval        1:完全相同;0:不一致
*/
uint8_t iap_cmp(uint32_t appxaddr,uint32_t *data,uint32_t size)
{
    uint32_t i;
    if(size==0)return 1;
    
    for(i=0;i<size;i+=4)
    {    
        if( *(uint32_t*)(appxaddr+i)  != data[i/4] )
        {
            return 0;
        }
    } 
    return 1 ;

}


/*!
    \brief      过程:读文件,判断版本,写Flash,检查是否一致,显示进度,判断完整性,显示结果。
    \param      none
    \retval     0:成功 ;Other:没有成功
*/
uint8_t iap_all(void)
{    
    FIL appfile;
    FILINFO fileinfo;
    uint8_t res = 0 ;
    char * ptr = (char *)filebuf;
    

    printf( "open appfile:" );
    
    if( f_open( &appfile,APPPATH_STR ,FA_READ ) == FR_OK )
    {    
        uint16_t  i=0;
        uint32_t  tlen=0;
        UINT  br=0;
        
        f_stat( (char*) APPPATH_STR ,&fileinfo );

        printf("ok,file size :%d\r\n",fileinfo.fsize);
        LCD_ShowString(0,8,"App Upd:",RED);
        LCD_ShowString(2,8+16,"Prog:   %",RED);
        
        LCD_ShowNum( 0,16*4, fileinfo.fsize  ,8,BLACK) ;

        printf("\r\nRead appfile data:\r\n");
        res=0;

        if(  fileinfo.fsize >= FMC_PAGE_SIZE )
        {
            for( i=0; i<(fileinfo.fsize/FMC_PAGE_SIZE); i++ )
            {
                memset(ptr,0xff,FMC_PAGE_SIZE);

                res = f_read( &appfile, ptr , FMC_PAGE_SIZE/2 ,&br);
                if(  res   )
                {
                    printf("appfile read error\r\n");
                    res = 0x02 ;
                    break;
                }else
                {
                    tlen+=br;

                    if( i==0)
                    {   
                        //把版本号放在APP_STARTADDR+4,这里比较,如果相同则不更新APP
                        if( filebuf[1] == *(uint32_t*)(APP_STARTADDR+4) )
                        {
                            res |= 0x10;
                            break;
                        }
                        //LCD_ShowNum( 8*9,8  , filebuf[1] ,8,RED) ;
                    }

                    res = f_read( &appfile, ptr+512 , FMC_PAGE_SIZE/2 ,&br);
                    tlen+=br;


                    fmc_program_onepage( APP_STARTADDR+i*FMC_PAGE_SIZE, filebuf );

                    if ( iap_cmp(APP_STARTADDR+i*FMC_PAGE_SIZE , filebuf ,br ) )
                    {
                        printf( "iap prog:%d%%\r\n", (uint32_t)(i*100/(fileinfo.fsize/FMC_PAGE_SIZE) +0.5 ) );
                        LCD_ShowNum( 2 +8*5,8+16  , (uint32_t)(i*100/(fileinfo.fsize/FMC_PAGE_SIZE) )+0.5 ,3,RED) ;
                    } 
                    else 
                    {

                        res = 0x03;
                        printf("cmp err\r\n" );
                        break;
                    }
                }
            }
        }

        //最后不够FMC_PAGE_SIZE的部分
        if( res==0 && fileinfo.fsize%FMC_PAGE_SIZE)
        {
            memset( ptr,0xff,FMC_PAGE_SIZE);

            res = f_read( &appfile, ptr , FMC_PAGE_SIZE/2 ,&br);
            if(  res   )
            {
                printf("appfile read error\r\n");
                res = 0x02 ;

            }else
            {

                if( i==0 )
                {
                    //把版本号放在APP_STARTADDR+4,这里比较,如果相同则不更新APP
                    if( filebuf[1] == *(uint32_t*)(APP_STARTADDR+4) )
                    {
                        res |= 0x10;
                    }
                }

                if( !res )
                {
                    tlen+=br;
                    res = f_read( &appfile, ptr+512 , FMC_PAGE_SIZE/2 ,&br);

                    tlen+=br;
                    LCD_ShowNum( 2 +8*5,8+16  , (uint32_t)(i*100/(fileinfo.fsize/FMC_PAGE_SIZE) ) ,3,RED) ;

                    fmc_program_onepage( APP_STARTADDR+i*FMC_PAGE_SIZE, filebuf );

                    if ( iap_cmp(APP_STARTADDR+i*FMC_PAGE_SIZE , filebuf ,br ) )
                    {
                        printf( "iap prog:%d%%\r\n", (uint32_t)(i*100/(fileinfo.fsize/FMC_PAGE_SIZE) +0.5 ) );
                        LCD_ShowNum( 2 +8*5,8+16  , (uint32_t)(i*100/(fileinfo.fsize/FMC_PAGE_SIZE) )+0.5 ,3,RED) ;
                    }
                    else
                    {

                        res = 0x03;
                        printf("cmp err\r\n" );

                    }
                }
            }
        }

        printf( "\r\nreaded data size:%d\r\n",tlen );

        f_close(&appfile); 
        
        LCD_ShowNum( 8*10,8  ,  tlen ,8,BLACK) ;

        if( res==0 && tlen == fileinfo.fsize )
        { 
            //res = f_unlink(APPPATH_STR);  //del file

            LCD_ShowString(0,16*3,"iap ok!",BLUE);
            printf("IAP ok,res:%d\r\n",res );
            
        }else if( res&0x10 )
        {
             res=0;
             printf("version same\r\n" );
             LCD_ShowString(0,16*3,"version same!",BLUE);
        }else
        {
            res |= 0x80;
            printf("iap fail\r\n" );
            LCD_ShowString(0,16*3,"iap fail!",BLUE);
        }

    }else
    {
        res = 0x01 ;
        printf("No appfile\r\n" );
    }
    return res;
}

上面会进行文件版本判断。文件版本放在镜像文件的第二个“字”,可以直接在启动文件里设置。

2.APP

写一个待验证app程序,例如定时器控制流水灯什么的,最好带中断,以便验证。

注意一点,需要在链接脚本文件里面修改起始地址。

/********************* Flash Configuration ************************************
 * <h> Flash Configuration
 * <o0> Flash Base Address <0x0-0xFFFFFFFF:8>
 * <o1> Flash Size (in Bytes) <0x0-0xFFFFFFFF:8>
 * </h>
 */
__ROM_BASE = 0x08000000;
__ROM_SIZE = 0x00020000;

修改为

/********************* Flash Configuration ************************************
 * <h> Flash Configuration
 * <o0> Flash Base Address <0x0-0xFFFFFFFF:8>
 * <o1> Flash Size (in Bytes) <0x0-0xFFFFFFFF:8>
 * </h>
 */
__ROM_BASE = 0x08010000;
__ROM_SIZE = 0x00010000;

注意要保证__ROM_BASE不能小于IAP程序代码大小,并且剩余flash空间足够放得下APP程序代码。

跳转指令:

 ((void(*)())(0x08010000))();

保险起见,跳转前关全局中断。

__disable_irq();

下图为测试图:IAP成功后跳转至APP。

GD32VF103 IAP测试

 

评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值