【以太网摄像头采集存储终端】项目简介

1. 引言

项目来自 B 站 UP 主:<技术探索者>

这是一个基于 STM32H757XIH6 的双核摄像头采集终端:CM7(Cortex-M7,480 MHz)作为主核运行 FreeRTOS,驱动 OV5640 摄像头连续采集 800×480 图像;图像一路送 RGB LCD 本地预览,一路经硬件 JPEG 压缩后通过 HTTP 推流,在浏览器中直接观看;同时支持 SD 卡抓拍、USB 读卡器导出,以及双核 A/B 槽位 OTA 升级。CM4(Cortex-M4,240 MHz)作为从核,通过 OpenAMP 与 CM7 协作。

image

当前项目实现的功能:

  • 本地预览:OV5640 经 DCMI 连续采集,三缓冲换帧,LTDC 直接送显 RGB LCD。
  • 网络推流:硬件 JPEG 编码后以 MJPEG over HTTP 推流,浏览器打开页面即可观看,支持两个观看连接。
  • 抓拍导出:按键短按将当前画面保存为 BMP 到 SD 卡,长按切换 USB 读卡器模式,由电脑直接读卡导出。
  • 双核 OTA:固件经 HTTP 分块上传到空闲槽位,重启后试运行,失败自动回退,CM7 与 CM4 独立升级。
  • 双核协作:CM4 日志经 OpenAMP 回传 CM7,与 CM7 日志一起从 USART1 输出。

2. 功能总览

按功能域分组,每个域对应固件中一组相对独立的任务与驱动。

采集与显示。摄像头任务完成 OV5640 的输出格式、分辨率与图像质量配置后启动连续采集:DCMI DMA 先把像素写入 D2 SRAM 中的双行缓冲,再由 DMA2D 逐行搬运到 SDRAM 帧三缓冲;LTDC 在消隐期切换显示帧地址,画面更新不撕裂。采集链路出现 DMA 错误或长时间无新帧时,任务会停止采集并自动重启,恢复流程见系列 MJPEG 文章。

网络推流。HTTP 服务提供预览主页、/stream 图像流和 /status 状态查询三个入口。推流采用"一次编码、多端共享"的结构:一个编码任务持有完整 RGB565 帧完成颜色转换与硬件 JPEG 压缩,发布到三块输出缓冲;两个发送任务各自维护独立的观看连接,较慢的连接会跳过中间帧,只发送最新 JPEG。满员或 OTA 期间新的观看请求返回 503。

抓拍与导出。抓拍任务监听 KEY0(PA8):短按松开时把一帧完整画面复制到独立抓拍缓冲,编码为 0:/CAM0001.BMP~CAM9999.BMP 保存到 SD 卡;长按两秒在本地 FatFs 与 USB 读卡器模式间切换。两种模式由存储所有权机制保证互斥:卸载 FatFs 后才开放 USB,USB 停止后才重新挂载,避免主机与固件同时访问同一张卡。

OTA 升级。升级镜像经 HTTP 分块上传(每块最大 8192 字节),写入目标核当前未运行的槽位,CRC32 校验通过后在 EEPROM 登记 pending;复位后由 Bootloader 试启动新槽,试运行成功由应用提交 confirmed,失败自动回退旧槽。两核各自独立执行这套流程。

双核协作与日志。CM4 的全部应用逻辑围绕 IPC 展开:周期轮询 OpenAMP 消息、响应 CM4 槽位擦写握手、启动时发布运行槽报告。CM4 的 printf 输出经 rpmsg 回传 CM7,由 CM7 统一从 USART1 打印,双核日志在同一个串口终端里按前缀区分。

一帧 800×480 JPEG 平均约 70 KB,颜色转换加硬件编码合计约 52 ms(数据来自《HTTP MJPEG 浏览器推流》的串口日志实测,画面内容不同会有差异)。按编码耗时折合单端约 19 帧/秒。

9427c13115ff669071654ac083838141

