GD32f103C8T6 IAP 升级教程

参考stm32的IAP升级原理

IAP测试源码

GD32和stm32的内核都是一样的,又因为IAP升级主要涉及升级的路径之和内核先关,所以gd32和stm32升级IAP升级是一样的。

0.升级原理

GD32的内部(FLASH)地址起始于0x08000000,一般情况下,程序就从此地址开始写入,此外GD32是基于Cortex-M3内核的微控制器,其内部通过一张“中断向量表”来响应中断,程序启动后,将首先从“中断向量表”取出复位中断向量执行复位中断程序完成启动,而这张“中断向量表”的起始地址是0x08000004,当中断来临,GD32的内部硬件机制亦会自动将PC指针定位到“中断向量表”处,并根据中断源取出对应的中断向量执行中断服务程序。在上图中,GD32在复位后,先从0x08000004地址取出复位中断向量的地址,并跳转到复位中断服务程序,如图标号①所示:在复位中断服务程序执行完之后,会跳转到我们的main函数,如图标号②所示;而我们的main函数一般都是一个死循环,在main函数执行过程中,如果收到中断请求(发生中断),此时GD32强制将PC指针指向中断向量表处,如图标号③所示;然后根据中断源进入相应的中断服务程序,如图标号④所示;在执行完中断服务程序以后,程序再次返回main函数执行,如图⑤所示。

当加入IAP程序之后,程序运行流程如图下所示:

在上面所示流程图中,GD32复位后,还是从0x08000004地址取出复位中断向量的地址并跳转到复位中断服务程序,在运行完复位中断服务程序之后跳转到IAP的main函数,如图标号①所示;在执行IAP以后(即将新的APP代码写入GD32的FLASH,灰底部分。新程序的复位中断向量起始地址0x08000004+N+M),跳转至新写入程序的复位向量表,取出新程序的复位中断向量的地址,并跳转执行新程序的复位中断服务程序,随后跳转至新程序的main函数,如图标号②和③所示,同样main函数为一个死循环,并且注意到此时GD32的FLASH,在不同位置上,共有两个中断向量表。在main函数执行过程中,如果CPU得到一个中断请求,PC指针仍强制跳转到地址0x08000004中断向量处,而不是新程序的中断向量处,如图④所示;程序再根据我们设置的中断向量表偏移量,跳转到对应中断源新的中断服务程序中,如图标号⑤所示;在执行完中断服务程序后,程序返回main函数继续运行,如标号⑥所示

1.升级条件/环境

gd32f103c8t6 ROM 地址范围ROM 地址范围: 0x800 0000~0x800 FFFF 合计64K

扇区大小 1024Byte

falsh 空间分配设置

boot loader 分配30K空间 flash 0x8000000-0x80077ff
user bin 分配34k空间 flash 0x8007800-0x800ffff

2.bin程序

app程序功能是 LED0间隔1s闪烁,每秒打印app is run

1.设置flash地址和flash大小

2.添加生成bin文件地址

注意:下边命令中涉及到的路径需要和实际路径向匹配,涉及3个路径

  1. fromelf.exe 文件路径(一般都和我一样的)
  2. GD32F103C8T6.bin 文件路径 保存bin文件路径
  3. GD32F103C8T6.axf mkd生成axf的文件路径
C:\Keil_v5\ARM\ARMCC\bin\fromelf.exe --bin -o GD32F103C8T6.bin  .\Objects\GD32F103C8T6.axf

3.修改中断偏移地址

2.bootload 程序

使用串口的中断+空闲中断方式接收数据,然后使用按键作为触发动作写入bin文件内容到flash,

最后跳转至APP程序;

1.中断接收程序

