乐于分享
好东西不私藏

沁恒微蓝牙空中升级说明(二、应用细节说明)

沁恒微蓝牙空中升级说明(二、应用细节说明)

沁恒微蓝牙空中升级补充说明    ...... 矜辰所致

前言

之前我们写过一篇文章,讲解了一下沁恒微蓝牙空中升级流程,当时我们是基于官方给定的示例进行的测试说明,在实际应用中,我们有可能是先基于普通的不带 OTA 的示例进行的我们 APP 开发,然后再添加 OTA 功能,博主自己测试时候也发现有一些需要注意的地方。

所以本文我们对沁恒微的空中升级流程做一些补充说明 以及 基于普通例程开发的 APP 如何实现带 OTA 功能。

相关博文:沁恒微蓝牙无线升级(OTA)测试说明沁恒微 RISC-V 芯片开发工具 MounRiver Studio 使用

CH58x 蓝牙主机扫描相关应用(扫描到广播包)

我是矜辰所致,全网同名,尽量用心写好每一系列文章,不浮夸,不将就,认真对待学知识的我们,矜辰所致,金石为开!

本文目录一、 OTA 示例的补充说明├─ 1.1 OTA 各部分代码存放位置├─ 1.2 带库升级示例说明└─ 1.3 不带库升级示例说明二、 普通示例修改带 OTA 功能├─ 2.1 想要实现的 APP 效果├─ 2.2 JumpIAP 修改├─ 2.3 IAP 修改├─ 2.4 APP 修改│ ├─ 2.4.1 工程配置│ ├─ 2.4.2 Link.ld 修改│ ├─ 2.4.3 Startup_CH585.S 修改│ ├─ 2.4.4 增加 APP判断文件有效性标志│ └─ 2.4.5 添加Jump_OTA代码└─ 2.5 效果测试结语

一、 OTA 示例的补充说明

基础的流程说明请先过一遍 《 沁恒微蓝牙无线升级(OTA)测试说明 》 。

1.1 OTA 各部分代码存放位置

这里我在原始图区域图加上了示例代码位置,Flash 地址信息标注,大家直接看图。

带库升级 OTA 示例 Flash 存放示意图:

IAP 我们放在 Flash 的最后区域,而不是按照跳转流程顺序排放的。

不带库升级 OTA 示例 FLash 存放示意图:

这里就需要注意一下,对于 CH585 来说,示例里面 app 只给了 44K 空间,然后给最新的库预留了 192 K 的空间,库的存放地址为 0x0004 0000 。 所以还有 192K 的剩余空间。

为什么示例这里这么设计,是因为之前有一些芯片的 Codeflash 大小为 192K, 之前的蓝牙库和文档上面标注的一样为 128K, 这种布局正好放满 192 K 的 Codeflash ,如下图:

上面的 OTA 示例存放的位置在代码里面的 Link.ld 文件和跳转部分代码都是会有体现的,这个在本文后面会有说明。

通过上面我们知道了对于 Flash 大的芯片,我们是有很多剩余空间的,官方示例只有 44K 给 APP,应用中我们可以加大我们 APP 占用的空间以满足实际需求。这也正是我们下文要说明的重点之一。

1.2 带库升级示例说明

我们再来说明一下官方示例中两种升级方式中各部分代码做的工作,可以让大家能更好的明白为什么两种 OTA 方式在流程上的不一样,也便于大家在以后应用中选择适合自己的方式。

JumpIAP

工程: BackupUpgrade_JumpIAP

Flash 存放位置:从地址 0 开始,占 4K 空间,在链接文件 Link.ld 中设定如下

