本文基于GD32F303CGT6芯片 使用软件:
- KEIL5
- Tera Term
什么是IAP
对于芯片的程序烧录,一共三种方式:
ICP(in-circuit-programmer在电路编程)
ICP就是在工厂生产产品时,先把芯片烧录好后再去贴片
ISP(in-system-programmer在系统编程)
ISP就是芯片先贴片后烧录代码,也就是我们最常用的利用J-Link、DAP进行烧录代码的方式
IAP(in-application-programmer在应用编程)
IAP最常应用于出厂芯片的外部升级,通过已有的bootloader引导系统将程序烧录到对应的存储块上
什么是Xmodem
对于Xmodem,百度百科上是这样说的:
XMODEM协议是一种使用拨号调制解调器的个人计算机通信中广泛使用的异步文件运输协议。这种协议以128字节块的形式传输数据,并且每个块都使用一个校验和过程来进行错误检测。
其实就是一种传输协议,对你要传输的文件进行分包,通过串口、SPI、I2C、USB、BLE(低功耗蓝牙协议)、WIFI等各种各样的方式进行传输文件。 经过多年的更新迭代,Xmodem衍生出了一下几种系列协议:
- Xmodem-1k
- Xmodem-CRC
- Ymodem
Xmodem-1k
Xmodem-1k只是Xmodem的升级版,采用CRC校验,每个包的数据可达1k
Xmodem-CRC
Xmodem-CRC与普通Xmodem类似,采用CRC校验,每个包只有128字节
Ymodem
Ymodem与以上协议有一些区别,虽然都是Xmodem演变而来的,与Xmodem每一包都是文件的数据不同,Ymodem第一包发送的是文件名和文件大小,数据帧长度只有128字节,而它接下来的包有1k字节。(其余细小差别可百度)
Xmodem的帧格式
每个协议都会有各自定义帧格式的方式,Xmodem作为较早出现的数据传输协议(1978年),必然也有自己的一套帧格式
控制符
定义 | 取值 | 作用 |
---|
SOH | 0x01 | 128字节帧头标志 | STX | 0x02 | 1k字节帧头标志 | EOT | 0x04 | 发送结束标志 | ACK | 0x06 | 应答标志 | NCK | 0x15 | 非应答标志 | CAN | 0x18 | 取消发送标志 | CRC16 | 0x43 | CRC16校验帧 |
帧包格式(只说Xmodem)
BYTE1 | BYTE2 | BYTE3 | BYTE4-BYTE131 | BYTE132 |
---|
SOH | 包序列 | ~包序列 | 数据 | checksum |
传输流程
RECV
SEND
NCK
3S
NCK
|SOH|0x01|0xFE|DATA[0-127]|checksum|
checksum is OK
ACK
|SOH|0x02|0xFD|DATA[0-127]|checksum|
checksum is wrong
NCK
|SOH|0x02|0xFD|DATA[0-127]|checksum|
checksum is OK
ACK
...
...
|EOT|
ACK
RECV
SEND
以上为Xmodem传输流程,最开始的NCK为告知发送端接收端已经准备好,然后发送端开始按顺序发送数据,其中DATA[0-127]表示每一帧固定发送128字节数据,当剩余数据不够128字节时,一般用ctrlZ替代。 在发送过程中,如果某一帧数据传输错误,会导致checksum错误,这是接收端会发送NAK给发送端,发送接收到NAK后重新发送该帧数据。当所有数据发送完毕时,发送端发送一个EOT,接收端回复ACK表示整个发送过程技术。
GD32F30X的IAP使用方法
对于MCU,内部一般会有可擦除区域,这块区域可用于存储某些小块数据,亦或者可用于存储代码等,而IAP正是利用这块区域,或者说芯片的这一功能来对芯片内部代码进行修改的。 对于GD32F30X,其拥有一个叫做FMC(flash memory controller闪存控制器)的内部外设,该外设可对芯片内部flash进行擦写操作,参考芯片的用户手册以及数据手册之后可以对芯片的代码和数据分区,芯片擦写时间进行初步判断。 此图来源于GD32F30X用户手册,表明了GD32F30X各个系列内部flash的分区,对于IAP,我们主要操作的是图中的主存储闪存块 由图可知,主存储闪存块分为两部分,一部分是前256页,用户手册以及官方demo将其命名为bank0,另一部分为256-896页,用户手册以及官方demo将其命名为bank1 这两个bank的区别在于:
- 对于bank0,CPU指令操作零等待,此范围对于其他内存地址有操作延时小的优势
- bank0每一页的容量为2k,bank1每一页的容量为4k
- 对于主存储闪存块容量不大于512k的芯片,只有bank0
图中信息块为Boot loader区与我们自己写的bootloader不同,此为芯片内部引导芯片烧录方式以及执行方式的区域,芯片出厂时就已经定下来,用户不可擦写,可以理解为电脑的BIOS
芯片IAP方式
正如上文所述,芯片的IAP方式可通过串口、SPI、I2C、BLE、WIFI等各种各样的方式。我选用了最简单的串口+Xmodem的方式进行入门学习。
IAP流程
IAP的流程可以细分为很多中,在不管哪一种都离不开一个接收到数据后将数据写入芯片,并在写完之后将PC指针跳转到它该去的地方这个基本框架。 而为了芯片在烧录过程的安全性,避免因为一些非可抗力因素导致芯片升级失败后成为一块死芯片,这是在产品发布后,比如手机在用户手里进行系统升级时,决不可发生的事情。因此,为了保证升级过程绝对安全,一般会有各种各样的方式:
- 在芯片某一个闪存区写一个FLAG,当芯片开始升级时和升级成功后对FLAG进行相应的操作。
- 对芯片的烧录位置和bootloader代码进行严格的区分,保证他们井水不犯河水。
这里我两者都使用了,即在芯片需要进行升级时,先选择一块固定的闪存区,写一个FLAG进去,之后跳转到bootloader区,进行接收bin文件,在这个过程中即使中途因为断电停止接收了,芯片上电时,判断到FLAG区为正在升级时,也仍然会留在bootloader区重新开始升级。当芯片升级成功后,会跳转到APP区,并把FLAG擦除掉。这时即使重新上电,芯片也能成功跳转到APP区。
shut down and on
FLAG==UPGRADE
FLAG==UNPUGRADE
upgrade operation
recv
APP/bootloader
bootloader
APP
Tera Term
IAP时需要注意的点
- 对于Xmodem,在发送端发送数据前,一定要收到发送端的NAK,否则不会开始发送数据。
- 选定APP写入的地址,该地址应该是与bootloader井水不犯河水,所以我们在写完bootloader之后,需要计算bootloader一共占用芯片多少内存,之后APP生成bin文件前,要进行对应的地址偏移操作,以及FLASH-VTOR偏移(这个操作是告诉芯片应该去哪里找中断向量表,比如你把APP写在偏离主存储闪存区0x2000的地方,你的中断向量就应该偏移2000)。
- 第二点引申出最重要的这一点为,芯片在跳转时应该跳转到写入的地址+4,为什么要加4呢?这是因为写入的地址并非复位中断服务函数地址,而是中断向量表地址,我们在芯片复位时,PC指针应该是跳转到复位中断服务函数地址进行芯片复位,这样才能正常运行。
IAP学习过程中遇到的bug
- 在写芯片擦写过程的代码时,由于串口是以一个字节的形式发送数据的,而FMC在写数据的时候是以四个字节的形式写进去的,这个时候需要注意接收到的数据量与接收地址的数量关系,而我在写代码的时候,因为考虑到APP大代码量的情况,想到如果APP超过一页甚至超过bank1的时候怎么办?在判断数据时加了接收数据量这一判断操作,导致后续在判断是否超过一页时,使其跟写入地址混乱了。数据在写到四分之一的时候即跳到下一页开始擦写。
这个情况也让我学习到,在不知道数据是否写入正确时,可以利用J-FLASH查看写入的数据。
跳转代码
if(*(uint32_t *)UPGRADE_ADDR == UPGRADE_FLAG)
{
if(((*(__IO uint32_t *)0x08002000) & 0x2FFE0000) == 0x20000000)
{
LED_deinit(LED1);
usart_disable(USART0);
timer_disable(BASE_TIMER);
jUMP_ADDR = *(__IO uint32_t *)(0x08002000 + 4);
jump2App = (pfunction) jUMP_ADDR;
__set_MSP(*(__IO uint32_t *)0x08002000);
fmc_sector_erase(0x082FF000);
nvic_vector_table_set(NVIC_VECTTAB_FLASH, 0x2000);
jump2App();
}
}
|