#ifndef __GD32_BSP_UART_H__
#define __GD32_BSP_UART_H__/*串口初始化
*/#include "stdint.h"
#define CACHE_NUM 1024*10extern volatile uint16_t rxcount ;         //已经接收到的数据数量
extern volatile uint8_t idle_or_full; //数据接收完成标志
extern uint8_t rxbuffer[CACHE_NUM];     //接收数据缓冲区/*! 串口基本配置
*/
void usart_base_init(void);
/*使能中断相关配置
*/
void enable_uart_interrupt(void);#endif
#include "gd32f10x.h"
#include "stdio.h"
#include "bsp_uart.h"uint8_t rxbuffer[CACHE_NUM];
volatile uint16_t rxcount = 0;
volatile uint8_t idle_or_full=0;/* 重定向printf函数 */
int fputc(int ch, FILE *f)
{usart_data_transmit(USART0, (uint8_t)ch);int cnt=1000;while(RESET == usart_flag_get(USART0, USART_FLAG_TBE) &&cnt--);return ch;
}/*! 串口基本配置
*/
/*基本初始化函数*/
void usart_base_init(void)
{/* 使能串口时钟 */rcu_periph_clock_enable(RCU_USART0);#if defined USART0_REMAP //如果串口引脚需要重映射/* 使能gpio时钟  */rcu_periph_clock_enable(RCU_GPIOB);rcu_periph_clock_enable(RCU_AF); //打开重映射时钟/* USART0 串口重映射使能 */gpio_pin_remap_config(GPIO_USART0_REMAP, ENABLE);/* 发送引脚初始化 */gpio_init(GPIOB, GPIO_MODE_AF_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_6);/* 接收引脚初始化 */gpio_init(GPIOB, GPIO_MODE_IN_FLOATING, GPIO_OSPEED_50MHZ, GPIO_PIN_7);#else/* 使能gpio时钟 */rcu_periph_clock_enable(RCU_GPIOA);/* gpio IO 初始化发送引脚 */gpio_init(GPIOA, GPIO_MODE_AF_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_9);/* 初始化接收引脚 */gpio_init(GPIOA, GPIO_MODE_IN_FLOATING, GPIO_OSPEED_50MHZ, GPIO_PIN_10);#endif/* USART configure 串口参数初始化 */usart_deinit(USART0);//设置波特率usart_baudrate_set(USART0, 115200U);//设置数据长度usart_word_length_set(USART0, USART_WL_8BIT);//设置停止位usart_stop_bit_set(USART0, USART_STB_1BIT);//设置检验位usart_parity_config(USART0, USART_PM_NONE);//硬件流管理 都关闭usart_hardware_flow_rts_config(USART0, USART_RTS_DISABLE);usart_hardware_flow_cts_config(USART0, USART_CTS_DISABLE);//串口接收使能usart_receive_config(USART0, USART_RECEIVE_ENABLE);//串口发送使能usart_transmit_config(USART0, USART_TRANSMIT_ENABLE);//使能串口usart_enable(USART0);
}/*使能中断相关配置
*/
/*使能串口中断
*/
void enable_uart_interrupt(void)
{/*中断管理器使能,并分配优先级*/nvic_irq_enable(USART0_IRQn, 1, 1);/*清除中断标志*/usart_interrupt_flag_clear(USART0, USART_INT_RBNE);usart_interrupt_flag_clear(USART0, USART_INT_IDLE);/* 使能串口中断 */  //usart_interrupt_enable(USART0, USART_INT_TBE);//发送为空中断usart_interrupt_enable(USART0, USART_INT_RBNE);//接收不为空中断usart_interrupt_enable(USART0, USART_INT_IDLE);//空闲中断
}/*串口0中断处理函数
*/
/*串口0中断处理函数
*/
void USART0_IRQHandler(void)
{if(RESET != usart_interrupt_flag_get(USART0, USART_INT_FLAG_RBNE))//读取缓冲区不为空{rxbuffer[rxcount++] = usart_data_receive(USART0);//读取数据}else if(RESET != usart_interrupt_flag_get(USART0, USART_INT_FLAG_IDLE))//发送缓冲区为控制{usart_data_receive(USART0);//仅仅是为了清除空闲中断标记//if(rxbuffer[rxcount-2]==0x0d && rxbuffer[rxcount-1]==0x0a)idle_or_full=1;//表示接收到一帧数据}
}

2.flash跳转程序

