温湿度demo项目机智云官网设置

Tutorial: 卓越工程师 Category: 嵌入式 Published: 2026-10-23 11:05:29 Views: 0 Likes: 0 Comments: 0

168.3温湿度demo项目机智云官网设置

/* ==================== Inc/dht11.h:本节使用的已有 DHT11 接口 ==================== */
extern uint8_t Data[5];                              /* 声明由 dht11.c 定义的 5 字节接收数组,当前文件只引用它。 */
uint8_t DHT_Read(void);                              /* 声明 DHT11 完整读取函数;返回值表示本次校验是否成功。 */

/* ==================== Gizwits/gizwits_product.c:本节实际接入的业务代码 ==================== */
#include "dht11.h"                                  /* 引入 DHT_Read() 和 Data[] 的声明。 */

void userHandle(void)                               /* 机智云主循环会反复调用这个用户数据采集函数。 */
{                                                   /* 本次温湿度采集开始。 */
    DHT_Read();                                     /* 按课程原代码读取一次 DHT11;这里没有判断返回值。 */
}                                                   /* 本次用户数据处理结束。 */

源码范围说明:顶部只收录本节已确认接入的 DHT11 接口与读取调用。课程在下一节实机调试时明确说明,本节末尾曾把数据点赋值误放进 userInit(),而且下标尚未最终校正;因此这里不把下一节才完成的 Data[2] → temp、Data[0] → humi 提前写成本节源码。dht11.c、dht11.h 是从前面的 DHT11 实验整体复制过来的既有驱动,本节没有重新编写其完整实现;CubeMX 自动生成的 TIM1 初始化和机智云 SDK 内部协议函数也不冒充本节手写业务代码。课程原代码存在采样过快、忽略 DHT_Read() 返回值等问题,正文会指出,但不在顶部代码中擅自修复或补全。

一、机智云平台的设置

本节完成“平台建产品 → 定义温湿度数据点 → 生成 STM32 工程 → 接入 DHT11”的最小流程。各部分的职责是:

DHT11 ──单线时序──> STM32F103 ──UART/机智云协议──> ESP8266 GAgent
                                                        │
                                                     2.4 GHz Wi-Fi
                                                        │
                                                   机智云与调试 App
  • STM32 读取传感器并维护 currentDataPoint。
  • 机智云生成代码负责数据点编码、协议收发和 Wi-Fi 状态事件。
  • ESP8266 运行上一节烧入的 GAgent 固件,承担 Wi-Fi 与云端连接。
  • 平台中的数据点定义会直接影响生成的结构体成员、取值范围和协议长度,所以网页配置与 MCU 代码必须来自同一个产品版本。

1.1 机智云开发者中心

1.1.1 登录与注册机智云开发者中心

课程通过搜索“机智云”进入官网,再打开开发者中心并登录;没有账号时先注册。视频只说“好像需要实名”,没有实际展示实名认证流程,因此不能把它整理成必然步骤。复现时按当前开发者中心的实际提示完成账号、手机号或身份验证即可。

机智云网页和菜单名称可能随平台版本变化。本节应以视频录制时的操作逻辑为主,不要求新版页面与旧视频逐像素一致。

1.1.2 个人项目与产品创建

课程创建产品时采用以下选择:

项目课程设置含义
设备类别其他不套用现成家电模板,自行定义功能
自定义方案其他建立自定义温湿度产品
产品名称温湿度检测便于在开发者中心识别
产品类型单品、Wi-Fi/ESP8266一只产品通过 Wi-Fi 模组接入云端
数据点协议形式变长使用机智云变长产品数据点协议
功耗类型正常按课程的常规在线设备方案创建

“变长”不是 C 语言变长数组,也不是 UART 字符长度或波特率。它是产品数据点协议的生成选项;本地初始代码包的 Changelog.txt 也明确记录了 SDK 对“变长产品数据点变化上报”的支持。生成的 MCU SDK、产品定义和 GAgent 协议必须配套,不能在拿到代码后随意改成另一种产品类型。

1.1.3 数据点的编辑与定义

数据点是设备能力在云端的抽象。一个数据点至少包含:

  • 标识名:生成 C 变量、宏和协议字段时使用,建立后不应随意改名。
  • 显示名:App 或管理页面给用户看的名称。
  • 读写类型:只读数据由设备上报;可写数据还可以由 App/云端下发控制。
  • 数据类型:布尔、枚举、数值等。
  • 范围与分辨率:决定允许的显示值以及设备值与协议值的换算。

