一种有趣的 OTA 升级思路(基于 LoRa 通信的 OTA 固件升级的调试记录)

文章介绍了在采用LoRa通信技术的产品中,为加速OTA固件升级,通过将库文件放在Bootload并封装成共享函数,减少APP区域固件大小的优化方法。详细阐述了如何使用IAR工具实现函数和变量的绝对地址定位,以及在Bootload和APP中调用这些共享函数的实现步骤,并提到了调试中遇到的问题及解决策略。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1 概述

  采用 LoRa 技术进行无线通讯,考虑到产品的实际需求,增加了产品的 OTA 固件升级的功能。因为 LoRa 通讯速度较慢,合理的减小 APP 区域固件的大小加快固件升级的速度变的尤为重要,于是就开启了优化调整 APP 区域固件大小之旅。
  代码中使用到了 STM32_Cryptographic_Library、STM32_Std_Library 和 LoRa 驱动库,这些库编译之后的体积较大,猜想能不能将所有的这些库文件放在 Bootload 进行固化,然后封装好接口供 APP 调用,顺着这个思路开启了优化之路。

2 调试之路

2.1 想法

  常见的固件升级是将片内 Flash 分为 Bootload 区域和 APP 区域(如下图所示),由 APP 区域接收新固件存储在片内或者片外 Flash,然后置升级的标志位并跳转到 Bootload,在 Bootload 完成新固件的更新工作。这样实现比较常规,但是由于 APP 中包含了多种库导致目标文件比较大,LoRa 通讯速率又不高会使整个升级时间很长。
screenshot_image.png
  为了减小 APP 的大小,考虑将使用到库文件都固化在 Bootload 内,将片内 Flash 分为三个区域(如下图所示),增加一个共有函数区域,用于存放 Bootload 中封装好的接口。在函数调用时,如果 APP 调用的是共有函数,那么首先去共有函数区域找到函数在 Flash 中的地址,然后到 Bootload 中的对应位置执行相应的代码,再讲执行结果返回给 APP 区域,整个调用过程如下所示。
screenshot_image.png

2.2 函数和变量定义在绝对地址的实现

  有了上面的想法,首先需要验证的是如何将函数和变量放置在 Flash 的固定位置处,这样每次在调用固定位置处的接口就能找到 Bootload 中固化的代码接口。查阅相关资料,了解到 IAR 中的具体实现如下:

2.2.1 IAR的扩展关键字

  @ 用于函数变量的绝对地址定位,将函数变量等放到指定的 section
  __no_init禁止系统启动时初始化变量
  __root 保证没有使用的函数或者变量也能够包含在目标代码中

2.2.2 函数的绝对定位

  要将函数定义在绝对位置,需要在函数定义时后面加上 @".section_name",例如:

void fun1(int a, int b) @".MY_SECTION"
{
    ...  // 函数内容
}

  然后在链接文件 .icf 中添加如下内容。其中 0x08010000 表示在 Flash 中的地址,.MY_SECTION 必须与函数 @ 后面双引号中内容一致

place at address mem:0x08010000 { readonly section .MY_SECTION};

2.2.3 变量的绝对定位

  示例如下,变量绝对定位,无须修改 .icf 链接文件,直接指定具体位置即可。

__no_init char array1[100]@0x2000B000;

2.2.4 常量的绝对定位

  常量的绝对定位示例如下:

__root const int str1[4]@".MYSEG" = {1, 2, 3, 4};

  常量绝对定位,需要改.icf文件,示例如下:

place at address mem:0x08018500 { readonly section .MYSEG};

2.2.4 .c文件的绝对定位

  要将 test.c 文件定位到 Flash 的绝对地址,那么在 .icf文件中应该按照如下格式添加:

place at address mem:0x08018000 { section .text object test.o };

  编译完成后整个 test.c 文件的所有函数,都在 0x08018000 之后。

2.3 Bootload 共有函数的实现

  考虑到在初期编写代码时共有函数是可能发生变化的,如果按照上述的方法一个一个将函数放在固定的位置不是很方便,因此采用数组的方式将所有的共有函数放置在一起,如下所示:

__root const uint32_t func_table[]@".COMMON_FUNC_SEG" = {
    (uint32_t)&fun1,  /** 00 */
    (uint32_t)&fun2,  /** 01 */
    (uint32_t)&fun3,  /** 02 */
}

  按照上面数组的方式将所有共有函数集合在一起,然后再 .icf 链接文件中将该数组放置在固定位置处,这样在 0x08010000 位置处依次就能找到定义的所有共有函数(每个成员是函数对象的地址,占 4 个字节)。

