shuichan_old/V1.1/STM32F103_App1
helei 713d6ecca9 修复 After Build 复合命令引号嵌套导致 bin 可能不更新:拆成两条独立用户命令(建目录+fromelf) 2026-08-09 11:43:04 +08:00
..
.vscode 水产投料机与四路继电器二合一:V1.1/V1.2/V1.3(4G/WiFi双通道、GPS定位、双通道OTA) 2026-08-08 20:04:09 +08:00
Drivers 水产投料机与四路继电器二合一:V1.1/V1.2/V1.3(4G/WiFi双通道、GPS定位、双通道OTA) 2026-08-08 20:04:09 +08:00
HardWare 新增 V1.4(FreeRTOS 版);V1.1~V1.3 版本号统一为文件夹规则 2026-08-08 21:13:56 +08:00
Inc 水产投料机与四路继电器二合一:V1.1/V1.2/V1.3(4G/WiFi双通道、GPS定位、双通道OTA) 2026-08-08 20:04:09 +08:00
MDK-ARM 修复 After Build 复合命令引号嵌套导致 bin 可能不更新:拆成两条独立用户命令(建目录+fromelf) 2026-08-09 11:43:04 +08:00
Src 水产投料机与四路继电器二合一:V1.1/V1.2/V1.3(4G/WiFi双通道、GPS定位、双通道OTA) 2026-08-08 20:04:09 +08:00
m_app 水产投料机与四路继电器二合一:V1.1/V1.2/V1.3(4G/WiFi双通道、GPS定位、双通道OTA) 2026-08-08 20:04:09 +08:00
README.md 新增 V1.4(FreeRTOS 版);V1.1~V1.3 版本号统一为文件夹规则 2026-08-08 21:13:56 +08:00
keilkill.bat 水产投料机与四路继电器二合一:V1.1/V1.2/V1.3(4G/WiFi双通道、GPS定位、双通道OTA) 2026-08-08 20:04:09 +08:00

README.md

智能水产投料机 V1.1.0

工程概述

本工程基于 STM32F103RCT6 + STM32Cube HAL + Keil MDK,实现设备的控制、联网与投喂。

  • BootLoaderSTM32F103_BootLoader/,负责 OTA 固件烧录、试运行计数与失败回滚
  • 应用固件STM32F103_App1/,当前运行版本 V1.1.0

产品型号配置

同一套板子适配两种产品,编译前在 Inc/main.h 顶部通过一个宏二选一:

#define PRODUCT_WITH_FEED_SCALE   0
取值 产品 投喂行为
1 智能水产投料机(接 RS485 磅秤) feedingonceWt 定量自动投喂,带重量反增/超时保护;sw1 指令被忽略
0 基础四路继电器(不接磅秤) feeding / sw1 直接开关投喂继电器 1由平台侧自行计时

硬件接口

接口 用途 引脚 参数
USART1 蓝牙串口调试 / 日志输出 PA9/PA10 115200-8N1
USART2 RS485 喂料秤 PA2/PA3 9600-8N1
USART3 4G Air780E MQTT PB10/PB11 921600-8N1见下文波特率自适应
Relay1 投喂电机控制 PB7 高电平吸合
Relay2~4 备用控制 PB8/PC0/PC2 高电平吸合
I2C软件 AT24C02 配置存储 PA11/PA12 页写入
SPI软件 W25Q64 外部 Flash PB3/PB4/PA15/PB5 OTA 固件暂存
ADC1 电池电压检测 PA0 低电量上报 pow:0
PA8 手动模式检测 PA8 高电平=手动模式
PC13 LED2 心跳 / TPL5010 喂狗 PC13 每 1s 翻转(见下文看门狗)

看门狗(双狗架构)