温度和湿度是两种不同物理量,因此要建立两个数据点,而不是把“温湿度模块”只建成一个字段。

1.1.4 例题1: 温湿度检测项目的数据点设置

课程创建的数据点可由生成的 gizwits_protocol.h 反向核实:标识是 temp 和 humi,而不是 OCR 笔记中的 humidity、timepr 等错误写法。

标识名显示名读写类型数据类型最小值最大值分辨率
temp温度只读数值0601
humi湿度只读数值01001

生成代码中的对应关系是:

平台标识用户区结构体成员协议宏
tempcurrentDataPoint.valuetemptemp_MIN=0、temp_MAX=60、temp_RATIO=1
humicurrentDataPoint.valuehumihumi_MIN=0、humi_MAX=100、humi_RATIO=1

需要区分“平台允许范围”和“传感器规格范围”:

  • 把温度数据点设成 0~60 ℃,只是允许协议和 App 表达这段范围,不会让 DHT11 的实际测量范围、精度或分辨率提高。
  • DHT11 的课程驱动只使用整数部分,因此平台分辨率设为 1 与本 Demo 相符。
  • 视频提到 DS18B20 的 0.125 ℃,这是其 11 位转换分辨率对应的量化步长;DS18B20 还可选择 9、10、12 位,步长分别为 0.5、0.25、0.0625 ℃。所以 0.125 ℃ 不是它能达到的最高分辨率。
  • 传感器检测量应设为只读;灯、舵机等需要 App 下发控制的执行器,才通常设为可写或使用合适的控制型数据点。
1.1.5 开发向导与模组选择

在模组信息中,课程选择乐鑫 ESP8266 方案并保存。这里要纠正两个容易混淆的名称:

  • 乐鑫(Espressif)设计 ESP8266 芯片。
  • 安信可(Ai-Thinker)生产多种采用 ESP8266 芯片的模组,例如课程使用的 ESP-01S。

所以“安信可就是乐鑫模组”并不严谨;本节实际是让平台生成适配 ESP8266 GAgent 的产品配置,实物仍要核对完整模组型号。

视频中的密码、前缀等字段暂时保持当时页面的默认或既有设置。新版平台若要求用户填写,必须根据字段说明设置,不能照抄视频账号中的值。产品信息中的 Product Key 用于标识产品,Product Secret 用于鉴权;课程配套源码会把两者写入生成文件,尤其不要在截图、公开仓库或聊天中泄露 Product Secret。一旦真实密钥已经公开,应在平台侧按当前功能更换或重新创建凭据,而不是只把本地字符串遮住。

1.1.6 MCU与App开发资源概览

课程使用官方调试 App,不在本节自行开发 App,主要工作放在 MCU 端。平台设置与 MCU 开发的关系是:

  1. 网页定义产品和数据点。
  2. 平台按数据点生成 MCU 协议框架。
  3. 开发者移植传感器驱动,并在用户接口中填入采集值或执行控制动作。
  4. MCU SDK 与 GAgent 通过 UART 交换协议帧,GAgent 再负责云连接。

自动生成并不等于“所有功能已经完成”。平台能生成字段、序列化、解析和事件框架,却不知道 DHT11 接在哪个 GPIO,也不知道怎样读取实际硬件;这些仍由开发者实现。

1.2 产品开发

1.2.1 选择硬件方案与平台

课程选择“独立 MCU 方案”:STM32 是主控,ESP8266 是联网模组,两者通过串口连接。它与 SoC 方案的差别是,业务控制和传感器采集仍运行在独立 STM32 上,而不是把所有逻辑都放进 ESP8266。

平台模板选择 STM32F103C8x。课程开发板实际使用 STM32F103ZET6,视频说“C8T6 代码可以直接跑在 ZET6 上”需要加上严格限制:

  • 本节生成的 .ioc 明确指定 STM32F103C8Tx、LQFP48;Keil 工程选择 STM32F103C8,宏为 STM32F103xB,启动文件也是 startup_stm32f103xb.s。
  • STM32F103ZET6 属于不同容量等级和 LQFP144 封装,Flash/RAM、启动文件、链接地址、可用外设和引脚数量都不同。
  • 同属 STM32F1 使不少 HAL 业务源码容易迁移,但不能把 C8 工程生成的二进制直接当作 ZE 工程烧录。正确做法是在 CubeMX 中选择真实 MCU,迁移机智云 Gizwits、Hal、Utils 和用户驱动,重新核对时钟、引脚、启动文件、容量宏、链接布局与下载算法,再编译生成目标芯片固件。