/** 将数组放置在固定位置 */
place at address mem:0x08010000 { readonly section .COMMON_FUNC_SEG};

2.4 APP 共有函数的使用

  按照上述的方法可以将所有的库函数封装好并固化在 Bootload 中,并且实现了将所有的共有函数接口放置在固定的位置,在 APP 区可以使用函数指针的方式进行访问,示例如下:

/** 1. 声明 */
typedef int (*app_fun1)(int a, int b);
typedef void (*app_fun2)(void);
typedef char *(*app_fun3)(char *p);

/** 2. 定义函数指针类型的变量 */
app_fun1 fun1;
app_fun2 fun2;
app_fun1 fun3;

/** 3. 共有函数的重定义 */
#define FUNC_TABLE_ADDR (0x08010000)  /** 共有函数的首地址 */
void redefine_common_function(void)
{
    uint32_t *func_table_addr = (uint32_t *)FUNC_TABLE_ADDR;

    fun1 = (app_fun1)func_table_addr[0];    /* 00 */
    fun2 = (app_fun2)func_table_addr[1];    /* 01 */
    fun3 = (app_fun3)func_table_addr[2];    /* 02 */
}

  通过上面的方式就能在 APP 区域调用 Bootload 中固化的接口了,不过要注意这种方式调试起来不是很方便,需要前期验证好 Bootload 中封装的接口有没有问题。

3 注意事项

  按照上述的方法操作时有一些注意事项如下:
  1. 固件更新区的绝对定位的函数,不能随意调用其他库函数,那些被调用的函数也必须是绝对定位的。
  2. 绝对定位的函数,如果要使用常量,那么被使用的常量也必须是绝对定位的。
  3. 绝对定位的函数,如果要使用全局变量,那么被使用的常量也必须是绝对定位的,而局部变量则不受此限制。

4 调试坎坷之路

  上面的想法很有新意,在调试时自己封装的接口文件也经过了验证,但是在 APP 调用共有函数时程序还是跑飞了,经过不断的分析现实线现象,找到了问题的根源所在。STM32 标准库在进行时钟配置时定义了两个全局的数组如下,由于开始没有注意到这两个全局数组,而这两个全局数组是在 Bootload 区域定义的,跳转到 APP 区域后会对栈空间重新初始化,原本放这两个数组的位置就被初始化其他数值了,到时时钟配置出错。

/** stm32f10x_rcc.c */
static __I uint8_t APBAHBPrescTable[16] = {0, 0, 0, 0, 1, 2, 3, 4, 1, 2, 3, 4, 6, 7, 8, 9};
static __I uint8_t ADCPrescTable[4] = {2, 4, 6, 8};

  分析后的解决办法如下,因为这两个全局数据需要在 Bootload 区域中使用,而 Bootload 需要进行固化,所以需要将这两个数组放置固定的位置,这样每次使用到该数组时就回去固定的位置找,就不会出现被误修改的情况了。修改方式如下:

__root const uint8_t APBAHBPrescTable[16]@".AHBAPB_PRESC_TABLE"={0, 0, 0, 0, 1, 2, 3, 4, 1, 2, 3, 4, 6, 7, 8, 9};
__root const uint8_t ADCPrescTable[4]@".ADC_PRESC_TABLE"={2, 4, 6, 8};

/** 对应的修改 .icf 文件 */
place at address mem:0x08010000 { readonly section .AHBAPB_PRESC_TABLE};
place at address mem:0x08010010 { readonly section .ADC_PRESC_TABLE};

5 补充

  上述讲解了在 Bootload 和 APP 中共有函数的定义和使用,怎么验证是不是将其定义在绝对地址了呢?我们可以查看编译后生成的 map 文件,如下所示,可以看到在 map 文件中可以找到定义的 section。

screenshot_image.png

