零基础国产GD32单片机编程入门(十三)单片机IAP(在应用编程)详解及实战源码

本文主要是介绍零基础国产GD32单片机编程入门(十三)单片机IAP(在应用编程)详解及实战源码,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

文章目录

    • 一.概要
    • 二.GD32F103C8T6单片机IAP介绍
      • 1.GD32F103C8T6单片机IAP基本原理
      • 2.GD32F103C8T6单片机IAP基本流程
    • 三.配置一个BOOT工程
    • 四.配置一个APP工程
    • 五.工程源代码下载
    • 六.小结

一.概要

GD32单片机程序升级方法有很多种,主要有以下几种:

1.将编译生成的hex/bin文件使用ST-Link/J-Link工具直接下载进 Flash 即可,Keil中点击下载就能下载,下载后的代码会存放在Flash的起始地址0x08000000处。

2.ISP(In System Programing),这个是利用了GD32单片机自带的 Bootloader 升级程序。一般可通过USART串口对Flash重新编程,再通过电脑上的ISP下载软件导入程序。在用户参考手册中,可以看到下表,关于启动模式设置的,ISP就是BOOT1引脚为0,BOOT0引脚为1,单片机就进入ISP模式。
在这里插入图片描述
3. IAP(In Application Programing),即在应用编程,与之相对应的叫做ISP,两者的不同是ISP需要依靠烧写器在单片机复位离线的情况下编程,需要人工的干预,而IAP则是用户自己的程序在运行过程中对Flash 的部分区域进行烧写,目的是为了在产品发布后可以方便地通过预留的通信口对产品中的固件程序进行更新升级。

使用IAP技术能很好地降低现场工作量,实现IAP有两个很重要的前提
1.单片机程序能对自身的内部Flash 进行擦写。
2.单片机要有能够和外部进行通讯的方式,无论是网络还是别的方式,只要能传输数据就行。

二.GD32F103C8T6单片机IAP介绍

1.GD32F103C8T6单片机IAP基本原理

以GD32F103C8T6单片机为例,每次程序复位是从0x08000000的位置开始执行主程序,如果不做IAP则这64KB(0x10000)空间都可以用来存放应用程序,但为了实现IAP,需要有划出一部分空间存放BOOT程序,BOOT程序跟应用程序是两个独立的工程,BOOT程序主要功能是来接收外部通讯(串口,485等)协议传输的应用程序代码文件(bin文件),并调用FLASH写入函数把bin文件分成N个32Bit数据,写入到应用程序地址空间,就实现对应用程序的升级。
在这里插入图片描述

2.GD32F103C8T6单片机IAP基本流程

单片机先在BOOT工程的程序中跑,BOOT程序通过串口接收上位机发来的.bin文件(应用程序工程),检查后将.bin文件写入到Flash特定位置(0x08004000开始的地址),bin文件写完后,单片机就从BOOT程序的空间跳转到应用程序的空间运行。
在这里插入图片描述

三.配置一个BOOT工程

本实验配置一个包含跳转的程序工程,包含FLASH写入以及Y-Modem协议。

添加代码
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

主要代码如下:

//串口设置USART0 ,PB6,PB7脚,波特率115200,无校验,8位数据,1位停止位
void gd_eval_com_init(void)
{/* enable GPIO clock */rcu_periph_clock_enable(RCU_GPIOB);/* enable USART clock */rcu_periph_clock_enable(RCU_USART0);gpio_pin_remap_config(GPIO_USART0_REMAP, ENABLE);//PB6,PB7需要重映射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);/* USART configure */usart_deinit(USART0);usart_baudrate_set(USART0,115200);//波特率115200usart_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_transmit_config(USART0, USART_TRANSMIT_ENABLE);usart_receive_config(USART0, USART_RECEIVE_ENABLE);usart_enable(USART0);
}
int main(void)
{rcu_ahb_clock_config(RCU_AHB_CKSYS_DIV1);//AHB主频是1分频systick_config();//系统主频108MHZ,采用外部晶振,由两个宏决定(__SYSTEM_CLOCK_108M_PLL_HXTAL与HXTAL_VALUE)rcu_periph_clock_enable(RCU_AF); //管脚复用时钟alternate function clock使能gd_eval_com_init();//串口初始化FLASH_If_Init();//FLASH解锁while(1){Main_Menu();//实现程序升级并跳转,Ymodem协议}return 0;
}/*** @brief  Receive a file using the ymodem protocol* @param  buf: Address of the first byte* @retval The size of the file*/
int32_t Ymodem_Receive (uint8_t *buf)
{uint8_t packet_data[PACKET_1K_SIZE + PACKET_OVERHEAD], file_size[FILE_SIZE_LENGTH], *file_ptr, *buf_ptr;int32_t i, packet_length, session_done, file_done, packets_received, errors, session_begin, size = 0;uint32_t flashdestination, ramsource;/* Initialize flashdestination variable */flashdestination = APPLICATION_ADDRESS;for (session_done = 0, errors = 0, session_begin = 0; ;){for (packets_received = 0, file_done = 0, buf_ptr = buf; ;){switch (Receive_Packet(packet_data, &packet_length, NAK_TIMEOUT)){case 0:errors = 0;switch (packet_length){/* Abort by sender */case - 1:Send_Byte(ACK);return 0;/* End of transmission */case 0:Send_Byte(ACK);file_done = 1;break;/* Normal packet */default:if ((packet_data[PACKET_SEQNO_INDEX] & 0xff) != (packets_received & 0xff)){Send_Byte(NAK);}else{if (packets_received == 0){/* Filename packet */if (packet_data[PACKET_HEADER] != 0){/* Filename packet has valid data */for (i = 0, file_ptr = packet_data + PACKET_HEADER; (*file_ptr != 0) && (i < FILE_NAME_LENGTH);){FileName[i++] = *file_ptr++;}FileName[i++] = '\0';for (i = 0, file_ptr ++; (*file_ptr != ' ') && (i < (FILE_SIZE_LENGTH - 1));){file_size[i++] = *file_ptr++;}file_size[i++] = '\0';Str2Int(file_size, &size);/* Test the size of the image to be sent *//* Image size is greater than Flash size */if (size > (USER_FLASH_SIZE + 1)){/* End session */Send_Byte(CA);Send_Byte(CA);return -1;}/* erase user application area */FLASH_If_Erase(APPLICATION_ADDRESS);Send_Byte(ACK);Send_Byte(CRC16);}/* Filename packet is empty, end session */else{Send_Byte(ACK);file_done = 1;session_done = 1;break;}}/* Data packet */else{memcpy(buf_ptr, packet_data + PACKET_HEADER, packet_length);ramsource = (uint32_t)buf;/* Write received data in Flash */if (FLASH_If_Write(&flashdestination, (uint32_t*) ramsource, (uint16_t) packet_length/4)  == 0){Send_Byte(ACK);}else /* An error occurred while writing to Flash memory */{/* End session */Send_Byte(CA);Send_Byte(CA);return -2;}}packets_received ++;session_begin = 1;}}break;case 1:Send_Byte(CA);Send_Byte(CA);return -3;default:if (session_begin > 0){errors ++;}if (errors > MAX_ERRORS){Send_Byte(CA);Send_Byte(CA);return 0;}Send_Byte(CRC16);break;}if (file_done != 0){break;}}if (session_done != 0){break;}}return (int32_t)size;
}void SerialDownload(void)
{uint8_t Number[10] = {0};int32_t Size = 0;SerialPutString("Waiting for the file to be sent ... (press 'a' to abort)\n\r");Size = Ymodem_Receive(&tab_1024[0]);if (Size > 0){SerialPutString("\n\n\r Programming Completed Successfully!\n\r--------------------------------\r\n Name: ");SerialPutString(FileName);Int2Str(Number, Size);SerialPutString("\n\r Size: ");SerialPutString(Number);SerialPutString(" Bytes\r\n");SerialPutString("Jump To App\r\n");JumpAddress = *(__IO uint32_t*) (APPLICATION_ADDRESS + 4);Jump_To_Application = (pFunction) JumpAddress;/* Initialize user application's Stack Pointer */__set_MSP(*(__IO uint32_t*) APPLICATION_ADDRESS);/* Jump to application */Jump_To_Application();}else if (Size == -1){SerialPutString("\n\n\rThe image size is higher than the allowed space memory!\n\r");}else if (Size == -2){SerialPutString("\n\n\rVerification failed!\n\r");}else if (Size == -3){SerialPutString("\r\n\nAborted by user.\n\r");}else{SerialPutString("\n\rFailed to receive the file!\n\r");}
}