#ifndef __GD32_FLASH_H__
#define __GD32_FLASH_H__
#include "stdint.h"
#include "stdio.h"
/*写入flash数据,函数内部做了4字节对齐操作
*/void write_flash(uint32_t write_addr, uint32_t *data, int len);
/*读取flash数据,函数内部做了4字节对齐操作
*/
void read_4Btye(uint32_t read_addr, uint32_t *data, uint32_t len);
#endif
#include "gd32f10x.h"
#include <stdio.h>
#include "bsp_flash.h"
/*对于主存储闪存容量不多于512KB的GD32F10x_CL和GD32F10x_HD,只使用了bank0;对 于 GD32F10x_MD , 闪 存 页 大 小 为 1KB 。GD32F10x_CL 和 GD32F10x_HD ,GD32F10x_XD, bank0的闪存页大小为2KB, bank1的闪存页大小为4KB;
*/
/*页大小*/
#define FMC_PAGE_SIZE           ((uint16_t)0x400U)/*读取flash数据,函数内部做了4字节对齐操作
*/
void read_4Btye(uint32_t read_addr, uint32_t *data, uint32_t len)
{int dest_addr;dest_addr = read_addr & 0xfffffffc;//4字节对齐dest_addr += read_addr & 0x03 > 0 ? 4 : 0;int real_len = len >> 2;uint32_t *addr = (uint32_t*)dest_addr;for(int i = 0; i < real_len; i++){*(data + i) = *(addr + i);}
}/*检查是否需要擦除页addr:待检查页起始地址*/
int check_ease(int start_addr )
{//指着类型转换uint32_t *addr = (uint32_t*)start_addr;for(int i = 0; i<FMC_PAGE_SIZE >> 2; i++){if(*(addr + i) != 0xffffffff){return 1;}}return 0;
}/*擦除需要使用的扇区,并保存扇区前半部分没有使用的部分
*/
void ease_flash(uint32_t write_addr, int page_num)
{int i = 0;//当前页的偏移地址uint32_t page_offset = write_addr & (FMC_PAGE_SIZE - 1);page_offset = page_offset >> 2; //换算为4字节//页起始地址uint32_t start_addr  = write_addr & (0xffffffff - FMC_PAGE_SIZE + 1);//指着类型转换uint32_t *addr = (uint32_t*)start_addr;uint32_t *buff = (uint32_t*)malloc(FMC_PAGE_SIZE);//将不操作的空间保存起来if(page_offset){for( i = 0; i < page_offset; i++){buff[i] = *(addr + i);}}uint32_t erase_counter;/* 解锁flash */fmc_unlock();//清除操作结束标志fmc_flag_clear(FMC_FLAG_BANK0_END);//清除擦除/错误标志fmc_flag_clear(FMC_FLAG_BANK0_WPERR);//清楚页编程错误标志fmc_flag_clear(FMC_FLAG_BANK0_PGERR);/* 擦除使用到的页 */for(erase_counter = 0; erase_counter < page_num; erase_counter++){//擦除指定的页,参数页地址if(check_ease(start_addr + FMC_PAGE_SIZE * erase_counter)){fmc_page_erase(start_addr + FMC_PAGE_SIZE * erase_counter);fmc_flag_clear(FMC_FLAG_BANK0_END);fmc_flag_clear(FMC_FLAG_BANK0_WPERR);fmc_flag_clear(FMC_FLAG_BANK0_PGERR);}}//写入扇区前半部分没有使用的空间if(page_offset){for( i = 0; i < page_offset; i++){//对flash编程,也就是写数据,每次写入1个字=4个字节fmc_word_program(start_addr + (i * 4), *(buff + i));fmc_flag_clear(FMC_FLAG_BANK0_END);fmc_flag_clear(FMC_FLAG_BANK0_WPERR);fmc_flag_clear(FMC_FLAG_BANK0_PGERR);}}/* flash加锁,禁止编程 */fmc_lock();free(buff);
}/*写入flash数据,函数内部做了4字节对齐操作
*/void write_flash(uint32_t write_addr, uint32_t *data, int len)
{int dest_addr;dest_addr = write_addr & 0xfffffffc;//4字节对齐dest_addr += write_addr & 0x03 > 0 ? 4 : 0;uint32_t page_num = len / FMC_PAGE_SIZE; //存放数据需要的扇区大小page_num += len % FMC_PAGE_SIZE ? 1 : 0;////擦除扇区ease_flash(dest_addr, page_num);int len_4Byte = len >> 2;/* 解锁 */fmc_unlock();for(int i = 0; i < len_4Byte; i++){//对flash编程,也就是写数据,每次写入1个字=4个字节fmc_word_program(dest_addr + i * 4, *(data + i));fmc_flag_clear(FMC_FLAG_BANK0_END);fmc_flag_clear(FMC_FLAG_BANK0_WPERR);fmc_flag_clear(FMC_FLAG_BANK0_PGERR);}/* 加锁*/fmc_lock();}

3.APP程序跳转

#ifndef _IAP_
#define _IAP_#include "gd32f10x.h"
#include "stdint.h"#define FLASH_APP1_ADDR     0x08007800      //第一个应用程序起始地址(存放在FLASH)typedef  void (*iapfun)(void);              //定义一个函数类型的参数.
void MSR_MSP(uint32_t addr);    //设置堆栈地址void iap_load_app(uint32_t appxaddr);           //执行flash里面的app程序#endif
#include "bsp_iap_bootloader.h"//设置栈顶地址
//addr:栈顶地址
__asm void MSR_MSP(uint32_t addr)
{MSR MSP, r0            //set Main Stack valueBX r14
}iapfun jump2app; //跳转到应用程序段
//appxaddr:用户代码起始地址.
void iap_load_app(uint32_t appxaddr)
{if(((*(uint32_t*)appxaddr)&0x2FFE0000)==0x20000000)  //检查栈顶地址是否合法.{ jump2app=(iapfun)*(uint32_t*)(appxaddr+4);     //用户代码区第二个字为程序开始地址(复位地址)        MSR_MSP(*(uint32_t*)appxaddr);                  //初始化APP堆栈指针(用户代码区的第一个字用于存放栈顶地址)jump2app();                                 //跳转到APP.}
}        

4.主函数

#include "gd32f10x.h"
#include "systick.h"
#include <stdio.h>
#include "main.h"
#include "string.h"
#include "bsp_uart.h"
#include "bsp_exti.h"
#include "gd32f103c_sys.h"
#include "bsp_flash.h"
#include "bsp_iap_bootloader.h"
#define LED0 PBout(13)
#define LED1 PBout(14)
#define LED2 PBout(15)
#define LED3 PAout(8)int main(void)
{int bin_size;/* 配置系统时钟 */systick_config();//设置中断分组nvic_priority_group_set(NVIC_PRIGROUP_PRE2_SUB2);/* gpio时钟使能*/rcu_periph_clock_enable(RCU_GPIOB);//rcu_periph_clock_enable(RCU_GPIOA);// LED gpio 初始化gpio_init(GPIOB, GPIO_MODE_OUT_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_13 | GPIO_PIN_14 | GPIO_PIN_15);//gpio_init(GPIOA,GPIO_MODE_OUT_PP,GPIO_OSPEED_50MHZ,GPIO_PIN_8);//串口初始化usart_base_init();//使能接收中断enable_uart_interrupt();/*GPIO初始化函数*/exit_gpio_init();//外部中断初始化input_exti_init();while (1){LED2 = !LED2;delay_1ms(50);if (idle_or_full){printf("read len = %d:", rxcount);for (int i = 0; i < rxcount; i++){printf("%02x ", (int)rxbuffer[i]);}printf("\r\n");idle_or_full = 0;bin_size  = rxcount;rxcount = 0;}if(exti_flag){exti_flag=0;write_flash(FLASH_APP1_ADDR,(uint32_t*)rxbuffer,bin_size);printf("wirte app\r\n");if(((*(uint32_t*)(FLASH_APP1_ADDR+4))&0xFF000000)==FLASH_BASE)//判断是否为0X08XXXXXX.{    iap_load_app(FLASH_APP1_ADDR);//执行FLASH APP代码}printf("update app\r\n");}}
}

其他辅助:

文件发送工具XCOM:

测试结果

24. GD32F103C8T6入门教程-IAP升级教程相关推荐

  1. gnocchi sdk_升级教程,Gnocchi 2.0版本和更多OpenStack新闻

    gnocchi sdk 有兴趣跟踪开源云中正在发生的事情吗? Opensource.com是您在开源云基础设施项目OpenStack中获取新闻的来源. Web上的OpenStack 关于OpenSta ...

  2. 【尚硅谷】Web前端零基础入门HTML5+CSS3基础教程

    [尚硅谷]Web前端零基础入门HTML5+CSS3基础教程 学习视频来源:哔哩哔哩弹幕网(https://www.bilibili.com/video/BV1XJ411X7Ud?spm_id_from ...

  3. python爬虫简单实例-最简单的Python爬虫案例,看得懂说明你已入门,附赠教程

    原标题:最简单的Python爬虫案例,看得懂说明你已入门,附赠教程 这是最简单的Python爬虫案例,如果你能看懂,那么请你保持信心,因为你已经入门Python爬虫,只要带着信心和努力,你的技术能力在 ...

  4. python自学视频教程 38-python编程开发入门中文视频培训教程38讲

    Python 当前位置:主页 > 编程教程 > Python > python编程开发入门中文视频培训教程38讲 python编程开发入门中文视频培训教程38讲 教程大小:   发布 ...

  5. altium designer 入门视频教程 制作pcb教程视频

    altium designer 入门视频教程 制作pcb教程视频 本套altium designer视频针对在校学生.初学者等刚刚进行单片机开发学习的同学进行录制,带领学生初识Altium Desig ...

  6. 零基础入门WordPress安装详细教程(图文)

    概述 如果还有不了解宝塔面板怎么使用的小伙伴,可以看下我总结的系列教程,保证从新手变老鸟: [宝塔面板精选教程汇总] 宝塔面板教程(1)基于云服务器搭建宝塔面板教程最全详解 宝塔面板教程(2)宝塔面板 ...

  7. grread使用例子 lisp_AutoLISP从入门到精通初级教程(新手必备);

    <AutoLISP从入门到精通初级教程(新手必备);>由会员分享,可在线阅读,更多相关<AutoLISP从入门到精通初级教程(新手必备);(36页珍藏版)>请在人人文库网上搜索 ...

  8. [转载]VBS入门教程 VBS基础教程

    [转载]VBS入门教程 VBS基础教程(收藏) VBS基础教程 VBS(VBScript的进一步简写)是基于Visual Basic的脚本语言. Microsoft Visual Basic是微软公司 ...

  9. 海美迪盒子android升级包,海美迪Q10官方固件ROM升级包下载_升级教程

    海美迪Q10机顶盒属于同类产品中的高端机了,性能个方便吊打百元机市场的盒子! 那么今天小编就跟大家分享下关于Q10盒子固件的升级教程! 产品型号:Q10四代固件版本:HMD-2.1.4 2019-08 ...

最新文章

  1. HDU2594——Simpsons’ Hidden Talents
  2. RSA选用小公钥指数(e=3)真的不安全吗?
  3. python中的json注意事项
  4. 职场‘下班沉默症’调查
  5. R语言观察日志(part3)--repeat循环
  6. ios 一直是正在等待审核_iOS开发者账号被调查了,相关问题整理
  7. PKU 3013 Big Christmas Tree 最短路 spfa
  8. jquery + ashx + Json 操作数据
  9. python中求众数_Python实现求众数的三种方法
  10. 【IT圈内事】2019互联网企业100强
  11. 关于 RabbitMQ,应该没有比这更详细的教程了
  12. 关于memset,malloc以及free后的野指针误区详解
  13. 爬虫库分享(一):requests常见用法总结
  14. 航班编程代码c语言,c语言编写航班查询代码.doc
  15. 网站分析平台:百度统计、谷歌统计、网数星,统计平台怎样选择?
  16. php电子商务网站案例,基于PHP的B2C电子商务网站开发
  17. win7万能声卡驱动_win7的早期版本下安装驱动失败
  18. 电气工程申请计算机科学,密歇根大学安娜堡分校
  19. java8 List去重
  20. RabbitMQ后台管理界面

热门文章

  1. 酒桌小游戏喝酒小程序
  2. 九歌·湘夫人 先秦 · 屈原
  3. 购物网站的html5页面,网购商城(html5页面设计)
  4. winServer2008下配置企业管理器
  5. unity 简单的吃豆豆项目
  6. IG541与七氟丙烷灭火系统到底有什么不一样呢?
  7. 服务器防火墙软件 —— iptables
  8. 【Chrome】图片批量下载扩展zzllrr Imager小乐图客V1.4 (支持正则表达式、自定义JS代码、自定义引擎、多网站取图规则)...
  9. tinygo的windows环境搭建及简单例程
  10. 逝者:Django贡献最多的核心开发者Malcolm Tredinnick