课程随后填写当前产品的生成凭据并生成代码。实际操作时应从自己的产品页面复制,绝不能复用课程作者或他人的 Product Key/Product Secret。

1.2.2 生成与下载代码

数据点保存后,在 MCU 开发中选择独立 MCU、STM32F103C8x,点击生成并等待下载代码包。生成器会依据当时的数据点定义写出:

  • dataPoint_t 中的字段;
  • 数据点变化标志和事件;
  • 数值范围、比例和偏移宏;
  • 数据打包、解析与上报框架;
  • STM32 HAL、UART、定时器和按键示例工程。

如果之后增加、删除或改名数据点,应重新生成或严格按新协议同步修改工程;只改网页、不更新 MCU 代码,会导致结构体和协议布局不一致。

1.2.3 下载与解压文件

课程把下载包保存到自己建立的“物联网项目/温湿度test”目录,解压后再把工程目录改成容易识别的名字。磁盘盘符和个人目录不是程序要求,可以按自己的工程管理习惯放置,但应避免中文超长路径、同名多层目录和解压后仍直接在压缩包内编辑。

本仓库同时保留:

  • 温湿度demo机智云初始代码包.zip:平台刚生成、尚未接入 DHT11 的基线。
  • 温湿度test/:课堂加入 TIM1、DHT11 驱动和 userHandle() 赋值后的工程。

对照两者可以确认本节真正新增的内容,而不是根据转录猜代码。

1.3 单片机代码详解

1.3.1 项目文件结构

原笔记中的“help/H 库”“JAVA 文件”“Qt 打开”都是语音识别错误。该代码包是 STM32 HAL 工程,主要结构如下:

目录/文件作用
STM32F103C8x.iocSTM32CubeMX 工程配置,不是 Java 文件
MDK-ARM/STM32F103C8x.uvprojxKeil MDK-ARM 工程入口
Drivers/CMSIS 与 STM32F1 HAL 驱动
Src/、Inc/CubeMX 生成的主程序和外设 .c/.h,本节还放入 DHT11 驱动
Gizwits/机智云产品接口与协议实现
Hal/机智云示例使用的按键、Flash 等硬件适配
Utils/环形缓冲、数据点工具、MD5 等公共组件
Bootloder/配套 Bootloader,名称沿用代码包中的拼写

课程在 MDK-ARM 目录打开 Keil 工程;.ioc 则使用 STM32CubeMX 打开,两个文件用途不能混淆。

1.3.2 主函数与初始化

生成工程的 main.c 初始化顺序包括:

  1. HAL_Init() 与系统时钟。
  2. GPIO、TIM2、USART1、USART2;本节配置后又生成 MX_TIM1_Init()。
  3. timerInit()、uartInit()、userInit()、gizwitsInit() 和 keyInit()。
  4. 主循环中先调用 userHandle(),再调用 gizwitsHandle(&currentDataPoint)。

两路串口职责不同:

串口引脚波特率用途
USART1PA9/TX、PA10/RX115200 bit/sGIZWITS_LOG/printf 调试日志
USART2PA2/TX、PA3/RX9600 bit/sSTM32 与运行 GAgent 的 ESP8266 通信

上一节烧录工具中的 115200 bit/s 是“电脑串口下载工具 ↔ ESP8266 ROM Bootloader”的下载速率;本节 USART2 的 9600 bit/s 是“STM32 ↔ 已启动 GAgent”的运行时协议速率。它们是两个不同阶段的链路,不能据此推断 ESP8266 在烧写结束后自行把同一串口参数从 115200 改成了 9600。

TIM2 已由生成工程用于约 1 ms 的周期中断,在回调中执行按键扫描和 gizTimerMs();本节另加 TIM1,只为 DHT11 的微秒延时,二者职责不同。

1.3.3 按键功能与模式切换

配套源码给两个按键分配了四种操作:

按键操作调用作用
KEY1 短按gizwitsSetMode(WIFI_PRODUCTION_TEST)进入产测模式
KEY1 长按gizwitsSetMode(WIFI_RESET_MODE)重置 Wi-Fi 配置
KEY2 短按gizwitsSetMode(WIFI_SOFTAP_MODE)进入 SoftAP 配网模式
KEY2 长按gizwitsSetMode(WIFI_AIRLINK_MODE)进入 AirLink 配网模式

视频口述“按键 1 进入产品模式、按键 2 进入 AP、长按进入 AirLink”只概括了部分行为;上表以实际生成源码为准。KEY1 为 PB10、KEY2 为 PA8,并且源码配置为低电平按下。

1.3.4 用户初始化与数据处理

正确函数名是 userInit(),不是 OCR 中带下划线的 user_init。生成代码会先用 memset() 把 currentDataPoint 整体清零,并在注释中为每个数据点保留初始化占位提示。

userInit() 适合放设备上电后只做一次的用户初始化;userHandle() 则位于无限循环中,会反复执行。两者不能按名称相似就互换:把传感器每次采样放进 userInit() 只会执行一次,把耗时很长的初始化放进 userHandle() 又会阻塞通信处理。

1.3.5 用户处理函数

正确函数名是 userHandle()。平台根据两个只读数据点生成:

dataPoint_t
├─ uint32_t valuetemp
└─ uint32_t valuehumi

开发者在 userHandle() 中更新这两个成员,紧随其后的 gizwitsHandle() 再完成协议处理和需要的状态上报。不能简单理解成“每执行一次赋值就立刻向互联网发送一次完整结构体”;实际是否上报还由 SDK 的变化检测、协议状态和通信流程决定。

课程还打开了完整物联网项目的 gizwits_protocol.h 作对比,其中会出现烟雾、两路灯、窗帘、两组温湿度和光照等更多字段。这说明结构体不是手工固定模板,而是由创建产品时的数据点集合生成。

机智云官方文档特别提醒:userHandle() 在 while 中执行很快,传感器采样应自行设置周期,不建议靠在函数中加入长阻塞延时降频。本节原样代码没有实现这一点,学习时必须知道这是最小接线演示,不是完善的调度设计。

1.3.6 温湿度检测实现

课程复用前面 STM32 DHT11 实验中的 dht11.c 和 dht11.h。驱动固定使用 PA5,并把 40 位结果按下列顺序存入 Data[5]:

下标内容完成工程中的用途(下一节最终校正)
Data[0]湿度整数部分是,写入 valuehumi
Data[1]湿度小数部分否
Data[2]温度整数部分是,写入 valuetemp
Data[3]温度小数部分否
Data[4]校验和驱动内部比较

下一节完成数据点赋值后,课程原实现仍存在三项重要限制:

  1. DHT_Read() 的返回值没有被判断;下一节加入赋值后,即使本次应答或校验失败,仍会把 Data[2]、Data[0] 写入数据点。
  2. DHT_Read() 被放在快速主循环中,源码每次又包含 18 ms 起始低电平。DHT11 不允许这样连续高频获取新测量值,读取间隔应至少约 1 s。
  3. 配套驱动以 uint32_t sum 直接和单字节校验值比较,没有显式只保留前四字节和的低 8 位;在总和超过 255 时可能误判。

这些问题不应偷偷改进后冒充课堂源码;应先用原代码理解数据通路,再在后续优化中增加采样节拍、成功判断和正确校验处理。

1.3.7 定时器配置与微秒延时

DHT11 的位判决需要几十微秒级时序,HAL_Delay() 只能提供毫秒级延时,因此复用驱动依赖 TIM1 构造 Delay_us()。本节不是在 userHandle() 中“初始化定时器”,而是在启动阶段由 main.c 调用 CubeMX 生成的 MX_TIM1_Init();userHandle() 只调用已经可用的 DHT11 驱动。

TIM1 使用内部时钟,不配置 PWM、输入捕获或外部定时器引脚。驱动会在每次 Delay_us() 中写计数器、启动、轮询到目标值后停止。

1.3.8 CubeMX配置与初始化

课程先在 CubeMX 的 Project Manager → Code Generator 中勾选“为每个外设生成独立的 .c/.h 文件”,对应工程配置中的 ProjectManager.CoupleFile=true。重新生成并在 Keil 中 Reload 后,可直接看到 gpio.c、tim.c、usart.c 等文件,便于查引脚与移植驱动。