3. 硬件平台

3.1 器件组成

整机的器件选型围绕"图像通路 + 网络通路 + 存储"三条线展开:

部分 器件 / 配置 用途
MCU STM32H757XIH6 双核 Cortex-M7 + Cortex-M4,2 MB Flash(双 Bank)、864 KB 各类 RAM
摄像头 OV5640 模块 DCMI 并口采集,SCCB(I2C4)配置,输出 800×480 RGB565
显示 RGB 接口 LCD LTDC + DMA2D 驱动,工作分辨率 800×480
以太网 LAN8720A RMII PHY,配合 LwIP 提供 HTTP 服务
SDRAM FMC 外接 32 MB 帧缓冲与 JPEG 工作缓冲,基地址 0xD0000000
存储 SD 卡 + 板载 EEPROM SD 卡(SDMMC2 + FatFs)保存抓拍;EEPROM 保存启动槽元数据
USB OTG_FS 读卡器(MSC)模式导出 SD 卡
调试与监控 ST-Link、USART1、IWDG1 烧录调试、日志输出、看门狗保活

3.2 时钟配置

时钟树由 CubeMX 配置生成,关键频率如下:

时钟 频率 说明
CM7 主频(SYSCLK) 480 MHz PLL1 输出
CM4 主频 240 MHz 与 HCLK 同源
AHB(HCLK) 240 MHz SYSCLK 二分频
APB1 / APB2 120 MHz HCLK 二分频

双 Bank 前提:CM7 与 CM4 的应用槽分别放在 Bank1 与 Bank2,两核各自独立擦写自己的 Bank,是双核 A/B 升级方案的基础。

4. 软件架构与数据流

4.1 双核分工

两个核的角色从一开始就定得比较清楚:CM7 独占所有高速外设与网络协议栈,CM4 保持轻量,只做通信配合。

核 角色 承担的工作
CM7(主核) 业务核心 摄像头采集与恢复、DMA2D/LTDC 显示、硬件 JPEG 编码、ETH + LwIP + HTTP、SD 抓拍、USB 导出、OTA 接收、看门狗喂狗
CM4(从核) 协作 OpenAMP 通信、CM4 槽位擦写时的 RAM 等待握手、运行槽报告、日志回传

4.2 任务组织

CM7 的任务在 Core/Src/freertos.c 中创建,汇总如下:

任务 职责
defaultTask 初始化 LwIP、启动 HTTP 服务,之后周期喂 IWDG 并翻转 LED
串口接收任务 响应串口输入,回报 CM7 / CM4 当前运行槽
IPC 任务 维护 OpenAMP 端点,把 CM4 回传的日志从串口打印
摄像头任务 OV5640 配置、采集监控与故障自动重启
抓拍任务 按键抓拍、SD 保存与 USB 模式的所有权切换
HTTP 主任务 接收连接、路由分发、状态查询、OTA 上传接收
MJPEG 编码任务 首次推流时创建:取帧 → 颜色转换 → 硬件 JPEG → 发布
发送任务 ×2 首次推流时创建:各自观看连接的独立发送与超时退出

CM4 只有一个 IPC 任务:启动时发布运行槽报告,之后每 100 ms 轮询一次 OpenAMP 消息并服务 OTA 握手。

4.3 从摄像头到浏览器的数据流

下图是整个终端的主数据通路,也是理解工程的最好入口。

