CH58X_IAP功能的使用
- IAP(In-Application Programming,在应用编程)是指 MCU 在运行用户程序的过程中,利用芯片自身的 Flash 擦写能力和通信接口,对 Flash 中的程序或数据进行更新,设备不需要拆机、不需要外部编程器/调试器,就能自己完成固件升级;IAP是将Flash划分为两部分,两部分区域各存放一个程序,一个叫Bootloader(引导加载程序),另一个叫user application(用户应用程序),简称APP。在程序执行初始进入bootloader,在bootloader里面检测条件是否被触发(可通过按键是否被按下、串口是否接收到特定的数据、U盘是否插入等等),如果有则进行对APP进行擦除和重新写入操作,擦除用户代码(Old APP)并重新写入新的用户代码(New APP),如果没有则直接跳转到APP执行应用;
- IAP升级方式解决产品在安装外壳后无法接触调试口、应用运行过程中触发升级等场景中使用,调试时使用ISP升级方式
CH58X_IAP示例
- CH585 EVT中IAP文件夹内提供了IAP例程,例程有USB/USBHS/UART三种更新方式,本文以全速USB_IAP为例
![image]()
- flash分区:从link.ld和iap.h文件可以看到flash分区信息
| 区域 | 地址范围 | 说明 |
|---|---|---|
| 用户bootloader | 0x00000000-0x00001000(4KB) | |
| 应用程序 | 0x00001000-0x00070000(444KB) | jumpAPP() |
| RAM | 0x20000000-0x2001FFFF(128KB) | highcode |
- 示例中的IAP方式是直接擦除444KB的flash空间,没有备份和回滚的,但是用户bootloader不会擦除,而且擦除代码用highcode修饰,都在RAM中,所以升级失败可以重新升级;备份升级方案在BLE/BackupUpgrade_IAP中
由于沁恒官方并没有给出相关寄存器来实现IAP,只能在官方示例上直接修改代码实现自定义IAP功能
USB_IAP
- USB_IAP即用户bootloader,主要功能是在上电后运行用户bootloder,判断是否需要进入升级模式,如果需要,通过USB接收上位机发送的固件并写入flash,如果不需要,就直接跳转到APP中运行
- main()逻辑
/********************
* @fn main
*
* @brief 主函数
*
* @return none
*/
int main()
{
uint16_t i;
uint8_t s;
mySetSysClock(); //为了精简程序体积,该函数比普通库的初始化函数有修改,只可以将时钟设置为62.4M
//USE_EEPROM_FLAG默认为0
#if USE_EEPROM_FLAG
EEPROM_READ(IAP_FLAG_DATAFLASH_ADD, &p_image_flash, 4);//从dataflash读取标志
if ((p_image_flash.ImageFlag != FLAG_USER_CALL_IAP))
{
jumpApp();//跳转到APP
}
#else
//初始化引脚为上拉输入。为了减小程序大小,采用寄存器编写。
R32_PB_PD_DRV &= ~GPIO_Pin_4;
R32_PB_PU |= GPIO_Pin_4;
R32_PB_DIR &= ~GPIO_Pin_4;
DelayMs(10);
//读取PB4,通过PB4是否拉低来检测是否需要跳转到APP;跳转方式用户可以自定义
if (GPIOB_ReadPortPin(GPIO_Pin_4))
{
DelayMs(5);
if (GPIOB_ReadPortPin(GPIO_Pin_4))
{
//启动前判断是否进入iap,没有按键按下
jumpApp();
}
}
#endif
/* USB初始化 */
R8_USB_CTRL = 0x00; // 先设定模式,取消 RB_UC_CLR_ALL
R8_UEP4_1_MOD = RB_UEP4_RX_EN | RB_UEP4_TX_EN | RB_UEP1_RX_EN | RB_UEP1_TX_EN; // 端点4 OUT+IN,端点1 OUT+IN
R8_UEP2_3_MOD = RB_UEP2_RX_EN | RB_UEP2_TX_EN | RB_UEP3_RX_EN | RB_UEP3_TX_EN; // 端点2 OUT+IN,端点3 OUT+IN
R32_UEP0_DMA = (uint32_t)EP0_Databuf;
R32_UEP1_DMA = (uint32_t)EP1_Databuf;
R32_UEP2_DMA = (uint32_t)EP2_Databuf;
R32_UEP3_DMA = (uint32_t)EP3_Databuf;
R8_UEP0_CTRL = UEP_R_RES_ACK | UEP_T_RES_NAK;
R8_UEP1_CTRL = UEP_R_RES_ACK | UEP_T_RES_NAK | RB_UEP_AUTO_TOG;
R8_UEP2_CTRL = UEP_R_RES_ACK | UEP_T_RES_NAK | RB_UEP_AUTO_TOG;
R8_UEP3_CTRL = UEP_R_RES_ACK | UEP_T_RES_NAK | RB_UEP_AUTO_TOG;
R8_UEP4_CTRL = UEP_R_RES_ACK | UEP_T_RES_NAK;
R8_USB_DEV_AD = 0x00;
R8_USB_CTRL = RB_UC_DEV_PU_EN | RB_UC_INT_BUSY | RB_UC_DMA_EN; // 启动USB设备及DMA,在中断期间中断标志未清除前自动返回NAK
R16_PIN_CONFIG |= RB_PIN_USB_EN | RB_UDP_PU_EN; // 防止USB端口浮空及上拉电阻
R8_USB_INT_FG = 0xFF; // 清中断标志
R8_UDEV_CTRL = RB_UD_PD_DIS | RB_UD_PORT_EN; // 允许USB端口
R8_USB_INT_EN = 0; //禁止usb中断,采用查询方式
/* 进入highcode主循环 */
Main_Circulation();
}
/*********************************************************************
* @fn Main_Circulation
*
* @brief IAP主循环,程序放ram中运行,提升速度.
*
* @param None.
*
* @return None.
*/
__attribute__((section(".highcode")))
void Main_Circulation()
{
uint16_t j = 0;
while (1)
{
j++;
if (j > 5)//100us处理一次数据
{
j = 0;
USB_DevTransProcess();//采用查询方式进行usb操作,不使用中断。该函数接收上位机命令操作flash
}
DelayUs(20);
g_tcnt++;
if (g_tcnt > 3000000)
{
//1分钟没有操作,进入app
R8_USB_CTRL = RB_UC_RESET_SIE;
R16_PIN_CONFIG &= ~(RB_UDP_PU_EN | RB_PIN_USB_EN);
DelayMs(10);
jumpApp();//跳转进入用户程序
}
}
}
APP
- APP是用户程序,其与IAP相关的就是如何跳转到IAP程序相关逻辑
- main()逻辑
int main()
{
uint16_t i = 0;
uint8_t s = 0;
HSECFG_Capacitance(HSECap_18p);
SetSysClock(SYSCLK_FREQ);
/* 配置串口调试 */
DebugInit();
PRINT("Start @ChipID=%02x\n", R8_CHIP_ID);
/* app程序必须执行该语句,保证app更新失败时,下次依然运行IAP */
SwitchImageFlag(FLAG_USER_CALL_APP);
GPIOB_ModeCfg(GPIO_Pin_4, GPIO_ModeIN_PU);
while (1)
{
PRINT("i:%d\n",i);
i++;
DelayMs(10);
if (GPIOB_ReadPortPin(GPIO_Pin_4) == 0)
{
s++;
//连续两次检测到按键按下,跳转到IAP
if(s >= 2)
{
jumpToIap();//跳转到IAP
}
}
else
{
s = 0;
}
DelayMs(100);
}
}
- IAP和APP都有PB4跳转逻辑,IAP中PB4跳转是上电后检测是否需要升级,APP是运行过程中检测是否需要升级,跳转逻辑可以用户自定义,如读取某个寄存器或者蓝牙接收到某个特征值
ld文件
- IAP工程中分配了4KBflash大小,地址范围是0x00000000-0x00001000
MEMORY
{
FLASH (rx) : ORIGIN = 0x00000000, LENGTH = 4K
RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 128K
}
- APP工程起始地址就是0x1000,占用后面的444KB flash,IAP+APP为448KB(其他芯片根据flash大小修改)
- myDevEP2_OUT_Deal()中擦除地址从0x1000开始
MEMORY
{
FLASH (rx) : ORIGIN = 0x00001000, LENGTH = 444K
RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 128K
}
startup_ch585.s
- 还需要修改startup_ch585.s中红框部分,普通程序为0x88,用户模式;需要升级的APP以及IAP程序需要改成0x1888,机器模式(用户模式和机器模式是RISCV中的特权等级)
![image]()
WCH USB_IAP升级流程
- 烧录时需要同时烧录APP和IAP固件,APP和IAP的代码以及ld文件、startup_ch585.s文件需要按上面所描述做出修改
![image]()
- 烧录完成后例程代码为循环打印变量i的值,需要升级的APP程序则打印i+5的值
![image]()
- 需要升级的APP编译完成后使用EVT\EXAM\IAP\WCHMcuIAP_WinAPP\WCHMcuIAP_WinAPP\Release文件夹内的WCHMcuIAP_WinAPP程序
![image]()
- 根据代码中拉低PB4两次进入IAP升级的方式,将PB4接地2次,按下图中操作,升级APP程序,注意目标地址就是APP起始地址,操作完成后点击程序下载,直到提示IAP下载成功
![image]()
- 通过打印可以发现已经运行新APP
![image]()








浙公网安备 33010602011771号