24. GD32F103C8T6入门教程-IAP升级教程
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个路径
- fromelf.exe 文件路径(一般都和我一样的)
- GD32F103C8T6.bin 文件路径 保存bin文件路径
- 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升级教程相关推荐
- gnocchi sdk_升级教程,Gnocchi 2.0版本和更多OpenStack新闻
gnocchi sdk 有兴趣跟踪开源云中正在发生的事情吗? Opensource.com是您在开源云基础设施项目OpenStack中获取新闻的来源. Web上的OpenStack 关于OpenSta ...
- 【尚硅谷】Web前端零基础入门HTML5+CSS3基础教程
[尚硅谷]Web前端零基础入门HTML5+CSS3基础教程 学习视频来源:哔哩哔哩弹幕网(https://www.bilibili.com/video/BV1XJ411X7Ud?spm_id_from ...
- python爬虫简单实例-最简单的Python爬虫案例,看得懂说明你已入门,附赠教程
原标题:最简单的Python爬虫案例,看得懂说明你已入门,附赠教程 这是最简单的Python爬虫案例,如果你能看懂,那么请你保持信心,因为你已经入门Python爬虫,只要带着信心和努力,你的技术能力在 ...
- python自学视频教程 38-python编程开发入门中文视频培训教程38讲
Python 当前位置:主页 > 编程教程 > Python > python编程开发入门中文视频培训教程38讲 python编程开发入门中文视频培训教程38讲 教程大小: 发布 ...
- altium designer 入门视频教程 制作pcb教程视频
altium designer 入门视频教程 制作pcb教程视频 本套altium designer视频针对在校学生.初学者等刚刚进行单片机开发学习的同学进行录制,带领学生初识Altium Desig ...
- 零基础入门WordPress安装详细教程(图文)
概述 如果还有不了解宝塔面板怎么使用的小伙伴,可以看下我总结的系列教程,保证从新手变老鸟: [宝塔面板精选教程汇总] 宝塔面板教程(1)基于云服务器搭建宝塔面板教程最全详解 宝塔面板教程(2)宝塔面板 ...
- grread使用例子 lisp_AutoLISP从入门到精通初级教程(新手必备);
<AutoLISP从入门到精通初级教程(新手必备);>由会员分享,可在线阅读,更多相关<AutoLISP从入门到精通初级教程(新手必备);(36页珍藏版)>请在人人文库网上搜索 ...
- [转载]VBS入门教程 VBS基础教程
[转载]VBS入门教程 VBS基础教程(收藏) VBS基础教程 VBS(VBScript的进一步简写)是基于Visual Basic的脚本语言. Microsoft Visual Basic是微软公司 ...
- 海美迪盒子android升级包,海美迪Q10官方固件ROM升级包下载_升级教程
海美迪Q10机顶盒属于同类产品中的高端机了,性能个方便吊打百元机市场的盒子! 那么今天小编就跟大家分享下关于Q10盒子固件的升级教程! 产品型号:Q10四代固件版本:HMD-2.1.4 2019-08 ...
最新文章
- HDU2594——Simpsons’ Hidden Talents
- RSA选用小公钥指数(e=3)真的不安全吗?
- python中的json注意事项
- 职场‘下班沉默症’调查
- R语言观察日志(part3)--repeat循环
- ios 一直是正在等待审核_iOS开发者账号被调查了,相关问题整理
- PKU 3013 Big Christmas Tree 最短路 spfa
- jquery + ashx + Json 操作数据
- python中求众数_Python实现求众数的三种方法
- 【IT圈内事】2019互联网企业100强
- 关于 RabbitMQ,应该没有比这更详细的教程了
- 关于memset,malloc以及free后的野指针误区详解
- 爬虫库分享(一):requests常见用法总结
- 航班编程代码c语言,c语言编写航班查询代码.doc
- 网站分析平台:百度统计、谷歌统计、网数星,统计平台怎样选择?
- php电子商务网站案例,基于PHP的B2C电子商务网站开发
- win7万能声卡驱动_win7的早期版本下安装驱动失败
- 电气工程申请计算机科学,密歇根大学安娜堡分校
- java8 List去重
- RabbitMQ后台管理界面
热门文章
- 酒桌小游戏喝酒小程序
- 九歌·湘夫人 先秦 · 屈原
- 购物网站的html5页面,网购商城(html5页面设计)
- winServer2008下配置企业管理器
- unity 简单的吃豆豆项目
- IG541与七氟丙烷灭火系统到底有什么不一样呢?
- 服务器防火墙软件 —— iptables
- 【Chrome】图片批量下载扩展zzllrr Imager小乐图客V1.4 (支持正则表达式、自定义JS代码、自定义引擎、多网站取图规则)...
- tinygo的windows环境搭建及简单例程
- 逝者:Django贡献最多的核心开发者Malcolm Tredinnick