TIM1 的最终配置以 .ioc 和生成的 tim.c 为准:

参数值
Clock SourceInternal Clock
Prescaler71
Counter ModeUp
Counter Period65535
Clock DivisionDIV1
Repetition Counter0
Auto-reload preloadDisable

原笔记把最后一项识别成“DC 包/One Pulse Mode”,并把 Disable 解释成“只触发一次”,这是错误的。Auto-reload preload=Disable 表示 ARR 不使用预装载缓冲;它不负责把定时器变成单脉冲。此驱动之所以每次只计一小段时间,是因为 Delay_us() 显式启动 TIM1,达到目标计数后又显式停止。

二、定时器的初始化

系统时钟为 72 MHz,TIM1 位于 APB2,工程中 TIM1 时钟为 72 MHz。STM32 定时器预分频器的实际分频系数是 PSC+1:

$ f_{CNT}=\frac{72\ \text{MHz}}{71+1}=1\ \text{MHz} $

因此计数器每加 1 约经过 $1\ \mu s$。Period=65535 让 16 位计数器从 0 数到 65535;若连续运行,一圈约为:

$ T=\frac{65536}{1\ \text{MHz}}=65.536\ \text{ms} $

CubeMX 生成 tim.c/tim.h 后,还会在 main.c 外设初始化区加入 MX_TIM1_Init()。它必须在首次 DHT_Read() 前执行;只把 dht11.c 加进 Keil 而没有初始化 TIM1,Delay_us() 就没有可靠的计数基础。

配套 Delay_us() 把 CNT 起点设为 0xFFFF-us-25,轮询到 0xFFFF-25 后停止。两边同时减 25 不改变目标计数差,us 个计数仍约等于 us 微秒;这里的 25 是课程实现中避开溢出边缘的偏移,不应理解成又减去了 25 μs。

三、文件的添加

本节的文件迁移步骤是:

  1. 从前面的 09DHT11 工程复制 dht11.c 到当前工程的 Src/。
  2. 复制 dht11.h 到当前工程的 Inc/。
  3. 在 Keil 相应分组中 Add Existing Files,把 dht11.c 加入工程构建。
  4. 头文件通常不需要作为编译单元加入分组,但 Inc/ 必须位于编译器 Include Paths 中。
  5. 在 gizwits_product.c 包含 dht11.h,使编译器看到函数和 Data[] 声明。
  6. 重新编译,确认不是误把另一份同名文件加入工程。

“头文件不用加”只表示 .h 不单独参与编译,绝不表示它可以不复制、不声明或不在包含路径内。C 文件才会独立编译成目标文件,头文件则在预处理阶段被 #include 展开。

CubeMX 再次 Generate Code 后,Keil 若提示外部文件已改变,应先保存自己的用户区代码,再 Reload 工具生成文件。自定义代码应尽量位于 USER CODE BEGIN/END 区或独立源文件中,避免重新生成时被覆盖。

四、温湿度文件的运用

dht11.h 对外提供两项本节直接使用的接口:

  • DHT_Read():发起一次完整读取,把结果写入 Data[5],返回读取/校验状态。
  • extern uint8_t Data[5]:声明数组定义在 dht11.c,让 gizwits_product.c 能读取同一块全局数据。

extern 不会再分配一份数组;真正的存储只由 dht11.c 中的定义创建。如果误在头文件中再次定义非 extern 全局数组,多个 C 文件包含它时可能出现重复定义。

本驱动没有单独的 DHT_Init()。这不等于完全不需要初始化:

  • GPIOA 外设时钟必须已经开启;生成工程的 MX_GPIO_Init() 提供了这个前提。
  • TIM1 必须先由 MX_TIM1_Init() 配置为约 1 MHz 计数。
  • PA5 的连接、上拉、电压和端口占用必须正确。
  • 每次读取时,驱动再把 PA5 在输出与输入模式之间切换。

五、温湿度文件的运用细节

DHT11 DATA 是双向共享线,课程驱动的控制权变化为:

PA5 推挽输出并拉低 18 ms
        │
        ├─ 拉高约 20 μs
        │