四.配置一个APP工程

本实验配置一个LED闪烁的程序工程
添加代码

配置应用程序起始地址
在这里插入图片描述

生成.bin文件配置,配置完,编译的时候就会生成.bin文件
在这里插入图片描述

中断向量表偏移配置
在这里插入图片描述
主要代码如下:

#include "gd32f10x.h"
#include "gd32f10x_libopt.h"
#include "systick.h"int main(void)
{rcu_ahb_clock_config(RCU_AHB_CKSYS_DIV1);//AHB主频是1分频nvic_vector_table_set(NVIC_VECTTAB_FLASH, 0x4000);//中断向量地址偏移0x4000systick_config();//系统主频108MHZ,采用外部晶振,由两个宏决定(__SYSTEM_CLOCK_108M_PLL_HXTAL与HXTAL_VALUE)rcu_periph_clock_enable(RCU_AF); //管脚复用时钟alternate function clock使能delay_1ms(1000);//等待1秒gpio_pin_remap_config(GPIO_SWJ_NONJTRST_REMAP, ENABLE);//PB4管脚默认是NJTRST,要当GPIO,需要重映射rcu_periph_clock_enable(RCU_GPIOB);//GPIOB时钟使能gpio_init(GPIOB, GPIO_MODE_OUT_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_4);//PB4配置成输出while(1){delay_1ms(1000);//等待1秒gpio_bit_set(GPIOB, GPIO_PIN_4);//输出高电平delay_1ms(1000);//等待1秒gpio_bit_reset(GPIOB, GPIO_PIN_4);//输出低电平}
}

实验效果:
BOOT程序:主要实现YMODEM协议以及内部FLASH编程,程序烧录完之后,由APP程序生成的APP.Bin文件烧录到APP程序的FLASH地址空间,再实现程序跳转。

1.Keil5打开BOOT工程,编译,并烧录BOOT程序。
2. Keil5打开APP程序,编译,生成APP.bin文件,文件在工程Objects目录下。
3. 打开Xshell6软件,配置好串口参数,115200波特率,无校验,COM口号是根据电脑自动识别,再点连接。会提示连接成功
在这里插入图片描述
在这里插入图片描述

4.板子重新上电,提示输入1,就在屏幕上输入1,会有C符号提示
在这里插入图片描述

5.选择YMODEM传输,选择APP.Bin文件
在这里插入图片描述

6.选择文件传输后,进度条会有进度,而且最终屏幕显示Jum To App,说明IAP升级成功
在这里插入图片描述

五.工程源代码下载

通过网盘分享的文件:14.IAP实验.zip
链接: https://pan.baidu.com/s/1WxJfDd0OxS_6HjPBc_W6Mg 提取码: 8g3k

如果链接失效,可以联系博主给最新链接
程序下载下来之后解压就行

六.小结

在单片机应用中,在线升级功能是必不可少的,它可以让我们在不破坏硬件的情况下对程序进行升级和修正,提高了开发效率。

这篇关于零基础国产GD32单片机编程入门(十三)单片机IAP(在应用编程)详解及实战源码的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1136834

相关文章

MySQL数据库双机热备的配置方法详解

