当前位置: 首页 > news >正文

《架构特别篇二:SYSTEM 层》

架构特别篇二:SYSTEM 层 — 硬件抽象的精髓在一张表里

换芯片、换底板——只改一个文件cc_h7_def.h。DMA 8 路串口不打架——靠dma.c里的一张映射表。.sct 分散加载——每一块内存放什么,编译期就决定了。这一章讲 SYSTEM 层的三个核心设计。


1. cc_h7_def.h:一张表统治所有硬件

大多数 STM32 项目的引脚分散在十几个文件里——GPIO 初始化在这里、UART 引脚在那边、CAN 引脚又在别处。换一块底板等于在代码库玩"找引脚"游戏。

煜坤的做法:所有硬件定义集中在一个文件

// cc_h7_def.h — 想知道任何引脚、任何外设配置,只查这里/* 串口引脚 (需要换底板? 改这里就行) */#defineCOM1_TX_PORTGPIOA#defineCOM1_TX_PINGPIO_PIN_9#defineCOM1_TX_AFGPIO_AF7_USART1#defineCOM1_RX_PORTGPIOA#defineCOM1_RX_PINGPIO_PIN_10#defineCOM1_RX_AFGPIO_AF7_USART1#defineCOM4_TX_PORTGPIOB// 雷达串口#defineCOM4_TX_PINGPIO_PIN_8// ... 8 路串口, 全部在这里/* CAN 引脚 */#defineCAN1_RX_PORTGPIOD#defineCAN1_RX_PINGPIO_PIN_0/* I2C 引脚 */#defineI2C_HW_SCL_PORTGPIOF#defineI2C_HW_SCL_PINGPIO_PIN_14/* PWM 引脚 */#definePWM1_CH1_PORTGPIOE#definePWM1_CH1_PINGPIO_PIN_9/* 电源口、继电器口、LED... */

为什么这是设计亮点?

  • 换底板:只改这一个文件。CONTROL 层 20 个任务、3 万行代码,一行不动。
  • 换芯片:文件头改#ifdef STM32H743xx→ 条件编译切换。我们从 H723 迁到 H743 用了不到一天。
  • 审图:硬件工程师和软件工程师只需要对这张表。不需要翻 10 个文件。

PX4 的做法board_config.h+.px4boardmanifest。两个文件。比我们多一层抽象,但也多一层理解成本。

APM 的做法AP_HAL_ChibiOS/hwdef/目录下每个板子一个子目录。灵活性最高,但一个人的项目维护多个板子目录是负担。


2. DMA 映射表:一张表解决 8 路串口冲突

STM32 的 DMA 在 F4 上是固定绑定——UART1_RX 只能用 DMA1_Stream5。H7 有 DMAMUX——灵活路由,但也意味着需要自己分配,分配不好就冲突。

我们的解法:一张编译期常量表

// dma.c — DMA 通道分配表 (编译期确定, 永不冲突)constDMA_ChannelConfig_t g_dma_channels[]={[COM1]={DMA1_Stream0,DMAREQ_ID_USART1_RX,41,DMA1_Stream0_IRQn,0},[COM2]={DMA1_Stream1,DMAREQ_ID_USART2_RX,43,DMA1_Stream1_IRQn,1},[COM3]={DMA1_Stream2,DMAREQ_ID_USART3_RX,45,DMA1_Stream2_IRQn,2},[COM4]={DMA1_Stream3,DMAREQ_ID_UART4_RX,63,DMA1_Stream3_IRQn,3},[COM5]={DMA1_Stream4,DMAREQ_ID_UART5_RX,65,DMA1_Stream4_IRQn,4},[COM6]={DMA1_Stream5,DMAREQ_ID_USART6_RX,71,DMA1_Stream5_IRQn,5},[COM7]={DMA1_Stream6,DMAREQ_ID_UART7_RX,79,DMA1_Stream6_IRQn,6},[COM8]={DMA1_Stream7,DMAREQ_ID_UART8_RX,81,DMA1_Stream7_IRQn,7},};// 用 [COMx] 数组索引 → DMA 初始化时自动查表 → 绑定
  • 数组索引是COMx枚举值 → 编译期检查 → 不会绑错
  • 8 个串口各占一个 DMA 流 → 互不冲突
  • 加第 9 个串口?表中加一行

3. .sct 分散加载:内存是编译期分配的

大多数嵌入式项目用默认链接脚本,堆和栈混在一起,谁踩了谁不知道。

我们用自定义.sct把内存分区:

// NexusMcu.sct — 内存分区表 (编译期确定)LR_IROM10x080000000x00200000{// 2MB FlashER_IROM10x080000000x00200000{// 代码段*.o(RESET,+First).ANY(+RO)// 所有只读数据}RW_DTCM0x200000000x00020000{// DTCM 128KB*(.dtcm_data)// 代价地图 + EKF + 任务栈}// AXI SRAM 512KB — DMA 缓冲区RW_AXI_ETH0x240000000x8000{*(.eth_dma)}RW_AXI_SPI0x240080000x8000{*(.spi_dma)}RW_AXI_USART0x240100000x10000{*(.usart_dma)}// FreeRTOS HeapHEAP_AXI0x240200000x20000{*(.heap_axi_dummy)}// 通用数据主区RW_AXI_GEN0x240400000x40000{.ANY(+RW+ZI)}// SRAM1/2/3/4 — 溢出 + DMA 专用区RW_SRAM10x300000000x20000{*(.i2c_dma)}RW_SRAM20x300200000x20000{.ANY(+RW+ZI)}// ...}