flowchart TB subgraph BOARD["STM32H757XIH6 · 双核"] direction TB subgraph CM7["CM7 主核 · 480 MHz · FreeRTOS"] direction TB CAM["摄像头任务<br/>OV5640 配置 / 采集监控"] DCMI["DCMI + DMA<br/>D2 SRAM 双行缓冲"] D2D["DMA2D<br/>RGB565 搬运"] FB["SDRAM 帧三缓冲<br/>显示帧 / 采集帧 / 待显示帧"] MJPEG["MJPEG 编码任务<br/>RGB565→YCbCr420 + 硬件 JPEG"] JBUF["JPEG 输出三缓冲<br/>每块 512 KiB"] SEND["发送任务 ×2<br/>独立观看连接"] HTTP["HTTP 主任务<br/>页面 / 状态 / OTA"] SNAP["抓拍任务<br/>BMP 保存 / USB 导出"] IPC7["IPC 任务<br/>OpenAMP 端点 / 日志转发"] end subgraph CM4BOX["CM4 从核 · 240 MHz"] IPC4["IPC 任务<br/>OpenAMP 轮询 / OTA 握手"] end SHM["SRAM4 共享内存<br/>OpenAMP 59 KiB"] LTDC["LTDC<br/>消隐期换帧"] LCD["RGB LCD 800×480"] SD["SD 卡"] USBHOST["USB 主机<br/>读卡器模式"] PHY["LAN8720A"] end BROWSER["浏览器观看端"] CAM --> DCMI --> D2D --> FB FB --> LTDC --> LCD FB --> MJPEG --> JBUF --> SEND HTTP -->|"连接移交"| SEND SEND --> PHY --> BROWSER HTTP --> PHY SNAP --> SD SNAP --> USBHOST IPC4 <--> SHM IPC7 <--> SHM classDef core7 fill:#EAF3FF,stroke:#3973AC,color:#16324F classDef core4 fill:#FFF3DE,stroke:#BC8526,color:#573B12 classDef mem fill:#EDE9FA,stroke:#8064AD,color:#382653 classDef peri fill:#E5F4EB,stroke:#43865D,color:#20432E classDef ext fill:#F2F4F7,stroke:#788596,color:#293544 class CAM,DCMI,D2D,FB,MJPEG,JBUF,SEND,HTTP,SNAP,IPC7 core7 class IPC4 core4 class SHM mem class LTDC,LCD,SD,USBHOST,PHY peri class BROWSER ext

图像自上而下经过采集、缓冲、显示与编码发送两条支路;抓拍支路从帧缓冲取完整画面走 SD/USB;CM4 不参与图像通路,只经 SRAM4 共享内存与 CM7 交换消息。

4.4 软件栈

从 RTOS 到板级支撑,各层选用的组件如下:

层 组件
RTOS FreeRTOS(CMSIS-RTOS2 封装)
网络 LwIP + LAN8720A(RMII)
双核通信 OpenAMP / rpmsg(VirtIO Mailbox + HSEM)
文件系统 FatFs
图像与显示 DCMI、DMA2D、LTDC、硬件 JPEG + MDMA、TJpgDec(软件解码备用)
板级支撑 STM32H7 HAL(STM32Cube FW_H7 V1.13.0,CubeMX 生成)

5. 启动链与 OTA 升级

5.1 双核启动链

上电后由固定 Bootloader 完成选槽,再把两个核分别引到各自的应用槽,链路如下:

flowchart TB PWR["上电 / 复位"] --> BL["CM7 Bootloader<br/>0x08000000"] BL --> EE["读取 EEPROM 双备份<br/>confirmed / pending 元数据"] EE --> SEL{"选择 CM7 应用槽"} SEL -->|"pending 槽<br/>试启动最多 3 次"| APP7["CM7 应用<br/>A 或 B 槽"] SEL -->|"试启动失败"| BACK["回退 confirmed 槽"] BACK --> APP7 APP7 --> HO["CM7 应用 main()<br/>读 EEPROM 选 CM4 槽<br/>写 SRAM4 handoff 并使能 CM4"] HO --> STUB["CM4 固定 Stub<br/>0x08100000<br/>校验 handoff magic / 入口"] STUB --> APP4["CM4 应用<br/>A 或 B 槽"] APP4 --> RUN["双核运行<br/>OpenAMP 建立通信"] classDef boot fill:#EAF3FF,stroke:#3973AC,color:#16324F classDef app fill:#E5F4EB,stroke:#43865D,color:#20432E classDef cm4 fill:#FFF3DE,stroke:#BC8526,color:#573B12 class PWR,BL,EE,SEL,BACK boot class APP7,HO,RUN app class STUB,APP4 cm4