看门狗 超时 喂狗点 作用
IWDG片内独立看门狗 ~26sLSI 漂移下最短 ~17s APP 主循环、app_bg_service()(所有 AT/OTA 等待每 20ms 让出一次BL 的擦除/拷贝循环 固件卡死 → 复位BL 启动OTA 试运行期计一次试错
TPL5010外置硬件狗 7 分钟 PC13(LED2) 电平翻转APP activeEvents() 每秒翻转BL 进入时及长循环中翻转 MCU 完全失控(含 IWDG 失效)时的最后兜底

注意LED2 同时承担运行指示和外部狗喂狗,修改 LED2 闪烁逻辑时必须保持周期性翻转,否则 7 分钟后整板复位。

4G 波特率自适应115200 → 921600

  • Air780E 出厂/自适应模式只覆盖 9600~115200921600 必须用 AT+IPR 固定。
  • APP 联网时永远先按 115200 探测(避免向自适应侦测窗口发送其他波特率的乱码):
    • 通 → 发 AT+IPR=921600 + AT&W 固化,切换到 921600日志出现 baud -> 921600
    • 不通(模块已固化 921600→ 直接切 921600 连接。
  • 切换由 USART3_SetBaudRate() 运行中重写 BRR 完成,不影响 GPIO/DMA/NVIC。

OTA 升级

流程

平台下发 iot.ota.upgrade.post(url, crc16, ver)
  → APP: 模块硬复位 → HTTP HEAD 拿大小 → 擦 W25Q64 槽 B
  → 3072 字节/块分段下载AT+HTTPPARA BREAK/BREAKEND + HTTPACTION + HTTPREAD
  → 每块写 W25Q64 并回读校验,全程累计 CRC16
  → 整包 CRC + 栈顶/复位向量校验 → 可靠上报 result.post等 OK + 重试)
  → 写 OTA 信息区(状态 DOWNLOADED) → 复位进 BootLoader
BootLoader:
  → 擦除备份槽 → 备份当前 APP1 到槽 A失败则中止更新留在旧版本
  → 擦 APP1 → 写入新固件 → CRC + 向量校验 → 状态 INSTALLED → 跳转
新 APP 试运行:
  → MQTT 就绪 + 首次发布成功 + 3s → 上报 confirm.post
  → 确认标志写 W25Q64回读验证 + 3 次重试)→ 版本号存 EEPROM
  → 下次启动 BL 清理 OTA 标志,升级完成

Flash 分区

内部 Flash256KB

区域 地址 大小
BootLoader 0x08000000 32KB
APP1 运行区 0x08008000 224KB

W25Q64 外部 Flash8MB

区域 地址 用途
槽 A 0x000000 (256KB) 当前固件备份(回滚镜像)
槽 B 0x040000 (256KB) 新固件下载暂存
信息区 A/B 0x080000 / 0x081000 (各 4KB) OTA 状态双副本magic + 序号 + checksum

健康检查与回退

新固件"健康" = 4G 附着 → MQTT 连接 → 首次发布成功。回退判据(3 次真实故障才回退):

  • 看门狗复位IWDG/WWDG固件真的卡死→ BL 计一次试错(读 RCC->CSR 判别);
  • 健康超时MQTT 就绪 1 分钟仍无首次发布)→ APP 复位前自行计一次;
  • 上电/按键/普通软件复位不计次——现场断电、断网不会误触发回退;
  • 试运行期间完全断网 → 不计次、不回退,持续等待网络恢复。

掉电/断网安全性

故障点 结果
下载中断网/掉电 信息区未更新,旧固件照常运行,平台可重推
BL 备份阶段掉电 backup_valid=0,重启后重新备份
BL 擦写 APP1 中途掉电 状态 COPYING 视为 DOWNLOADED,重启后完整重做(备份不会被坏镜像覆盖)
备份创建失败 中止更新,留在旧版本,下次重启重试
回退过程掉电 重启后重新执行回退拷贝

