01-Docker开发环境容器化:从基本概念到Ubuntu20
Docker 开发环境容器化:从基本概念到 Ubuntu 20.04 安装排查 摘要:从 Image、Container、Dockerfile 和挂载建立 Docker 心智模型,并结合 Ubuntu 20.04 的安装冲突说明为什么要把旧开发环境容器化。 @[toc]学习 Docker 最容易走偏的方式,是先背几十条命令,再去想这些命令解决什么问题。对于开发环境容器化,更合理的顺序正好相反:先明确工程为什么需要 Docker,再理解 Image、Container、Dockerfile、挂载和权限这些概念,最后才把命令放进实际场景。 本文从一个典型的嵌入式交叉编译环境出发:旧工程长期依赖 Ubuntu 20.04、厂商 Yocto SDK、固定 CMake、代码生成工具和若干宿主机软件。旧机器能稳定构建,但一旦换电脑、升级系统或让其他开发者接手,就很难准确复刻环境。 Docker 在这里的目标不是“把 Linux 再装一遍”,而是: 把构建环境固定成可重复生成的镜像,把日常修改的源码继续留在宿主机。 1. 为什么开发环境值得容器化传统开发机往往随着项目迭代逐渐变成这样...
02-大型SDK开发镜像工程化:BuildKit、Buildx与项目脚本
大型 SDK 开发镜像工程化:BuildKit、Buildx 与项目脚本 摘要:以约 25 GiB 的交叉编译 SDK 为例,解释 BuildKit、Buildx、named context、镜像层,以及 Dockerfile、Compose 和配套脚本怎样组成完整开发环境。 @[toc] 普通 Docker 教程经常从一个几十 MB 的源码目录开始:写 Dockerfile、COPY . .、执行 docker build,几秒钟就能看到结果。但真实嵌入式开发环境可能完全不是这个规模。 假设已有一套厂商 Yocto SDK,展开后约 25 GiB,还需要固定版本的 CMake、host 侧代码生成工具以及一套 UID/GID 兼容逻辑。此时问题已经从“会不会写 Dockerfile”变成了: 25 GiB SDK 怎样进入 Image; 怎样避免为了构建镜像再制造一个巨大的 SDK 压缩包; BuildKit 和 Buildx 分别负责什么; 为什么使用额外 build context; 为什么源码不和 SDK 一起做进 Image; 为什么需要 entry...
03-交叉编译开发容器故障排查:从CMakeCache到目标库链接
交叉编译开发容器故障排查:从 CMakeCache 到目标库链接 摘要:按真实首次失败点复盘旧 CMakeCache、host/target Thrift、SDK 权限、编译参数冲突和目标库链接,说明怎样沿证据链逐层收敛问题。 @[toc] Docker 能启动、交叉编译器能执行,只能证明环境最外层入口正常。历史 C/C++ 工程仍可能依次暴露旧 CMake 缓存、宿主工具、目标库、权限和编译参数策略。 这次排查最重要的原则是:围绕当前第一次失败收集证据,证明后再继续向后。 最终故障链可以概括为: 1234567flowchart TD Cache["旧 CMakeCache\n编译器路径属于旧 Host"] -->|干净 build tree| Compiler["交叉 GCC 正确识别"] Compiler --> Thrift["host Thrift 查找失败"] Thrift -->|确认 host generator| Header["...
04-Docker常用语法与命令:CLI、Dockerfile与Compose速查
# Docker 常用语法与命令:CLI、Dockerfile 与 Compose 速查 摘要:用开发环境场景整理 Docker CLI、Dockerfile、Buildx、Bind Mount、Compose、镜像迁移与磁盘清理的常用语法,重点解释参数改变了什么。 @[toc] 这一篇把前面频繁出现的 Docker 语法集中成开发机速查表,不穷举全部参数,只保留最常用对象和命令结构。 1. Docker CLI 先按“对象 + 动作”理解现代 Docker CLI 很多命令可以读成: 1docker <object> <command> [options] 例如: 123456docker image lsdocker image inspect arm64-dev:20.04docker container lsdocker container rm demodocker volume lsdocker network ls 同时 Docker 仍保留大量短写: 1234docker imagesdocker psdocker rmdoc...
canopennode-rtt推荐,不只可以做从站,也可以承担主站角色
canopennode-rtt推荐,不只可以做从站,也可以承担主站角色 摘要:canopennode-rtt 将 CANopenNode 接入 RT-Thread,可同时承载设备与控制器角色;本文还对比 RTT-CanFestival 的架构、CiA 402、维护与选型差异。 仓库地址:https://github.com/wdfk-prog/canopennode-rtt在线文档:https://wdfk-prog.space/canopennode-rtt/上游协议栈:https://github.com/CANopenNode/CANopenNode本文基于 canopennode-rtt master 分支 2026-09-01 的公开状态整理,参考提交:b5a9dc1a242d784358ebc8a5bf81d5afcdc8f4ba。 @[toc] 如果你正在 RT-Thread 上做伺服驱动、运动控制器、工业 I/O、传感器节点、执行器、网关或者任何需要 CANopen 的 MCU 项目,那么我比较推荐关注一下这个仓库:wdfk-prog/can...
RT-Thread 如何用 MPU 做线程栈保护
RT-Thread 如何用 MPU 做线程栈保护 摘要:结合 RT-Thread 当前 Cortex-M4 实现,拆解 MPU 内存保护、线程双 Guard 区、上下文切换重装载以及 MemManage Fault 的完整运行链路。 在 Cortex-M 系列 MCU 上,线程栈溢出一直是最难排查的一类故障之一。它往往不是在“栈刚好用完”的那一刻表现为明确错误,而是先覆盖相邻内存,再以随机变量异常、链表损坏、HardFault、跑飞甚至数分钟后的非确定性故障出现。 RT-Thread 传统的 RT_USING_OVERFLOW_CHECK 可以对线程栈做软件检查,但软件检查本质上依赖检查时机:如果栈已经越界,在下一次检查发生之前,相邻内存仍可能先被破坏。 另一条路线是利用 Cortex-M 自带的 MPU(Memory Protection Unit)。RT-Thread 的 RT_USING_MEM_PROTECTION 提供了 MPU 抽象层,而 RT_USING_HW_STACK_GUARD 则进一步利用 MPU,在每个线程栈的上下边界放置不可访问区域。一旦 CPU...
GitHub push 失败:如何扫描并清理 Git 历史中的大文件
GitHub push 失败:如何扫描并清理 Git 历史中的大文件@[toc] 摘要:从 GitHub 100 MiB 大文件拒绝问题出发,说明为何删除文件后仍无法推送,并给出 PowerShell 扫描、历史清理、Git LFS 与防复发方案。 在把一个已有 Git 项目首次推送到 GitHub 时,可能会遇到如下错误: 1234remote: error: File vendor/example/.git.zip is 151.91 MB;remote: error: this exceeds GitHub's file size limit of 100.00 MBremote: error: GH001: Large files detected.! [remote rejected] master -> master (pre-receive hook declined) 这里最容易产生的误解是:既然问题文件已经不需要了,那么再新增一个 commit 把它删除,然后重新 git push 不就可以了吗? 答案是否定的。 GitHub 检查的不...
Lely CANopen 启动与从机联机后的运行机制
Lely CANopen 启动与从机联机后的运行机制:Executor、Loop、Timer、CAN 与 Future/Promise 摘要:梳理 Lely CANopen 主站启动、从机 boot 和稳定运行阶段实际存在的任务、事件循环、逻辑定时器、CAN 报文及 Future/Promise 完成链路。 @[toc] 结论以 Linux、SocketCAN、liblely-io2、liblely-ev、liblely-coapp 和 canopen::AsyncMaster 的典型主站为基线,可以先得到六个结论: 通常只有一个主 ev::Loop,不是每个从机一个 Loop。 多个从机的 NMT boot、SDO、Heartbeat 和 PDO 状态机可由同一个事件循环驱动。 通常只有一个 Loop executor。 AsyncMaster 将用户侧 OnBoot()、OnConfig()、OnState()、OnHeartbeat()、OnEmcy()、OnRpdoWrite() 等回调包装为 ev_task,再投递到相应 Driver 的 e...
Lely CANopen coapp Driver
Lely CANopen coapp Driver:BasicMaster 与 BasicDriver 的职责边界、注册表和事件流程 摘要:从 BasicMaster 的远程节点操作能力出发,解析 Driver 注册表、非拥有关系、事件分发,以及各类 Driver 的协作机制。 @[toc]Lely CANopen 的 coapp 层中,Driver 很容易被误解成 CAN 硬件驱动、协议状态机,或者由 Master 自动创建的“远程节点对象”。源码表达的关系并不是这样:BasicMaster 本身可以直接操作远程节点;BasicDriver 是绑定固定 Node-ID 和 Executor 的节点级应用适配器,同时也是 Master 分发节点事件的接收对象。 1. 先回答两个核心问题1.1 BasicMaster 能否直接操作远程节点可以。BasicMaster 的公开接口已经包含 Node-ID 参数,可以直接对指定远程节点执行操作,例如: Command(cs, id):发送 NMT 命令; Boot(id)、IsReady(id):启动并查询 NMT boot-s...
Lely CANopen Master
Lely CANopen coapp::Master:NMT 总控、SDO 仲裁与 Driver 事件分发机制 摘要:从 master.hpp 与 master.cpp 追踪 BasicMaster/AsyncMaster 如何组织 NMT 启动、SDO 所有权、配置握手及 Driver 事件分发。 @[toc]lely::canopen::BasicMaster 和 lely::canopen::AsyncMaster 是 Lely CANopen C++ 应用层中负责主站编排的核心对象。它们并不是简单地把 NMT、SDO、PDO API 包装成 C++ 成员函数,而是在 Node 的本地 CANopen 节点能力之上,进一步维护远端节点 Driver、节点启动状态、配置阶段状态以及 Client-SDO 使用权。 理解这两个类的关键,不是逐个记住 SubmitRead()、Command()、OnBoot() 等接口,而是建立下面这条主线: 12345本地主站 Node 启动 -> NMT master 发起或接管远端节点 boot slave ...








