文章目录
一.概要
单片机上电或系统复位后,ARM® Cortex®-M4处理器先从0x0000 0000地址获取栈顶值,再从0x0000 0004地址获得引导代码的基地址,然后从引导代码的基地址开始执行程序。
所选引导源对应的存储空间会被映射到引导存储空间,即从0x0000 0000开始的地址空间。如果片上SRAM(开始于0x2000 0000的存储空间)被选为引导源,用户必须在应用程序初始化代码中通过修改NVIC异常向量表和偏移地址将向量表重置到SRAM中。当主FLASH存储器被选择作为引导源,从0x0800 0000开始的存储空间会被映射到引导存储空间。GD32F4xx系列微控制器提供了三种引导源,可以通过BOOT0和BOOT1引脚来进行选择,如下图所示,一般我们都选主闪存存储器作为引导源。
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模式。
- IAP(In Application Programing),即在应用编程,与之相对应的叫做ISP,两者的不同是ISP需要依靠烧写器在单片机复位离线的情况下编程,需要人工的干预,而IAP则是用户自己的程序在运行过程中对Flash 的部分区域进行烧写,目的是为了在产品发布后可以方便地通过预留的通信口对产品中的固件程序进行更新升级。
使用IAP技术能很好地降低现场工作量,实现IAP有两个很重要的前提
1.单片机程序能对自身的内部Flash 进行擦写。
2.单片机要有能够和外部进行通讯的方式,无论是网络还是别的方式,只要能传输数据就行。
二.GD32F407VET6单片机IAP介绍
1.GD32F407VET6单片机IAP基本原理
以GD32F407VET6单片机为例,每次程序复位是从0x08000000的位置开始执行主程序,如果不做IAP则这512KB(0x80000)空间都可以用来存放应用程序,但为了实现IAP,需要有划出一部分空间存放BOOT程序,BOOT程序跟应用程序是两个独立的工程,BOOT程序主要功能是来接收外部通讯(串口,485等)协议传输的应用程序代码文件(bin文件),并调用FLASH写入函数把bin文件分成N个32Bit数据,写入到应用程序地址空间,就实现对应用程序的升级。
2.GD32F407VET6单片机IAP基本流程
单片机先在BOOT工程的程序中跑,BOOT程序通过串口接收上位机发来的.bin文件(应用程序工程),检查后将.bin文件写入到Flash特定位置(0x08004000开始的地址),bin文件写完后,单片机就从BOOT程序的空间跳转到应用程序的空间运行。
3.Ymodem协议
YMODEM协议介绍
YModem协议是一种高效的文件传输协议,广泛应用于嵌入式系统中的文件传输。YModem协议是一种发送并等待的协议,它基于XModem协议演变而来,但提供了更高的传输效率。YModem协议每包数据可以达到1024字节,相比XModem的128字节,大大提高了文件传输的速度。此外,YModem协议还支持批处理模式,允许一次传输多个文件,非常适合嵌入式系统中的文件传输需求。
YModem协议的基本操作流程:
• 传输启动:传输由接收方发起,接收方通过串口发送一个字符’C’来请求开始传输。
• 发送文件头:发送方收到’C’后,发送第一个数据包(包含文件名、文件大小等信息)。这个数据包通常使用SOH(Start of Header)作为起始标志。发送方等待接收方的确认(ACK)。
• 接收方确认:接收方收到文件头后,如果准备接收文件,则再次发送字符’C’。
• 发送文件数据:发送方开始发送文件数据包,每个数据包包含1024字节的数据(除了最后一个数据包可能小于1024字节)。每个数据包后都等待接收方的ACK确认。
• 传输结束:数据传输完毕后,发送方发送EOT(End of Transmission)信号表示传输结束。接收方首次以NAK应答进行二次确认,发送方重发EOT后,接收方以ACK应答。
• 结束传输:若无其他文件需传输,发送方发送一个全零数据包,接收方应答ACK后,正式结束数据传输。
数据包格式解析
YModem协议的数据包格式如下:
• 起始标志:数据包以SOH(0x01)或STX(0x02)作为起始标志,表示数据块的大小。SOH用于128字节的数据块,而STX用于1024字节的数据块。
• 发送序号和反码:数据包中的编号用于标识数据包的顺序,而反码则是编号的二进制反码,用于确保数据包的正确性。
• 数据字段:数据字段包含实际传输的数据。对于文件名和文件大小等信息,它们通常被编码为ASCII字符并填充到数据字段中。
• 校验字段:数据包包含CRC(Cyclic Redundancy Check)校验码,用于验证数据的完整性。CRC校验码通常附加在数据字段的末尾。
YModem协议基本格式如下所示。
三.配置一个BOOT工程
本实验配置一个包含跳转的程序工程,包含FLASH写入以及Y-Modem协议。
添加代码
定义程序跳转地址与升级代码刷写的地址。
修改ymodem协议中的串口收发函数。
定义BOOT代码地址空间为0x08000000~0x08004000。
主要代码如下:
#include "gd32f4xx.h"
#include "gd32f4xx_libopt.h"
#include "systick.h"
extern void Main_Menu(void);
//USART0初始化,使用PA9(TX),PA10(RX)脚,115200波特率,无校验,8位数据,1位停止
void gd_eval_com_init(void)
{
/* enable GPIO clock */
rcu_periph_clock_enable(RCU_GPIOA);//使能GPIOA时钟
/* enable USART clock */
rcu_periph_clock_enable(RCU_USART0);//使能USART0时钟
gpio_af_set(GPIOA, GPIO_AF_7, GPIO_PIN_9);//复用功能7
gpio_mode_set(GPIOA, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_9);//PA9配置成串口输出
gpio_output_options_set(GPIOA, GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ,GPIO_PIN_9);
gpio_af_set(GPIOA, GPIO_AF_7, GPIO_PIN_10);//复用功能7
gpio_mode_set(GPIOA, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_10);//PA10配置成串口输入
gpio_output_options_set(GPIOA, GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ,GPIO_PIN_10);
/* USART configure */
usart_deinit(USART0);
usart_baudrate_set(USART0,115200);
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_transmit_config(USART0, USART_TRANSMIT_ENABLE);
usart_receive_config(USART0, USART_RECEIVE_ENABLE);
usart_enable(USART0);//使能USART0
}
int main(void)
{
systick_config();//配置系统主频168M,外部8M晶振,配置在#define __SYSTEM_CLOCK_168M_PLL_8M_HXTAL (uint32_t)(168000000)
gd_eval_com_init();//串口初始化
while(1)
{
Main_Menu();//实现程序升级并跳转,Ymodem协议
}
}
void SerialDownload(void)
{
int32_t Size = 0;
Size = Ymodem_Receive(&tab_1024[0]);//升级代码
if (Size > 0)
{
SerialPutString("\r\n Programming Completed Successfully!\r\n ");//显示升级成功
delay_1ms(10);
SerialPutString("----------Jump To App-------------\r\n");
CLI();
NVIC_DeInit();
JumpAddress = *(__IO uint32_t*) (APPLICATION_ADDRESS + 4);//定义跳转地址是0x08004000
JumpToApplication = (pFunction) JumpAddress;
/* Initialize user application's Stack Pointer */
__set_MSP(*(__IO uint32_t*) APPLICATION_ADDRESS);
JumpToApplication();//跳转到应用程序
}
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闪烁的程序工程
配置应用程序起始地址为0x08004000
生成.bin文件配置,配置完,编译的时候就会生成.bin文件
中断向量表偏移配置
主要代码如下:
#include "gd32f4xx.h"
#include "gd32f4xx_libopt.h"
#include "systick.h"
#define CLI() __set_PRIMASK(1)//关闭总中断
#define SEI() __set_PRIMASK(0)//打开总中断
int main(void)
{
SEI();
systick_config();//配置系统主频168M,外部8M晶振,配置在#define __SYSTEM_CLOCK_168M_PLL_8M_HXTAL (uint32_t)(168000000)
nvic_vector_table_set(NVIC_VECTTAB_FLASH, 0x4000);//中断向量地址偏移0x4000
rcu_periph_clock_enable(RCU_GPIOB);//使能GPIOB时钟
gpio_mode_set(GPIOB, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, GPIO_PIN_4);//PB4配置成输出
gpio_output_options_set(GPIOB, GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_4);//PB4配置成推挽输出,50M速度
while(1)
{
gpio_bit_set(GPIOB, GPIO_PIN_4);//PB4输出高电平
delay_1ms(100);//等待100ms
gpio_bit_reset(GPIOB, GPIO_PIN_4);//PB4输出低电平
delay_1ms(100);
}
}
实验效果:
BOOT程序:主要实现YMODEM协议以及内部FLASH编程,程序烧录完之后,由APP程序生成的APP.Bin文件烧录到APP程序的FLASH地址空间,再实现程序跳转。
1.Keil5打开BOOT工程,编译,并烧录BOOT程序。
2. Keil5打开APP程序,编译,生成template.bin文件,文件在工程Objects目录下。
3. 打开超级终端软件,配置好串口参数,115200波特率,无校验,硬件流控制选择无,COM口号是根据电脑自动识别,再点确认,完成超级终端打开。
4.板子重新上电,会有很多C符号提示
5.选择YMODEM传输,选择template.bin文件
6.选择文件传输后,进度条会有进度,而且最终屏幕显示Successfully,说明IAP升级成功,板子LED灯闪烁
调试代码,运行到下图断点处,超级终端处应该有GD32F407显示,否则需要排查串口配置,串口接线以及超级终端配置。
在Ymodem协议数据解析处设置断点,开始传送文件后,能进入到断点,否则需要排查超级终端的协议选择是否正确。
五.工程源代码下载
工程源代码下载如下:
CSDN
六.小结
在单片机应用中,在线升级功能是必不可少的,它可以让我们在不破坏硬件的情况下对程序进行升级和修正,提高了开发效率。