利用 Claude Code 生成视频:18 集 STM32 万年历教学视频

脚本 → 配音 → 画面 → 字幕 → 合成的完整踩坑实录

作者:knight · 项目:LED 全彩点阵屏万年历

本文记录这套 18 集零基础教学视频的完整制作过程——如何用 Claude Code 从一份文字脚本出发,自动生成配音、画面、字幕并三轨同步合成,以及一路上踩过的坑。

一、项目是什么

这套视频教的是一个完整的嵌入式小项目:用 STM32 做一块 LED 全彩点阵屏日历时钟。成品长这样(128×64 点阵屏上同时显示):

它涵盖了一个嵌入式新手要走通的完整链路:画原理图 → 画 PCB → 打样焊接 → 用 Keil 写程序 → 驱动 DS1302 实时时钟 → 驱动 HUB75 点阵屏 → 烧录成品

1.1 硬件平台

部分

选型

主控

STM32F103C8(Cortex-M3,64KB Flash,20KB SRAM,72MHz,LQFP48)

RTC

DS1302 三线串行实时时钟,外接 32.768kHz 晶振

显示

128×64 LED 全彩点阵屏,HUB75 接口,1/32 扫描

供电

5V

电路板

两层板,自行设计打样

HUB75 接口接线(这是真机接线,不是示意图):

信号

GPIO

信号

GPIO

R1

PB0

A

PA3

G1

PB1

B

PA4

B1

PA2

C

PA5

R2

PB12

D

PA6

G2

PB8

E

PA7

B2

PB5

CLK

PA1

LAT

PA0

OE

PB6

DS1302 接线:

信号

GPIO

CLK

PA8

DAT

PA9

RST(CE)

PA10

注意:工程里禁用了 JTAG(保留 SWD),避免 PB3/PB4/PA15 与 LED 信号冲突。

1.2 软件架构

IDE 为 Keil MDK uVision 5.24.2.0,编译器 ARMCC V5.06,标准外设库 STM32F10x StdPeriph V3.5.0。工程结构(经典的 Keil 四文件夹):

+-- Start/      启动文件 + CMSIS(startup_stm32f10x_md.s 等)

+-- Library/    STM32 标准外设库(约 50 个文件)

+-- User/       核心用户代码

|   +-- main.c          主程序:帧渲染 + 扫描 + 时间管理

|   +-- DS1302.c/h      DS1302 RTC 驱动

|   +-- led_matrix.c/h  LED 矩阵底层操作

|   +-- display.c/h     显示缓冲(备用方案)

|   +-- Delay.c/h       精确延时(ms + us)

|   +-- stm32f10x_it.c/h

+-- Objects/    编译输出

几个值得一提的设计点:

1. 软件校准跳秒:DS1302 晶振偏快,实测每天快约 15 秒(≈174 ppm)。于是启动时从 DS1302 读一次基准时间,之后由 TIM2 的 1ms 中断做软件秒递增,每 5760 秒跳过 1 次递增——一天 86400 秒正好跳过 15 次,把误差从 +15s 压到 1s 以内。

2. 编译指纹自动对时:用 __DATE__ / __TIME__ 宏生成 16 位指纹,存进 DS1302 电池备份 RAM;新固件首次上电指纹不匹配就同步时间,同一固件再次上电就保留 DS1302 里已有的时间。

3. 星期用 Sakamoto 算法由年月日直接推算,DS1302 存的星期值仅作参考。

4. 3-bit 彩色系统:每个像素 1 字节低 3 位,8 种颜色(黑/红/绿/黄/蓝/洋红/青/白),字库分 ASCII 5×7 与中文 16×16(周一二三四五六日)。

二、为什么要做这套视频

市面上的 STM32 教程要么只讲单片机不讲成品,要么只给代码不讲"为什么"。这套视频定位是零基础实战:跟着走,能从一个元器件清单开始,一路做到一块挂在墙上的彩色日历时钟。

所以每集都遵循"先给结果、再拆原理、最后动手"的节奏,并且所有引脚、参数都严格对应真实工程,没有一处是画饼。

三、视频制作整体管线(核心)

整套视频是程序化生成的:写一个 Python 脚本,从一份文字脚本出发,自动产出配音、画面、字幕,最后用 FFmpeg 合成。每集目录结构一致:

episode_NN/

+-- script/script.json        文字脚本(每场景的旁白 + 标题)

+-- narration/                TTS 配音 mp3 + 词级时间戳 + timeline.json

+-- generated/                PIL 生成的静态画面 PNG

+-- subtitles/                .ass 字幕

+-- scenes/                   逐场景视频片段 + 静音成片

+-- output/                   最终 mp4

+-- gen_assets.py             生成静态画面

+-- tts_gen.py                生成配音 + 时间轴

+-- build_subtitles.py        生成 .ass 字幕

