零基础国产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

相关文章

一文详解Java异常处理你都了解哪些知识

《一文详解Java异常处理你都了解哪些知识》:本文主要介绍Java异常处理的相关资料,包括异常的分类、捕获和处理异常的语法、常见的异常类型以及自定义异常的实现,文中通过代码介绍的非常详细,需要的朋... 目录前言一、什么是异常二、异常的分类2.1 受检异常2.2 非受检异常三、异常处理的语法3.1 try-

Java中的@SneakyThrows注解用法详解

《Java中的@SneakyThrows注解用法详解》:本文主要介绍Java中的@SneakyThrows注解用法的相关资料,Lombok的@SneakyThrows注解简化了Java方法中的异常... 目录前言一、@SneakyThrows 简介1.1 什么是 Lombok?二、@SneakyThrows

Java中字符串转时间与时间转字符串的操作详解

《Java中字符串转时间与时间转字符串的操作详解》Java的java.time包提供了强大的日期和时间处理功能,通过DateTimeFormatter可以轻松地在日期时间对象和字符串之间进行转换,下面... 目录一、字符串转时间(一)使用预定义格式(二)自定义格式二、时间转字符串(一)使用预定义格式(二)自

Redis Pipeline(管道) 详解

《RedisPipeline(管道)详解》Pipeline管道是Redis提供的一种批量执行命令的机制,通过将多个命令一次性发送到服务器并统一接收响应,减少网络往返次数(RTT),显著提升执行效率... 目录Redis Pipeline 详解1. Pipeline 的核心概念2. 工作原理与性能提升3. 核

Python正则表达式语法及re模块中的常用函数详解

《Python正则表达式语法及re模块中的常用函数详解》这篇文章主要给大家介绍了关于Python正则表达式语法及re模块中常用函数的相关资料,正则表达式是一种强大的字符串处理工具,可以用于匹配、切分、... 目录概念、作用和步骤语法re模块中的常用函数总结 概念、作用和步骤概念: 本身也是一个字符串,其中

Nginx location匹配模式与规则详解

《Nginxlocation匹配模式与规则详解》:本文主要介绍Nginxlocation匹配模式与规则,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录一、环境二、匹配模式1. 精准模式2. 前缀模式(不继续匹配正则)3. 前缀模式(继续匹配正则)4. 正则模式(大

Android实现在线预览office文档的示例详解

《Android实现在线预览office文档的示例详解》在移动端展示在线Office文档(如Word、Excel、PPT)是一项常见需求,这篇文章为大家重点介绍了两种方案的实现方法,希望对大家有一定的... 目录一、项目概述二、相关技术知识三、实现思路3.1 方案一:WebView + Office Onl

Java中的Lambda表达式及其应用小结

《Java中的Lambda表达式及其应用小结》Java中的Lambda表达式是一项极具创新性的特性,它使得Java代码更加简洁和高效,尤其是在集合操作和并行处理方面,:本文主要介绍Java中的La... 目录前言1. 什么是Lambda表达式?2. Lambda表达式的基本语法例子1:最简单的Lambda表

Java实现优雅日期处理的方案详解

《Java实现优雅日期处理的方案详解》在我们的日常工作中,需要经常处理各种格式,各种类似的的日期或者时间,下面我们就来看看如何使用java处理这样的日期问题吧,感兴趣的小伙伴可以跟随小编一起学习一下... 目录前言一、日期的坑1.1 日期格式化陷阱1.2 时区转换二、优雅方案的进阶之路2.1 线程安全重构2

Java中的JSONObject详解

《Java中的JSONObject详解》:本文主要介绍Java中的JSONObject详解,需要的朋友可以参考下... Java中的jsONObject详解一、引言在Java开发中,处理JSON数据是一种常见的需求。JSONObject是处理JSON对象的一个非常有用的类,它提供了一系列的API来操作J