几条关键约定:

  • EEPROM 双备份:启动元数据在 EEPROM 的 0x0000 / 0x0100 各存一份 256 字节,读取时取合法且序号较大的一份,保存时轮换写入并读回校验。
  • 试启动与回退:pending 槽最多试启动 3 次,每次跳转前先扣减并保存计数;应用正常运行后主动提交 confirmed,用完次数仍未提交则回退旧槽。
  • CM4 由应用侧选择:Bootloader 只选 CM7 应用槽;CM7 应用启动后读 EEPROM 决定 CM4 槽位,经 SRAM4 的 handoff 区域(magic + 入口地址)通知固定 Stub 跳转,EEPROM 不可用时默认 CM4 A 槽。
  • 本地重刷登记:经 ST-Link 重刷 CM7 镜像后旧元数据 CRC 可能失配,按住 KEY0(PA8)两秒可重新登记当前镜像。

选槽校验、回退细节与本地升级协议在系列《双核系统 Bootloader》中展开。

5.2 OTA 升级链路

OTA 把"上位机上传 → 固件接收 → 擦写 → 重启确认"串成一条链,贯穿 qt-ota 上位机、HTTP 服务、OTA 模块和 Bootloader 四个部分:

flowchart TB HOST["OTA 上位机 / 浏览器<br/>选择目标核与镜像"] HOST -->|"1. /ota/status 查询"| SRV["CM7 HTTP / OTA 服务"] HOST -->|"2. /ota/begin 开始会话"| SRV HOST -->|"3. /ota/chunk ×N(每块 ≤8192 B)"| SRV HOST -->|"4. /ota/end 结束"| SRV SRV --> PREP["升级准备<br/>取存储锁 → 停止推流 → 暂停摄像头"] PREP -->|"目标为 CM7"| W7["擦写 CM7 空闲槽<br/>Bank1"] PREP -->|"目标为 CM4"| HOLD["cm4_ota_hold 握手<br/>CM4 关中断进入 RAM 等待"] HOLD --> W4["CM4 在 RAM 中<br/>擦写 Bank2 空闲槽"] W7 --> PEND["CRC32 校验<br/>EEPROM 登记 pending"] W4 --> PEND PEND --> RST["复位"] RST --> BOOT["Bootloader 试启动新槽<br/>成功 commit / 失败回退旧槽"] classDef host fill:#F2F4F7,stroke:#788596,color:#293544 classDef srv fill:#EAF3FF,stroke:#3973AC,color:#16324F classDef flash fill:#FFF3DE,stroke:#BC8526,color:#573B12 classDef boot fill:#EDE9FA,stroke:#8064AD,color:#382653 class HOST host class SRV,PREP srv class W7,HOLD,W4,PEND flash class RST,BOOT boot

两点值得注意:

擦写前先停业务:OTA 先取得存储占用,再停止 MJPEG 推流并等待发送任务退出、暂停摄像头,之后才开始擦写;失败或取消路径会恢复业务并释放占用。

CM4 擦写需要握手:CM4 从 Bank2 取指运行,擦写其空闲槽前必须让 CM4 经握手协议在 RAM 中关中断等待,全程由 .RamFunc 函数完成,结束后再恢复。

固件侧的 HTTP 入口如下,上传类接口均通过 X-Ota-Core 头区分目标核:

路由 用途
/ 预览主页,含图像标签与帧率轮询脚本
/stream MJPEG multipart 图像流,最多两个观看连接
/status JSON 状态:网络、运行槽、推流帧率
/ota/status 查询 OTA 状态与目标槽
/ota/begin /ota/chunk /ota/end /ota/abort 上传会话:开始、分块传输、结束、取消