《MySQL数据库双机热备的配置方法详解》在企业级应用中,数据库的高可用性和数据的安全性是至关重要的,MySQL作为最流行的开源关系型数据库管理系统之一,提供了多种方式来实现高可用性,其中双机热备(M... 目录1. 环境准备1.1 安装mysql1.2 配置MySQL1.2.1 主服务器配置1.2.2 从

Linux kill正在执行的后台任务 kill进程组使用详解

《Linuxkill正在执行的后台任务kill进程组使用详解》文章介绍了两个脚本的功能和区别,以及执行这些脚本时遇到的进程管理问题,通过查看进程树、使用`kill`命令和`lsof`命令,分析了子... 目录零. 用到的命令一. 待执行的脚本二. 执行含子进程的脚本,并kill2.1 进程查看2.2 遇到的

MyBatis常用XML语法详解

《MyBatis常用XML语法详解》文章介绍了MyBatis常用XML语法,包括结果映射、查询语句、插入语句、更新语句、删除语句、动态SQL标签以及ehcache.xml文件的使用,感兴趣的朋友跟随小... 目录1、定义结果映射2、查询语句3、插入语句4、更新语句5、删除语句6、动态 SQL 标签7、ehc

Java AOP面向切面编程的概念和实现方式

《JavaAOP面向切面编程的概念和实现方式》AOP是面向切面编程,通过动态代理将横切关注点(如日志、事务)与核心业务逻辑分离,提升代码复用性和可维护性,本文给大家介绍JavaAOP面向切面编程的概... 目录一、AOP 是什么?二、AOP 的核心概念与实现方式核心概念实现方式三、Spring AOP 的关

详解SpringBoot+Ehcache使用示例

《详解SpringBoot+Ehcache使用示例》本文介绍了SpringBoot中配置Ehcache、自定义get/set方式,并实际使用缓存的过程,文中通过示例代码介绍的非常详细,对大家的学习或者... 目录摘要概念内存与磁盘持久化存储:配置灵活性:编码示例引入依赖:配置ehcache.XML文件:配置

从基础到高级详解Go语言中错误处理的实践指南

《从基础到高级详解Go语言中错误处理的实践指南》Go语言采用了一种独特而明确的错误处理哲学,与其他主流编程语言形成鲜明对比,本文将为大家详细介绍Go语言中错误处理详细方法,希望对大家有所帮助... 目录1 Go 错误处理哲学与核心机制1.1 错误接口设计1.2 错误与异常的区别2 错误创建与检查2.1 基础

k8s按需创建PV和使用PVC详解

《k8s按需创建PV和使用PVC详解》Kubernetes中,PV和PVC用于管理持久存储,StorageClass实现动态PV分配,PVC声明存储需求并绑定PV,通过kubectl验证状态,注意回收... 目录1.按需创建 PV(使用 StorageClass)创建 StorageClass2.创建 PV

Python版本信息获取方法详解与实战

《Python版本信息获取方法详解与实战》在Python开发中,获取Python版本号是调试、兼容性检查和版本控制的重要基础操作,本文详细介绍了如何使用sys和platform模块获取Python的主... 目录1. python版本号获取基础2. 使用sys模块获取版本信息2.1 sys模块概述2.1.1

一文详解Python如何开发游戏

《一文详解Python如何开发游戏》Python是一种非常流行的编程语言,也可以用来开发游戏模组,:本文主要介绍Python如何开发游戏的相关资料,文中通过代码介绍的非常详细,需要的朋友可以参考下... 目录一、python简介二、Python 开发 2D 游戏的优劣势优势缺点三、Python 开发 3D

Redis 基本数据类型和使用详解

《Redis基本数据类型和使用详解》String是Redis最基本的数据类型,一个键对应一个值,它的功能十分强大,可以存储字符串、整数、浮点数等多种数据格式,本文给大家介绍Redis基本数据类型和... 目录一、Redis 入门介绍二、Redis 的五大基本数据类型2.1 String 类型2.2 Hash