+-- build_scenes.py           把画面编成逐场景视频

+-- compose.py                配音 + 字幕 + 角标 + 渐隐合成

一条流水线:

script.json

   |

   +-- tts_gen.py --> 配音 mp3 + timeline.json(场景时长、间隔)

   |                        |

   +-- gen_assets.py -> 1920x1080 静态画面(示意图/表格)

   |                        |

   +------------------------+--------------+

                            v              v

                    build_scenes.py   build_subtitles.py

                    逐场景片段          .ass 字幕

                            |              |

                            v              v

                        compose.py(FFmpeg)

                              |

                        output/第N集.mp4

视频生成项目·目录结构:

关键思路是"先配音,后画面"timeline.json 由 TTS 的时长驱动,同一份时间轴既决定每个场景的画面时长,也决定字幕出现的时间,从而保证视频 / 中文配音 / 中文字幕三轨严格同步

· 配音:edge-tts,中文音色 zh-CN-YunxiNeural

· 画面:Pillow(PIL)画的 1920×1080 示意图(浅色科技风)。

· 字幕:ASS 格式,白字 + 黑描边。

· 合成:FFmpeg,mux 配音、叠加角标、烧录字幕。

四、18 集分集结构

全套 18 集,分四个阶段:

标题

阶段

1

项目介绍与成品演示

认识

2

硬件清单与元器件认识

认识

3

元器件采购与选购

认识

4

STM32 入门

认识

5

DS1302 与点阵屏

认识

6

读懂原理图

制作

7

PCB 设计打样

制作

8

Gerber 导出与下单打样

制作

9

焊接与首次上电

制作

10

搭建 Keil 开发环境

软件

11

新建 STM32 工程

软件

12

主程序与点屏

软件

13

DS1302 时钟芯片

软件

14

显示与字库

软件

15

源码逐讲(一)main.c

深入

16

源码逐讲(二)DS1302

深入

17

常见故障排查

深入

18

烧录与成品总结

深入

五、每一步怎么做

5.1 文字脚本(script.json)

每集先写一个 script.json,把整集拆成若干"场景",每个场景有一段旁白文字。这是后面所有东西的源头。

5.2 配音 + 时间轴(tts_gen.py)

用 edge-tts 把每段旁白合成成 mp3,同时拿到句级时间戳,汇总成 timeline.json(含每场景时长、场景间隔 lead_in/lead_out)。

# 核心:edge-tts 流式合成 + 拿时间戳(示意)

import edge_tts, asyncio

 

async def synth(text, out_mp3, out_words):

    communicate = edge_tts.Communicate(text, "zh-CN-YunxiNeural")

    async for chunk in communicate.stream():

        if chunk["type"] in ("WordBoundary", "SentenceBoundary"):

            # offset / duration 单位是 100ns

            t = chunk["offset"] / 10_000_000

            dur = chunk["duration"] / 10_000_000

            out_words.append((chunk["text"], t, dur))

5.3 静态画面(gen_assets.py)

用 Pillow 画 1920×1080 的示意图。浅色科技风:白→浅灰渐变底、圆角卡片、天蓝色点缀(品牌色 (2,132,199))。

from PIL import Image, ImageDraw, ImageFont

 

W, H = 1920, 1080

def new_bg():

    img = Image.new("RGB", (W, H), (255, 255, 255))

    d = ImageDraw.Draw(img)

    for y in range(H):

        t = y / H

        d.line([(0, y), (W, y)],

               fill=tuple(int(255 + (238 - 255) * t) for _ in range(3)))

    return img

5.4 字幕(build_subtitles.py)

按句级时间戳生成 ASS 字幕,样式白字 + 黑描边:

Style: Default,msyh,40,&H00FFFFFF,&H00FFFFFF,&H00000000,&H00000000,0,0,0,0,100,100,0,0,1,2.6,1.4,2,80,80,58,1

5.5 逐场景视频 + 合成(build_scenes.py / compose.py)

静态画面用 -loop 1 编成视频片段,再 concat 成静音成片,最后合成:

# 单张图编成 N 帧片段

ffmpeg -y -loop 1 -i scene.png -vf fps=30 -frames:v N -c:v libx264 -pix_fmt yuv420p scene.mp4

合成时:mux 配音 → 左上角叠加品牌角标(天蓝 "Dream Chaser" + "制作人:宁鹏")→ 底部加黑色渐隐(保证浅色背景下白字幕可读)→ 烧录 ASS 字幕。

六、画面设计规范(统一视觉)

1. 左上角固定角标:天蓝色 "Dream Chaser" + 黑色 "制作人:宁鹏"。

2. 底部渐隐:y=800→1080 黑→透明(alpha 0→70),字幕落在 y≈940-1060,所以正文/徽章底边不得低于 y≈706,否则会被字幕/渐隐遮挡。