PA5 切为输入,DHT11 返回应答和 40 位数据
  • 输出阶段由 STM32 发起开始信号;输入阶段必须停止主动驱动,让 DHT11 能拉低或释放线路。
  • DATA 空闲高电平通常依赖上拉电阻。课程源码的 GPIO 配置结构体没有显式初始化所有成员,也没有明确设置 Pull;实物模块是否自带上拉必须查模块原理图或测量,不能仅凭“三针模块”外观推断。
  • 如果模块把 DATA 上拉到 5 V,还要确认 MCU 引脚是否允许该电压。DATA 上拉电压应优先保持在 MCU 可安全识别的 3.3 V 逻辑范围;传感器本身的供电则必须按所用模块手册选择。若模块把上拉电阻直接接到 VCC,就不能在没有核对原理图时一边用 5 V 给模块供电、一边假定 DATA 仍是 3.3 V。
  • DHT11 通信对微秒时序敏感。长中断、错误系统时钟、编译优化差异或接线电容过大,都可能让 40 μs 采样点失效。

课程驱动虽然给部分等待循环加了 retry<100,但没有把每一种超时明确向上传递;userHandle() 又忽略最终返回值。因此“编译通过”只证明语法和链接基本成功,不能证明传感器帧可靠、温湿度有效或云端已经收到正确值。

六、dh幺幺文件的运用

标题中的“dh幺幺”应读作 DHT11。本节先接通 DHT_Read();经过下一节纠正赋值位置和数组下标后,完整路径才是:

userHandle()
  └─ DHT_Read()
       └─ 更新 Data[0..4]
  ├─ Data[2] → currentDataPoint.valuetemp
  └─ Data[0] → currentDataPoint.valuehumi

gizwitsHandle(&currentDataPoint)
  └─ 比较/编码数据点并处理与 GAgent 的协议通信

最终版本中温度使用 Data[2]、湿度使用 Data[0],不能按“温度写在表格第一行”就把两个下标对调。这里上传的是整数部分,小数字节没有进入平台数据点;该映射在 169 课通过 App 实测后才定稿。

更稳健的产品代码应只在 DHT_Read() 成功且达到采样周期时更新数据点,并保留旧值或设置错误状态;但这是对课程代码的风险说明,不应把改写后的逻辑冒充成本节原源码。

七、产品的运用

本节结尾完成了头文件包含、DHT11 读取接入和 Keil 编译,并尝试添加数据点赋值;169 课实机调试表明,当时的赋值位置与下标还没有最终改对。视频没有继续展示 App 中的最终在线温湿度结果,因此不能仅凭“0 error”写成整条云链路已经验证成功。

真正验证产品至少要逐层检查:

  1. Keil 目标芯片、启动文件、容量宏和下载算法与实际 STM32 一致。
  2. DHT11 的 VCC、GND、PA5 DATA 与上拉正确,且两次有效采样间隔满足要求。
  3. USART2 的 PA2/TX 接 ESP8266 RX,PA3/RX 接 ESP8266 TX,双方共地并使用 9600 bit/s。
  4. ESP8266 已烧入与课程方案匹配的 GAgent 固件,并处于正常启动而不是下载模式。
  5. 当前 MCU 工程中的 Product Key/Product Secret 与平台产品匹配。
  6. 产品数据点仍是 temp、humi;若平台已修改,重新生成或同步协议代码。
  7. 使用 USART1 的 115200 bit/s 日志观察 MCU 初始化、Wi-Fi 状态和协议错误。
  8. 完成 SoftAP/AirLink 等配网后,确认模组连上路由器和云端,再观察 App 数据。

常见现象与定位方向:

现象优先检查
DHT_Read 标红或未定义dht11.h 是否在 Include Paths,dht11.c 是否加入构建
Data 未声明是否包含正确的 dht11.h,头文件中是否保留 extern 声明
程序卡在微秒延时TIM1 是否初始化、时钟是否为 72 MHz、句柄是否为 htim1
温湿度不变或乱跳PA5 接线、上拉、采样间隔、DHT11 返回值和校验
USART1 有日志但云端无数据USART2 接线/9600、GAgent 固件、配网和产品凭据
App 字段缺失或名称错误平台数据点与本地生成代码是否来自同一版本
C8 工程无法下载到 ZE 板重新按真实 STM32F103ZE 建工程并完成移植,不要直接烧 C8 二进制

参考资料