MEMORY{//代码存放位置,从 0开始,占 4K 空间    FLASH (rx) : ORIGIN = 0x00000000, LENGTH = 4K    RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 128K}

代码功能:跳转到 IAP ,里面什么都没做,只是在 startup_CH585.S 里面进行了跳转:

    .section    .init,"ax",@progbits    .global    _start    .align    1_start:    j    0x6D000

如需修改跳转地址,直接修改启动文件 startup_CH58x.s 中跳转指令 j 之后的地址。

IAP

工程: BackupUpgrade_IAP

Flash 存放位置:从地址 0x6D000 开始,占 12K 空间,在链接文件 Link.ld 中设定如下

MEMORY{//代码存放位置,从0x6D000开始,占 12K 空间    FLASH (rx) : ORIGIN = 0x0006D000, LENGTH = 12K    RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 128K}

代码功能:功能也比较简单直接,检查 IAP 标志位,看看是否要把 B 区代码搬运到 A 区,然后跳转到 A 区执行 APP。

if(CurrImageFlag == IMAGE_IAP_FLAG){    擦除 A 区    把 B 区内容 复制 到 A 区    把标志改成 IMAGE_A_FLAG    擦除 B 区}jumpApp();  // 跳去 A 区运行

APP(应用程序)

工程: BackupUpgrade_OTA

Flash 存放位置:从地址 0x1000 开始,占 216K 空间,在链接文件 Link.ld 中设定如下

MEMORY{//代码存放位置,从0x1000开始,占 216K 空间    FLASH (rx) : ORIGIN = 0x00001000, LENGTH = 216K    RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 128K}

代码功能:最终运行的应用程序,基于 Ble 从机示例修改的代码,是个从机设备,广播名为 "Simple Peripheral" 。代码中增加了与主机交互的 OTA 流程,所以通过沁恒微的 手机 APP <OTA升级工具> 可以直接扫描连接进行 OTA 流程。

1.3 不带库升级示例说明

JumpIAP

工程: OnlyUpdateApp_JumpIAP

Flash 存放位置:从地址 0 开始,占 4K 空间,在链接文件 Link.ld 中设定如下

MEMORY{//代码存放位置,从 0开始,占 4K 空间    FLASH (rx) : ORIGIN = 0x00000000, LENGTH = 4K//不带库的 RAM 区域有调整,留了一部分给协议栈使用    RAM (xrw) : ORIGIN = 0x20005000, LENGTH = 108K}

注意不带库 RAM 是从 0x20005000 开始,前面一部分留给了协议栈使用。

代码功能:和带库一样,只是调整,但是跳转的地址不一样,因为两种示例 IAP 的位置不一样:

    .section    .init,"ax",@progbits    .global    _start    .align    1_start:    j    0xC000

如需修改跳转地址,直接修改启动文件 startup_CH58x.s 中跳转指令 j 之后的地址。

IAP

工程: OnlyUpdateApp_IAP

Flash 存放位置:从地址 0xC000 开始,占 16K 空间,在链接文件 Link.ld 中设定如下

MEMORY{//代码存放位置,从0xC000开始,占 16K 空间    FLASH (rx) : ORIGIN = 0x0000C000, LENGTH = 16K    RAM (xrw) : ORIGIN = 0x20005000, LENGTH = 108K}

代码功能:检查是否是软复位 (软复位不跳 APP)和 检查 标志位判断是否需要跳转到 APP 代码执行,如果不跳转到 APP,需要进行 OTA, IAP 程序就会初始化从机功能,是一个 BLE 从机设备。基于 Ble 从机示例修改的代码,广播名为 "OTAOTA_OTAOTA_OTA" 。代码中增加了与主机交互的 OTA 流程,通过沁恒微的 手机 APP <OTA升级工具> 可以直接扫描连接进行 OTA 流程。

还有一点需要注意,因为库是分开的,需要用到蓝牙库,所以在 startup_CH585.S 文件里面最后部分有关联库地址的的汇编代码。

//startup_CH585.S 最后几行,库 hex 的存放位置为0x00040000    la t0, main    csrw mepc, t0    la a0, 0x00040000    jr a0  mret

对于不同的芯片固定库,这个跳转位置是不一样的,具体以对应芯片 EVT 包中的位置为准。

APP(应用程序)

工程: OnlyUpdateApp_Peripheral

Flash 存放位置:从地址 0x1000 开始,占 216K 空间,在链接文件 Link.ld 中设定如下

MEMORY{//代码存放位置,从0x1000开始,占 44K 空间    FLASH (rx) : ORIGIN = 0x00001000, LENGTH = 44K    RAM (xrw) : ORIGIN = 0x20005000, LENGTH = 108K}

代码功能:最终运行的应用程序,基本就是 Peripheral 示例一样的APP,广播名为 "Simple Peripheral" ,增加了跳转 OTA 升级流程的处理 Jump_OTA,但是本身不处理 OTA 流程 。

case SIMPLEPROFILE_CHAR3:        {uint8_t newValue[SIMPLEPROFILE_CHAR3_LEN];            tmos_memcpy(newValue, pValue, len);            PRINT("profile ChangeCB CHAR3..\n");            PRINT("jump OTA \n");            mDelaymS(5);            Jump_OTA();break;        }

在我们修改其他应用的时候,我们可以按照这个示例,把这部分代码复制过去,然后使用本示例的 IAP 流程进行空中升级 ,这下面会在示例中体现说明。

库文件

库文件位置如下,是已经做好的 hex 文件,烧录的时候直接选中对应芯片的库即可 :

使用固定库,需要定义一个宏 CH58xBLE_ROM = 1,工程代码如下:

二、 普通示例修改带 OTA 功能

经过上面的基础分析,我们基于不带库升级示例来实现一个应用。

2.1 想要实现的 APP 效果

把普通的主机示例 Central 修改成带 OTA 功能的,并且把 APP 程序空间加大 。

实现一个如下效果的 APP:

2.2 JumpIAP 修改

在 OnlyUpdateApp_JumpIAP 工程 上修改

通过计算可得,我们 IAP 的地址应该从 0x0003 C000 开始,所以我需要在 JumpIAP 的 startup_CH585.S 中跳转到这个地址:

    .section    .init,"ax",@progbits    .global    _start    .align    1_start:    j    0x3C000

2.3 IAP 修改

在 OnlyUpdateApp_IAP 工程上修改

因为 IAP 程序位置变了,我们需要在 IAP 的 Link.ld 文件中修改一下自己的起始地址:

MEMORY{    FLASH (rx) : ORIGIN = 0x0003C000, LENGTH = 16K    RAM (xrw) : ORIGIN = 0x20005000, LENGTH = 108K}

还有关键点,因为我们的 APP 例程变大了,我们的 IAP 负责 OTA 流程,接收新的 APP 程序,我们要修改一下定义 APP 区域大小的代码:

这个是根据我们自己想要给定 APP 区域大小来设置的,如果我们想 APP 是100K ,这里对应的 IMAGE_A_SIZE 就对应改成 100K 。

2.4 APP 修改

在 Central 工程上修改

2.4.1 工程配置

最关键的就是我们 APP 的修改,首先我们改成用固定库,在工程中添加宏 CH58xBLE_ROM = 1 :

可以看到改成使用库以后 Flash 和 RAM 占用都小了很多。

还有一个要注意的点,以前说过很多次的,因为原始的示例工程有很多共用文件,我们最好是把工程独立出来。

说明如下(我复制了 Central 一份,改了个名字,文件夹有小标志的就是使用的链接文件,是共用的):

如何不使用共用文件,请参考博文 《 沁恒微 RISC-V 芯片开发工具 MounRiver Studio 使用 》中第四章节:

对于本示例呢,我们只需要把 LD 和 Startup 改成自己的就行了,如果你不明白,全部独立出来也没问题,这样不会影响其他使用这两个文件的工程。

这里建议直接把示例 OnlyUpdateApp_Peripheral 中的 LD 和 Startup 文件夹直接拷贝过去,拷贝过去不要忘记工程还得配置一下不用链接文件了:

最后改完以后是这样的:

2.4.2 Link.ld 修改

这里我们修改,是基于上面使用 OnlyUpdateApp_Peripheral 里面现成的 ld 和 startup 文件进行的。

只需要修改一下 MEMORY :

MEMORY{    FLASH (rx) : ORIGIN = 0x00001000, LENGTH = 236K    RAM (xrw) : ORIGIN = 0x20005000, LENGTH = 108K/*    RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 96K *//*CH584*/}

2.4.3 Startup_CH585.S 修改

我们使用 OnlyUpdateApp_Peripheral 里面复制过来的 Startup_CH585.S ,就不需要修改。

2.4.4 增加 APP判断文件有效性标志

central_main.c文件中添加如下代码, 直接从 OnlyUpdateApp_Peripheral  里的 peripheral_main.c拷贝过来即可:

/* 用于APP判断文件有效性 */constuint32_t Address = 0xFFFFFFFF;__attribute__((aligned(4))) uint32_t Image_Flag __attribute__((section(".ImageFlag"))) = (uint32_t)&Address;

2.4.5 添加Jump_OTA代码

Jump_OTA 代码的实现可以直接按照示例中的来:

```c/* OTA 升级标志 */#define IMAGE_OTA_FLAG       0x03/* 存放在DataFlash地址,不能占用蓝牙的位置 */#define OTA_DATAFLASH_ADD    0x00077000 - FLASH_ROM_MAX_SIZE/* flash的数据临时存储 */__attribute__((aligned(8))) uint8_t block_buf[16];/********************************************************************* * @fn      Jump_OTA * * @brief   跳转OTA升级 * * @return  none */voidJump_OTA(void){uint16_t i;uint32_t ver_flag;/* 读取第一块 */    EEPROM_READ(OTA_DATAFLASH_ADD, (uint32_t *)&block_buf[0], 4);/* 擦除第一块 */    EEPROM_ERASE(OTA_DATAFLASH_ADD, EEPROM_PAGE_SIZE);/* 更新Image信息 */    block_buf[0] = IMAGE_OTA_FLAG;/* 编程DataFlash */    EEPROM_WRITE(OTA_DATAFLASH_ADD, (uint32_t *)&block_buf[0], 4);/* 软复位 */    SYS_ResetExecute();}

我们把上面一段直接放在central.c 代码最后面。

然后我们还要实现的就是触发 Jump_OTA 的操作,就是在哪里调用 Jump_OTA ,这里就是根据大家自己实际应用需求来,比如是一个按键按下,收到某个消息等。

博主这里测试,就简单点,只要扫描到某个固定的广播名称的从机设备 "888888 Peripheral" ,就进入 OTA 流程,此操作可以参考博主之前的博文 《CH58x 主机扫描事件相关应用(扫描到广播包)》,这里直接放一下对于代码,如下:

staticconstchar TARGET_NAME[] = "888888 Peripheral";voidJump_OTA(void);staticuint8_tscan_for_name(constuint8_t *pData, uint16_t dataLen){uint16_t pos = 0;while (pos < dataLen - 1) {uint8_t fieldLen = pData[pos];        // Lengthuint8_t fieldType = pData[pos + 1];   // AD Typeif (fieldLen == 0break;             // 安全终止/* 只处理 Short/Complete Local Name */if (fieldType == 0x08 || fieldType == 0x09) {uint8_t nameLen = fieldLen - 1;   // 去掉 type 字节char tmp[32];                     // 最长 31 足够if (nameLen >= sizeof(tmp)) nameLen = sizeof(tmp) - 1;memcpy(tmp, &pData[pos + 2], nameLen); // 拷出名字            tmp[nameLen] = '\0';              // 手工 '\0' 结尾if (strstr(tmp, TARGET_NAME))     // 子串匹配return TRUE;        }        pos += fieldLen + 1;                  // 跳到下一个 AD 结构    }return FALSE;}staticvoidcentralEventCB(gapRoleEvent_t *pEvent){switch(pEvent->gap.opcode)    {case GAP_DEVICE_INIT_DONE_EVENT:        {            PRINT("Discovering...\n");            GAPRole_CentralStartDiscovery(DEFAULT_DISCOVERY_MODE,                                          DEFAULT_DISCOVERY_ACTIVE_SCAN,                                          DEFAULT_DISCOVERY_WHITE_LIST);        }break;case GAP_DEVICE_INFO_EVENT:        {// const uint8_t *p = pEvent->deviceInfo.pEvtData;if (scan_for_name(pEvent->deviceInfo.pEvtData,pEvent->deviceInfo.dataLen)) {                    PRINT("Name matched: %s\r\n", TARGET_NAME);//找到名字做自己想做的事情,根据名字连接                    PRINT("jump OTA \n");                    mDelaymS(5);                    Jump_OTA();            }// Add device to list            centralAddDeviceInfo(pEvent->deviceInfo.addr, pEvent->deviceInfo.addrType);        }break;//......

OK ! 到这里,一个支持 OTA 的主机代码就完成了。

2.5 效果测试

本示例操作流程:

  1. 1. 烧录 4个 Hex 文件,测试板开始跑主机代码
  2. 2. 然后给广播名为 888888 Peripheral 的设备上电,主机扫描到就进入 OTA 流程,主机复位
  3. 3. 主机复位进入 IAP 程序,把自己变成一个广播名为 "OTAOTA_OTAOTA_OTA" 的从机设备等待手机连接
  4. 4. 手机通过 APP < OTA升级工具 >扫描到对应设备进行 OTA 操作(OTA 具体操作步骤参考上一篇博文 沁恒微蓝牙无线升级 (OTA)测试说明))。
  5. 5. 升级完成可直接观察到新的 APP 运行(如果新的 APP 进入 OTA 升级流程没变记得把进入 OTA 的条件去掉防止运行后又进入 OTA 流程,如果按照本示例就是关掉广播名为 888888 Peripheral 的设备。)

下面按照步骤测试,上一些图片说明:

程序正常运行:

做个新的 APP Hex 用作升级使用:

把待升级的 Hex 放手机对应文件夹中:

给广播名为 888888 Peripheral 的设备上电,使得需要测试的主机进入 OTA 流程:

然后打开手机 APP < OTA升级工具 > 按照步骤操作一遍:

打开 APP —> 扫描附件蓝牙设备 —> 选择 "OTAOTA_OTAOTA_OTA" 的蓝牙设备 —> GETINFO —> IMAGEA —> 选择 Hex 文件 —> 最下方 START—> 手动选择芯片类型(需要的话)

通过 Debug 可以查看过程:

OTA 升级成功,测试完成!

结语

好了,本文从实际应用出发,讲解了沁恒微蓝牙 OTA 流程更细节的一些东西,也展示了如何把一个普通的工程变成带 OTA 流程的示例。

虽然本文只展示也不带库的升级示例,但是只要明白流程,更简单的带库升级那应当也不在话下。

好了,本文就到这里。谢谢大家!

                分享💬 点赞👍 在看❤️ “三连”支持