3. 统一片头:18 集片头全部是同一版式——点阵网格背景 + 中央蓝色柔光 + 顶部 8 色 LED 光点带 + 主标题「LED 全彩点阵屏万年历」+ 副标题 + 蓝色集数徽章「第 N 集 · 标题」+ 底部技术栈标签(STM32/DS1302/HUB75/PCB),只有集数徽章文字不同。

七、踩坑记录(重点,全是血泪)

这部分可能是对你最有用的。视频程序化生成听上去美好,实际每个环节都有坑。

7.1 TTS 相关

1. edge-tts 的 WordBoundary 模式坏了(返回 0 个词 / 报 NoAudioReceived)。必须用默认的 SentenceBoundary 模式,只取 chunk["type"] in ("WordBoundary","SentenceBoundary") 的事件;offset/duration 单位是 100ns,要 /10_000_000 换成秒。

2. edge-tts 偶发 NoAudioReceived(限流):要加指数退避重试,成功后校验"词非空且 mp3 大小 > 0"。

3. edge-tts 会网络挂起(不抛异常,直接卡死):必须用 asyncio.wait_for(..., timeout=60) 包住流式读取。

4. 连续多请求会限流:退避要长(sleep(3.0 * attempt) + retries=6)。tts_gen.py 要做成断点续跑:每个场景成功后写 narration/{sid}.words.json,重跑时跳过已有,只补失败的场景。

7.2 FFmpeg 相关

5. 图片必须 -loop 1 -i img + -frames:v n。用 -stream_loop -1 处理图片会让它无限循环,叠加 zoompan 后内存暴涨到 930MB 直接卡死;-stream_loop -1 + -t 只能用于视频。

6. Windows 盘符冒号会破坏 -filter_complex 参数解析C:/... 的 : 被当成参数分隔符,报 "Unable to parse ... as image size")。解决:subprocess.run(cwd=BASE) + 过滤串里用相对路径(不带盘符)。

7. -filter_complex 里直接写 overlay=文件.png 会报错("Cannot find an unused video input stream")。必须用 movie=文件.png[pad] 当源滤镜,再 [main][pad]overlay

7.3 编码 / 环境相关

8. Windows GBK 控制台会乱码:subprocess 捕获用 capture_output=True(bytes)+ decode(errors="replace"),别用 text=True(会 UnicodeDecodeError)。

9. print 输出中文在 GBK 下会 UnicodeEncodeError:在脚本开头加 sys.stdout.reconfigure(encoding="utf-8")

10. 字幕里的品牌名 "Dream Chaser" 要用不换行空格 \u00a0 防拆行,否则可能被拆成两行。

7.4 画图相关

11. 横向排 N 张卡片一定要用 x0 + i*(cw+gap) 算 x,别手写每张的中心点;否则第一张卡 cx - half_w 会算出负值,卡片被画到画布外面。写死中心点的话要校验 cx - half_w >= 0

12. 放真实截图时要避开左上角角标(角标非透明区 bbox 约 (30,30,351,146)),否则截图会跟角标撞在一起,观感很差。

八、开源与交付

· 成品视频:18 集,命名 第N集 标题.mp4

· 配套工程:一个 Keil 工程,Project.uvprojx 里全部是相对路径.\Start .\Library .\User .\Objects),没有绝对路径,所以拷到任何电脑都能编译。

给初学者的上手步骤:

1. 装 Keil MDK uVision5(5.24.2.0 或更新),并在 Pack Installer 里装 STM32F1xx_DFP(否则芯片型号 STM32F103C8 认不出来);

2. 把工程文件夹放到纯英文路径下(例如 D:\STM32\Wannianli),别放桌面、别带中文——Keil 对非 ASCII 路径兼容差,启动文件汇编/链接可能报错;

3. 双击 Project.uvprojx,点 Build 编译,无报错即成功。

交付时只需给 Project.uvprojx + Project.uvoptx + Start/ + Library/ + User/ 五项;Objects/Listings/DebugConfig/Project.uvguix.* 都是编译产物/个人设置,Keil 会自动重建,不用发。

九、结语

这套视频最有价值的不是"会点灯",而是把硬件、软件、工艺、工程化流水线串成了一条完整链路——从元器件到成品、从脚本到成片。把视频制作本身也做成一条可复用的自动化流水线,是我做这个项目时最满意的地方。

如果你也在做嵌入式教学内容,或者想给自己的项目配一套成体系的视频,希望这篇实录能帮你少踩一些坑。

(全文完)

Logo

智能硬件社区聚焦AI智能硬件技术生态,汇聚嵌入式AI、物联网硬件开发者,打造交流分享平台,同步全国赛事资讯、开展 OPC 核心人才招募,助力技术落地与开发者成长。

更多推荐