其他要点

  • OTA 触发时先强制停止投喂(断开继电器 1
  • 下载期间蓝牙命令、秤轮询、LED 心跳、电量检测照常运行(bg_delay 切片让出),业务状态上报暂时屏蔽
  • 分段接收按 +HTTPREAD: <len> 声明长度收齐即处理无静默等待40KB 固件全程约 38 秒(含模块复位重连 ~15s

主要功能

  1. MQTT 联网Air780E 4G 模块,通过 MQTT 连接云平台
  2. 远程控制:平台下发开关 sw1/sw2/sw3/sw4feeding 指令(投料机模式下 sw1 被忽略,投喂只能走 feeding
  3. 自动投喂PRODUCT_WITH_FEED_SCALE = 1
    • 平台设置单次投喂量 onceWt
    • 平台下发 feeding=true 启动投喂
    • 继电器 1 吸合RS485 秤实时读取重量
    • 当重量减少 ≥ onceWt 时自动停止,带重量反增与 10 分钟超时保护
  4. 去皮/标定:平台设置 zero 空载重量,设置后按最新原始重量重新计算剩余料重并立即上报
  5. OTA 升级:见上文专题
  6. 蓝牙调试USART1 支持 RESET/INFO/STATUS/R1ON~R4OFF/ALLON/ALLOFF/OTA/RECOVERY/HELP 等命令
  7. 掉电安全:继电器 2~4 联网后从 EEPROM 恢复上次状态;继电器 1 上电强制断开,防止误投料

平台数据字段

字段 含义 说明
ver 固件版本号 每次状态上报携带,平台可据此识别设备是否发生回退
weight 空载重量 平台显示为去皮重量 zero
zero 剩余料重 平台显示为当前剩余料重
onceWt 单次投喂量 每次投喂目标重量
feeding / sw1 第一路继电器状态 1=投喂中0=停止,两个标识符等效
sw2~sw4 备用继电器状态 0/1
pow 电量状态 1=正常0=低电量

下行报文由 m_app/proto.c 按键名定位解析("sw2" 不会误中 "sw21",其他字段里的 true/false 不会造成误判)。

串口调试命令

通过 USART1蓝牙串口发送

RESET    - 系统复位
INFO     - 打印 MQTT 配置
STATUS   - 打印继电器状态
R1ON     - 打开继电器 1
R1OFF    - 关闭继电器 1
R2ON/R2OFF ~ R4ON/R4OFF - 控制继电器 2~4
ALLON    - 打开所有继电器
ALLOFF   - 关闭所有继电器
OTA      - 复位进入 BootLoader 检查升级
RECOVERY - 复位进入 BootLoader
HELP     - 显示支持命令

代码分层APP

main.c 只保留三行初始化和主循环,逻辑全部下沉到模块:

int main(void)
{
    SCB->VTOR = FLASH_BASE | Application_1_Addr;
    Board_Init();       // 基础硬件HAL/时钟/GPIO/DMA/UART/看门狗SYSINIT/system.c
    app_init();         // 业务模块初始化SYSINIT/app_loop.c
    app_loop_init();
    while (1) {
        IWDG_Feed();
        app_loop_process();   // 业务调度SYSINIT/app_loop.c
    }
}

目录结构

STM32F103_App1/
├── Src/
│   └── main.c              # 仅 VTOR + Board_Init/app_init/主循环
├── Inc/
│   └── main.h              # 产品型号宏 PRODUCT_WITH_FEED_SCALE、MQTT 默认配置
├── m_app/
│   ├── proto.c/h           # 平台 JSON 报文取值工具(按键名定位)
│   └── ota_app.h           # Flash 分区定义BootLoader 32K + APP1 224K
├── HardWare/
│   ├── USER_CMD/           # 蓝牙串口命令处理
│   ├── FEED_SCALE/         # RS485 喂料秤与定量投喂状态机
│   ├── RELAY/              # 继电器控制、反馈检测、平台指令执行
│   ├── NETWORK/            # 网络抽象层:状态上报/响应/发送互斥
│   ├── CAR/                # Air780E AT 驱动、波特率自适应、OTA 下载
│   ├── UART/               # USART1/2/3 初始化、运行中切波特率、中断/DMA 接收
│   ├── 24C02/              # EEPROM 读写(页写入)
│   ├── IIC/                # 软件 I2C
│   ├── ADC/                # 电池电压检测
│   ├── LED/                # 指示灯LED2 兼任 TPL5010 喂狗)
│   ├── LOG/                # 分级日志USART1 输出)
│   ├── SYSINIT/            # Board_Init/IWDG、app_init、主循环调度、后台任务切片
│   └── W25Q64/             # 外部 Flash 驱动与 OTA 双槽信息区
└── MDK-ARM/                # Keil 工程文件

STM32F103_BootLoader/
├── Src/main.c              # 硬件初始化 + IWDG + 入口
├── m_app/ota_boot.c        # OTA 状态机:备份/烧录/试运行计数/回退/跳转
├── HardWare/W25Q64/        # 外部 Flash 驱动(与 APP 共用设计)
├── HardWare/LOG/           # 分级日志(与 APP 相同)
└── MDK-ARM/                # Keil 工程文件

编译烧录

Keil GUI

  1. 打开 MDK-ARM/STM32F103rb_App1.uvprojxBootLoader 工程同理)
  2. 按产品形态设置 Inc/main.h 中的 PRODUCT_WITH_FEED_SCALE
  3. 编译下载(先烧 BootLoader 到 0x08000000APP1 起始地址 0x08008000
  4. 首次启动会自动把默认 MQTT 配置写入 AT24C02

命令行CI/脚本可用):

# 编译Keil 安装目录的 UV4-r 全量 -b 增量,-o 输出日志)
/c/Keil_v5/UV4/UV4.exe -r -j0 STM32F103rb_App1.uvprojx -o build.txt

# 烧录J-Link 命令脚本,工程 MDK-ARM 目录下有现成的 jlink_flash.jlink
JLink.exe -CommanderScript jlink_flash.jlink

版本

  • Version: 1.1.0
  • Author: Helei