设计意图

  • DTCM 放最热的数据(代价地图 DWA 每秒查 12 万次)→ 零等待
  • AXI SRAM 放 DMA 缓冲(ETH/SPI/USART)→ DMA 可以访问
  • SRAM2 放溢出数据(lwIP 协议栈、Modbus 缓冲)→ 低频访问

换芯片需要改什么?H723 的 AXI 128KB → H743 的 AXI 512KB。.sct里 AXIGEN 的 Size 从0x10000(64KB) 改成0x40000(256KB)。一行改动。


4. 为什么这层没有"面向对象"?

PX4/APM 的 HAL 用 C++ 虚函数实现多态:

// APM 风格classUARTDriver{virtualvoidbegin(uint32_tbaud)=0;virtualsize_twrite(...)=0;};// 然后: AP_HAL_ChibiOS::UARTDriver, AP_HAL_Linux::UARTDriver...

煜坤不这么做,原因:

1. 目前只有一个硬件平台
虚函数的多态价值体现在"多个实现"。只有一个实现时,虚表是纯开销。

2. 纯 C + static inline 更快

// 煜坤风格: 零调用开销staticinlineuint16_tcomGetBuf(uint8_tport,uint8_t*buf,uint16_tlen){returnring_buf_read(&g_uart_devices[port].rx_rb,buf,len);}

编译后直接内联——没有函数调用、没有虚表查表。DMA ISR 里每微秒都要省。

3. C 结构体 + 函数指针同样能实现"多态"

// 如果需要多实现, 可以用函数指针表typedefstruct{void(*init)(void);uint16_t(*read)(uint8_t*,uint16_t);}UART_Ops;// STM32 实现:UART_Ops stm32_uart_ops={stm32_uart_init,stm32_uart_read};// 未来 GD32 实现:UART_Ops gd32_uart_ops={gd32_uart_init,gd32_uart_read};

需要跨平台时再加——而不是一开始就加。


5. SYSTEM 层的"黄金法则"

① 不包含任何业务逻辑 → 不在这一层判断"这个雷达目标是威胁" ② 不依赖 FreeRTOS → 可以在 ISR 中使用,但不创建任务 ③ 所有硬件定义集中 → cc_h7_def.h + .sct + dma.c 三张表 ④ static inline 优先 → 无函数调用开销,适合 ISR 高频场景 ⑤ 换芯片只改这一层 → CONTROL 层零改动

本文是《从零搭建无人船控制系统》架构特别篇。项目地址:[煜坤 YuKun](开源准备中)。

http://www.cnnetsun.cn/news/3236184.html

相关文章:

  • 高斯泼溅研发哪家强?TOP5机构数据排名揭秘
  • 海外新闻稿发了等于白发?朝闻通「增强发布」戳中了多少出海品牌的传播痛点
  • 天工AI DeepResearch技术解析:智能文档生成与办公自动化实践
  • 计算机组成原理知识点+题(白中英等的版本)
  • 支付逻辑漏洞防御:5 项后端校验策略与 3 个真实代码修复案例
  • WaveTools鸣潮工具箱:3个核心功能让游戏体验提升200%的专业解决方案
  • 大数据毕设选题推荐:基于智能匹配的霍兰德职业招聘可视化系统的设计与实现 基于用户测评数据的就业趋势分析系统【附源码、mysql、文档、调试+代码讲解+全bao等】
  • Next.js DApp 的 Edge Runtime 部署:Vercel Edge 与 Cloudflare Workers 的性能对比
  • 计算机毕业设计之基于SSM框架的低碳校园宿舍用电监测与激励系统设计与实现
  • Agentic Workflow时代:后端开发者需要重新思考什么?
  • 自演化数据库架构:从静态Schema到AI驱动的自适应表结构设计
  • vLLM 0.8.5 部署 Qwen2.5-7B:3个关键参数调优,吞吐量提升 40%
  • ragflow v0.26.4发布:16种语言词干化上线,Go后端持续统一,Agent与MCP一口气修复大量关键问题
  • OpenAI 最重要的一篇论文有个 bug,整个 AI 行业按错误配方烧了两年算力
  • 医学影像云PACS系统——PACS诊断工作站
  • 橡果教育Creo产品结构设计培训专业办学能力研究报告
  • RAG解决复杂问题——多跳推理
  • API响应延迟差4.8倍?上下文崩溃率超32%?Claude与DeepSeek真实生产环境踩坑清单,开发者速存!
  • 全栈自研技术的GEO服务商哪家强?2026年五大服务商深度横评与决策参考建议
  • 【大数据课程设计/毕业设计】基于 Hadoop 的热点新闻情感分析系统的设计与实现 基于智能分析的网络热点新闻推送与研判系统【附源码、数据库、万字文档】
  • Kimi K2的“超长上下文”真相 vs DeepSeek V3的“零样本泛化”黑科技:一线团队踩坑复盘与迁移避坑清单
  • AI编程提效瓶颈突破,.cursorrules精准调优实战手册,仅限前200位开发者获取的私密配置模板
  • STRIDE威胁建模实战:用Microsoft Threat Modeling Tool构建Web应用安全防线
  • 2026新手必看:5款热门AI写小说工具测评,哪款写小说软件更适合长期连载?
  • Palantir AIP平台AI服务定价机制与成本优化策略解析
  • OpenCV 4.8 与 PyTorch 2.2 图像识别:5个实战项目代码与性能对比
  • 系统集成项目工程师备考复盘
  • 可研PPT怎么做?这份避坑指南,照着抄就对了
  • 分类学数据库更新对蛋白快速比对工具DIAMOND的影响
  • 数据集成平台|AIIData数据中台实现达梦、人大金仓、Hive、MySQL、Oracle、Kafka 一键接入Doris