<think>好的,我现在需要帮助用户设计一个基于LoRa的智慧园区环境监测系统的云平台方案。首先,我要回忆一下LoRa技术的特点,比如低功耗、长距离传输和强穿透能力,这些都很适合园区环境监测。然后,用户提到的云平台需要整合数据,进行实时监控和智能分析。 接下来,我需要考虑系统的整体架构。通常物联网系统会分感知层、传输层、平台层和应用层。感知层使用各种传感器,比如温湿度、PM2.5、噪音等,这些传感器通过LoRa模块传输到网关。传输层方面,LoRa网关负责收集数据并通过5G或者以太网上传到云平台。这里可能需要结合引用[3]中的设计,比如使用OLED显示和LoRa传输,确保数据实时显示和远距离传输。 然后是平台层的设计,需要数据接入、存储、处理和分析。数据存储可能需要时序数据库,比如InfluxDB,处理部分需要边缘计算进行初步分析,比如异常检测。智能分析部分可以结合AI模型,比如预测空气质量,这里可能需要引用[1]中的数字孪生技术,实现动态预测和优化。另外,引用[1]还提到联邦学习,用于数据共享同时保护隐私,这点在园区间数据整合时很重要。 应用层需要可视化界面,比如大屏展示,告警系统和API接口,方便与其他系统集成。安全方面,数据加密和权限管理是必须的,尤其是涉及用户隐私的部分,引用[3]提到LoRa传输的安全性,需要进一步考虑云平台的安全措施。 技术选型方面,云平台可以用阿里云或AWS,数据库用InfluxDB,可视化用Grafana,AI框架用TensorFlow或PyTorch。优化部分要考虑LoRa节点的低功耗设计,网关负载均衡,可能需要引用[2]中的5G技术来增强传输能力,尤其是在高密度设备情况下。 最后,需要参考相关的规范,比如引用[1]中的智慧园区物联网平台接口规范,确保设计符合标准。同时,考虑未来扩展性,比如与AIoT和大模型结合,实现自主决策,这也是引用[1]提到的趋势。 可能遇到的问题包括数据传输延迟、数据安全、设备管理维护等,需要在这些方面给出解决方案。例如,使用边缘计算减少延迟,联邦学习保护隐私,定期设备巡检等。 总结下来,设计方案需要分层次,结合LoRa的优势,整合云平台的功能,并参考已有的引用资料中的技术和标准,确保系统高效、智能且安全。</think>### 基于LoRa的智慧园区环境监测系统云平台设计方案 #### 一、系统架构设计 系统分为**感知层、传输层、平台层、应用层**四层架构: 1. **感知层** - 部署LoRa传感器节点,监测环境参数: - 空气质量(PM2.5、CO₂、VOC)[^3] - 温湿度、光照强度 - 噪声、水质(pH值、浊度) - 节点设计:采用低功耗MCU(如STM32L4)+ LoRa模组(SX1276),支持电池供电与太阳能补充。 2. **传输层** - LoRa网关部署:覆盖半径3-5km(视园区地形),支持多信道并行接收数据。 - 数据传输协议:采用MQTT over TLS,通过5G/光纤回传至云平台[^2]。 3. **平台层** - **数据接入**:支持百万级设备并发连接,基于Kafka实现数据流分发。 - **数据存储**:时序数据库(InfluxDB)存储实时数据,关系型数据库(PostgreSQL)存储设备元数据。 - **数据处理**: - 边缘计算:网关端预过滤异常数据(如突增PM2.5值) - 云端分析:通过AI模型预测空气质量趋势(参考数字孪生动态建模[^1]) - **智能服务**: - 告警引擎:阈值触发(如CO₂ > 1000ppm)或复合条件告警 - 联邦学习:跨园区数据协同训练模型,保护隐私[^1] 4. **应用层** - 可视化大屏:GIS地图展示设备状态,历史数据曲线对比 - 移动端APP:推送告警信息,远程控制设备采样频率 - API接口:对接园区管理系统(如能源调度、安防系统) #### 二、关键技术实现 1. **低功耗优化** - 传感器节点采用**自适应采样策略**: $$ T_{interval} = \begin{cases} T_{base} & \text{环境稳定时} \\ T_{base}/10 & \text{检测到突变值} \end{cases} $$ - 网关使用**负载均衡算法**动态分配信道资源[^3] 2. **数据安全** - 节点与网关间采用AES-128加密 - 云平台通过RBAC权限模型控制数据访问 3. **智能分析** - 构建LSTM模型预测空气质量: $$ \hat{y}_t = f(x_{t-1}, x_{t-2}, ..., x_{t-n}) $$ 输入特征包括气象数据、设备状态、历史污染值 #### 三、部署方案示例 ```python # 伪代码:云端数据处理流水线 def data_pipeline(): # 从MQTT Broker接收数据 raw_data = mqtt_consumer.subscribe("sensors/#") # 数据清洗 cleaned_data = spark_streaming.apply( lambda x: validate(x) # 剔除异常值 ) # 写入时序数据库 influxdb_client.write(cleaned_data) # 实时分析 if detect_anomaly(cleaned_data): trigger_alert() # 每小时批量训练模型 @scheduler.schedule(hour=1) def retrain_model(): historical_data = influxdb_client.query("24h") model.update(historical_data) ``` #### 四、标准化建议 1. 遵循《智慧园区物联网平台接口规范》定义设备接入协议 2. 采用OPC UA统一数据模型实现多系统互联 3. 建立设备生命周期管理模块,支持OTA固件升级
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值