上位机是一个 Qt 编写的 Windows 小工具,支持会话内断点续传与本地任务保存,具体实现见各专项文章。

5.3 内存布局

内存布局是这个项目里约束最多的一块:双核要隔离、DMA 要非缓存、A/B 槽链接地址不同。以下四张表覆盖了全部关键区域,数值均来自链接脚本、MPU 配置与源码常量。

Flash 分区(2 MB 双 Bank)

分区 地址范围 大小 内容
CM7 Bootloader 0x08000000~0x0801FFFF 128 KB 固定引导程序,尾部 16 字节保留给本地升级描述符
CM7 应用 A 槽 0x08020000~0x0807FFFF 384 KB CM7 应用镜像
预留 0x08080000~0x0809FFFF 128 KB —
CM7 应用 B 槽 0x080A0000~0x080FFFFF 384 KB CM7 应用镜像
CM4 Stub 0x08100000~0x0811FFFF 128 KB 固定跳转程序
CM4 应用 A 槽 0x08120000~0x0817FFFF 384 KB CM4 应用镜像
预留 0x08180000~0x0819FFFF 128 KB —
CM4 应用 B 槽 0x081A0000~0x081FFFFF 384 KB CM4 应用镜像

A/B 镜像不能混用:两核的 A、B 槽链接地址不同,镜像与槽位一一对应;两核各自使用核内槽索引 A=0、B=1。

内部 RAM(CM7 视角,含 MPU 缓存属性)

区域 地址 大小 缓存 用途
ITCM 0x00000000 64 KB — 指令 RAM(链接脚本保留)
DTCM 0x20000000 128 KB 开 CM7 私有数据,DMA 不可达
AXI SRAM(D1) 0x24000000 320 KB(链接脚本按 512 K 上限配置) 开 CM7 主 RAM:data / bss / 堆栈
D2 SRAM 0x30000000 288 KB 关 DMA 专用区,见下文细分
SRAM4(D3) 0x38000000 64 KB 关(共享) OpenAMP 共享内存与双核交接区

D2 SRAM 细分(地址从低到高):0x30000000 起 20 KB 内是 ETH DMA 描述符与 Rx 池,随后到 0x30025000 为 LwIP 堆(合计 148 KB);0x30025000 起是摄像头行缓冲(上限 12 KB);0x30028000 起的 128 KB 划给 CM4 作主 RAM,CM4 侧以别名地址 0x10028000 访问。ETH 与摄像头 DMA 区域关闭缓存,避免 DMA 与 CPU 缓存不一致。

SRAM4 细分布(64 KB)

地址 大小 内容
0x38000000 1 KB OpenAMP resource_table(NOLOAD)
0x38000400 59 KB OpenAMP 共享内存(rpmsg 缓冲)
0x3800F000~0x3800FFFF 4 KB CM4 启动 handoff(magic + 入口)、CM4 OTA 握手、运行槽报告

CM7 的 MPU 把整个 SRAM4 配置为共享、不可缓存;改这一段布局时必须同时核对两核的 OpenAMP 配置、四份链接脚本与 MPU。

SDRAM 关键区域(FMC 32 MB,0xD0000000 起,可缓存)

地址 大小 内容
0xD0000000 768000 B 显示帧,LTDC 直接读取
0xD0100000 — JPEG 解码工作区(MCU、RGB、输入缓冲)
0xD0300000 768000 B ×2 采集帧与待显示帧,两块连续
0xD0600000 — 编码用 YCbCr420 MCU 工作区
0xD0700000 512 KiB ×3 JPEG 输出三缓冲,至 0xD087FFFF
0xD0900000 768000 B 抓拍帧,独立于采集三缓冲

一帧 RGB565 为 800×480×2 = 768000 字节。采集帧安排在 0xD0300000 起的独立区域,避开 0xD0100000 起的 JPEG 工作缓冲;所有 CPU 与 DMA 交接处都保留了缓存维护操作。

6. 工程结构

仓库包含四个独立构建单元,没有根级统一构建入口:

子工程 内容
Bootloader/ CM7 Bootloader 与 CM4 Stub,出厂烧录后一般不再更新
BaseProject/ 双核 A/B 应用固件,本系列的主角
qt-ota/ Windows 端 OTA 上传上位机(Qt)
qt-camare/ Qt + OpenCV 的相机预览客户端,带 YuNet 人脸检测与 MobileFaceNet 表情识别,计划后续单独成文

BaseProject/ 内部按"生成代码 / 应用逻辑 / 板级驱动 / 共享代码"分层:

目录 说明
Makefile/ 两核各自的构建目录与四份 Flash 链接脚本,产物分 build/(A 槽)与 build_B/(B 槽)
CM7/Core/、CM4/Core/ CubeMX 生成的外设初始化代码,修改保留 USER CODE 区域
CM7/APP/ CM7 应用逻辑:BOOT(EEPROM 选槽)、CAMERA(采集 / 抓拍 / 存储 / USB)、JPEG、NET/HTTP(服务器与 MJPEG)、OTA、IPC
CM7/BSP/ 板级驱动:CAMERA、EEPROM、ETH、LCD、MPU、SDRAM 等
CM7/OPENAMP/、CM4/OPENAMP/ OpenAMP / HSEM mailbox 通信
CM4/APP/ CM4 应用逻辑,当前仅 IPC
Common/ 双核共享代码:槽位元数据、CM4 启动交接、OTA 握手、RAM 擦写、日志
Drivers/、Middlewares/ ST HAL、FreeRTOS、LwIP、FatFs、OpenAMP 等,不随意改动
tests/ 升级脚本模拟测试、JPEG 色彩回归、HTTP 实机测试

构建在 Windows PowerShell 中进行,使用 GNU Make 与 arm-none-eabi-* 工具链:make -C Makefile 默认构建双核 A 槽(先 CM4 后 CM7),make -C Makefile/CM7 APP_SLOT=B 构建指定核与槽位。四个 make_flash_cm*_slot*.ps1 脚本会构建、烧录指定槽并登记 pending 后复位。

7. 系列文章导航

系列按"通信 → 引导 → 采集 → 网络 → 升级 → 推流 → 存储"的顺序展开,建议按发布顺序阅读:

  1. 【STM32H757XIH6 双核】IPC 进程间通信 —— OpenAMP 多核异构处理器间通信框架配置:从零配置 OpenAMP,建立双核 rpmsg 通信。
  2. 【STM32H757XIH6 双核】IPC 进程间通信 —— CM7 printf 重定向到 OpenAMP 发送:CM4 日志经 OpenAMP 回传,双核日志同口输出。
  3. 【STM32H757XIH6 双核】双核系统 Bootloader:EEPROM 元数据、A/B 选槽、试启动与回退的完整引导方案。
  4. 【STM32H757XIH6 双核】驱动 OV5640 摄像头模块 + 三缓冲采集 + 双缓冲 DMA2D 搬运:摄像头采集链路与三缓冲显示机制。
  5. 【STM32H757XIH6 双核】驱动 LAN8720A 实现基础的 HTTP 服务:ETH、LwIP 与基础 HTTP 服务器的搭建。
  6. 【STM32H757XIH6 双核】HTTP AB 槽位 OTA:HTTP 分块上传与双核 A/B 槽升级确认。
  7. 【STM32H757XIH6 双核】HTTP MJPEG 浏览器推流:multipart 推流、双观看连接与慢端跳帧。
  8. 【STM32H757XIH6 双核】SD 抓拍存储以及 USB 设备导出:FatFs 抓拍与 USB 读卡器导出的所有权切换。

博客导航

博客导航

posted @ 2026-10-27 10:17  膝盖中箭卫兵  阅读(10)  评论(0)    收藏  举报
ORCID iD icon https://orcid.org/0000-0001-5102-772X