RISC-V Linux 嵌入式编程技术 · 第四章

本章实验 · 三个实验

实验一 select 最小复用 · 实验二 温控命令 REPL · 实验三 串口命令温控(真 DHT22)

本章三个实验一览

第四章的收口实验要同时干很多事:焊 TXS、接 DHT22、写命令处理、写 select 主循环、还要联真硬件调风扇。为了不一把抓,拆成三个递进实验:实验一、实验二纯软件、无接线,把「select 并发」和「命令处理器」这两半单独练会;实验三再把它们加上真 DHT22 与真 GPIO 合起来。

实验练什么器材源码目录
实验一
select 最小多路复用
select 同时等「定时 tick」和「stdin 输入」,打字时定时不丢 code/select-mini/
实验二
温控命令 REPL
status / set temp / set humidity 处理器(无传感器无 GPIO) code/cmd-repl/
实验三
串口命令温控
焊 TXS · 接 DHT22 · 把实验一/二的并发与命令接到采样控温 DHT22、TXS、AMS1117、继电器风扇 cmd_thermo.c
建议:先在板上把实验一、实验二跑通,再进实验三接线——那时你只剩「DHT 读取 + 把这些拼进 select」没写过。

实验一 · select 最小多路复用(select-mini)

实验三卡人的往往是 select 本身:一边要每 2 秒采一次样,一边要立刻响应你敲的命令。本实验把并发机制剥到最小:每秒打一个 [tick],同时 stdin 来一行就回显一行。源码 chapters/ch04/code/select-mini/,只实现 run_repl_loop

步骤

# 主机,或先 scp 到板子再进目录
$ cd chapters/ch04/code/select-mini            # 板上:cd ~/select-mini
$ make
# 手动试:./build/select-mini 然后敲几行,quit 退出

打开 src/select.c,用 select(fd+1, &rfds, NULL, NULL, &tv) 写主循环。判据看行为:没输入时每秒一个 [tick N];打字时立刻回显且不丢 tick;quit 退出。自动演示(参考 make sol):

$ (sleep 2; echo hello; sleep 1; echo quit) | ./build/select-mini
# 参考输出(时间轴示意):
#   [S] select demo: 1s tick, stdin echo, quit to exit
#   [tick 1]
#   echo: hello
#   [tick 2]
#   bye

本实验成功判据:自动演示里 tick 与 echo 交替出现、不互相饿死;quit 正常退出;能口头说「select 返回 0 是超时,返回正数是 stdin 可读」。

板上 SSH 运行 select-mini-sol:tick 与 echo 交替,quit 退出
板端证据(SSH → select-mini):tick 与 stdin echo 交替;quit 后 bye
select-mini 演示结束后的终端快照
同一次演示的终端快照(可归档进实验报告)

实验二 · 温控命令 REPL(cmd-repl)

实验三的另一半是命令处理:status 打印、set temp <数> / set humidity <数> 改阈值。本实验用纯 stdin/stdout 把这些处理器练会(没有传感器、没有 GPIO)。源码 chapters/ch04/code/cmd-repl/,只实现 state_init / cmd_status / cmd_set

步骤

# 主机,或先 scp 到板子再进目录
$ cd chapters/ch04/code/cmd-repl              # 板上:cd ~/cmd-repl
$ make
$ ./build/cmd-repl      # 未实现前:status 打不出、set 全报错 → 预期

主循环已给(读行 → 切首词 → 分发)。你只写三个函数:初始 30.0/95.0;status 打印当前 bar;cmd_setsscanf 解析 "temp 28.5" / "humidity 60"。喂命令对照(参考 make sol):

$ printf 'status\nset temp 28.5\nstatus\nset humidity 60\nstatus\nset temp abc\nquit\n' | ./build/cmd-repl
# 参考输出:
#   [repl] status | set temp <num> | set humidity <num> | quit
#   [status] temp_bar=30.0C hum_bar=95.0%
#   [ok] temp_bar=28.5C
#   [status] temp_bar=28.5C hum_bar=95.0%
#   [ok] hum_bar=60.0%
#   [status] temp_bar=28.5C hum_bar=60.0%
#   [err] usage: set temp <num> | set humidity <num>
#   bye

本实验成功判据:喂命令输出与参考一致(含 set temp abc 报用法错);能口头说这套处理器在实验三里改的是 g_st,而「会不会立刻生效」由主循环节奏决定。

板上 SSH 运行 cmd-repl-sol:status/set 命令演示
板端证据(SSH → cmd-repl):status / set temp / set humidity / 非法用法
cmd-repl 演示结束后的终端快照
同一次演示的终端快照

实验三 · 串口命令温控(真 DHT22)

实验一你练会了 select 并发,实验二练会了 status / set 处理器。实验三把它们焊进真硬件:接 DHT22 采真温湿度,用命令改阈值,select 让打字和采样同时及时。下面是原实验正文,器材、接线、实现契约、验收都从这里开始。

说明

本实验要干什么:焊好 TXS、接上 DHT22;在脚手架里实现命令处理与 select 主循环;在 SSH 里一边看自动控温,一边敲 status / set
你能学到什么:命令表怎么把一行字派到函数;用 select 让「采样」和「打字」同时及时;温湿度联合阈值如何驱动风扇。

讲义已讲清:字节进出、为何要 TXS、命令表思路、select 机制。本实验递进:整板接线 + 你自己写出可跑的对话层。

成功时你能演示:make 通过;后台静默采样控风扇;敲 status 立刻打出当前温湿度;set 改阈值立刻生效;打字时自动控温不停。
安全:风扇仍走继电器触点(第三章接法)。焊接注意烙铁温度与通风。

实验器材

东西用途
荔枝派 4A跑 Linux;排针接线
DHT22 模块真温湿度输入
TXS0108E1.8 V ↔ 3.3 V 电平转换(排针需焊接)
AMS1117-1.8V 小板给 TXS 低压侧(VA)供电
继电器 + 风扇 + 杜邦线沿用第三章
参考资料:chapters/ch04/manuals/——DHT22_规格书.pdfAHT20 产品手册 a2 2020-8-11.pdfAMS1117-1.8V降压小板.md

接线与焊接

第四章整板:DHT22、电平转换、继电器与风扇
图 1 整板联调:荔枝派 + 面包板 + DHT22 + 电平转换 + 继电器与风扇

先焊 TXS 排针:烙铁预热 → 排针插孔 → 背面逐脚上锡 → 检查连锡。

说明
排针 3.3VDHT22 正(VCC)传感器供电
排针 GNDDHT22 负(GND);TXS / AMS 共地共地
DHT22 OUTTXS B1高压侧 3.3 V
TXS A1排针 IO1_6低压侧 1.8 V
AMS1117 IN排针 3.3V降压输入
AMS1117 OUTTXS VA低压侧电源 1.8 V
排针 3.3VTXS VB高压侧电源
TXS OE接高使能

继电器 / 风扇保持第三章:IO1_5 直连信号端,风扇走 NO–COM。

打开 cmd_thermo.c,确认 GPIO_CHIP_PATHDHT_LINE=6FAN_LINE=5。首次联调建议保持默认 SIMULATE_SENSOR=1(模拟温度升降);接好真 DHT 并稳定后再用 -DSIMULATE_SENSOR=0 重编。

本步成功判据:能指着线说出「地 / TXS A·B / VA·VB / IO1_6」;知道当前是模拟还是真传感器。

实现命令与 select

打开 cmd_thermo.c。脚手架已提供 DHT 读取、风扇、sample_and_controlread_command_linemain只改标了 STUDENT TODO 的区块:在该注释与 read_command_line 之间写出命令函数、命令表、dispatch_commandmain_loop

先直接 make:应看到类似 implicit declaration of function 'dispatch_command'main_loop——这是预期起点。

你要写的契约

/* 打印 g_st:用 [STATUS] 前缀输出温度、湿度、风扇、温度bar、湿度bar。
 * 尚无采样(has_sample==0)时打印明确提示,不要打垃圾数。
 * 注意:采样函数本身不刷温湿度;只有本命令才打印。
 * args 可忽略。成功返回 0。
 */
static int cmd_status(char *args);

/* 解析 args:"temp <数>" 或 "humidity <数>"(也可用 hum),
 * 写入 g_st.t_bar / g_st.h_bar。用法错误打印 [ERR]。成功打印确认并返回 0。
 * 逻辑:温度或湿度高于对应 bar → 开风扇;两者都低于 → 关风扇。
 */
static int cmd_set(char *args);

struct cmd_entry {
    const char *name;
    int (*handler)(char *args);
};
/* 至少注册 status / set,表尾 { NULL, NULL } */

/* 拆出命令名与参数;查 g_cmds 并调用。
 * 空行返回 0;未知命令打印 [ERR] unknown command,返回非 0。
 * 进程不要因此 exit。
 */
static int dispatch_command(char *line);

/* 用 select 同时等:
 *   · cmd_fd(stdin)可读 → 调用已有的 read_command_line()
 *   · 距下次采样超时 → 调用已有的 sample_and_control()
 * 用墙上时钟维护 deadline / remain,处理命令后不要把采样周期整段重置。
 * g_running 为 0 时退出循环。
 */
static void main_loop(void);

写的时候要想清楚

select 线索:讲义 4.3 已给出返回值表与 deadline / remain 时间轴。全局 cmd_fdmain 里已设为 STDIN_FILENO
对照答案:cmd_thermo-sol.cmake sol 得到 cmd_thermo-sol。建议过关后再打开;卡住超过一刻钟再对照某一个函数。

编译运行

主路径与第三章相同:在 x86 Linux 激活 venv-gnu-ruyisdk 后交叉编译,再拷到荔枝派运行;或在荔枝派上直接 make(Makefile 按机器自动选择编译器)。访问 GPIO 需要 sudo。编译的是你自己写的 cmd_thermo.c

# —— 在 x86 Linux 上 ——
$ . ~/venv-gnu-ruyisdk/bin/ruyi-activate
$ cd chapters/ch04/code
$ make clean && make
$ file cmd_thermo

# —— 在荔枝派上 ——
$ cd ~/ruyi-riscv-embedded/ch04-lab
$ make clean && make
$ sudo ./cmd_thermo
# 模拟模式验证通过、接好真 DHT 后,改用:
$ make clean && make CFLAGS='-Wall -Wextra -O2 -DSIMULATE_SENSOR=0'
$ sudo ./cmd_thermo

先用默认模拟模式跑通软件流程;接真 DHT 时务必带上 -DSIMULATE_SENSOR=0。对照参考实现(助教自检):make sol fan-off

联调验收

荔枝派上真 DHT 编译运行 cmd_thermo-sol:file 为 RISC-V,status 读出温湿度并 set temp 开风扇
图 2 荔枝派上 make sol(真 DHT):产物为 RISC-V;status 读出温湿度;set temp 20 后选择开风扇
  1. 启动后等几秒(让 DHT 先采到样),敲 status:应出现 [STATUS] temp=… hum=…;平时终端不应刷温湿度。
  2. status:立刻打印完整状态(或「尚无采样」提示)。
  3. set temp 30,再 status:温度 bar 已变。
  4. set humidity 80,再 status:湿度 bar 已变。
  5. 控温进行中连续敲 status:每次立刻出现 [STATUS];风扇开/关瞬间打印上一采样与本次采样(无 [STATUS] 前缀)及「选择开/关风扇」。
  6. (可选,真 DHT)哈气提高湿度,观察是否按湿度 bar 开风扇。
板上 sudo cmd_thermo-sol:SIMULATE、status、set temp 20、Ctrl+C 清理
图 3 板端证据(SSH → make sol,模拟模式):status / set temp 20;采样后选择开风扇;Ctrl+C 清理
Ctrl+C:主程序会先拉低再释放。若被 kill -9 / 崩溃后风扇一直转:make fan-off && sudo ./fan_off(默认保持拉低直到 Ctrl+C;--once 只脉冲一次)。

常见故障

现象先查什么
implicit declaration / 编译不过四个符号是否写在 read_command_line 上方?签名是否与契约一致?
能编译,敲命令没反应main_loop 是否把 cmd_fd 放进 select?是否调用了 read_command_line
命令有回复,风扇不再动作是否卡在阻塞读或固定 sleep?超时分支是否调用 sample_and_control
真 DHT 一直 [ERR]TXS 焊接、VA/VB、OE、共地、IO1_6;先 SIMULATE_SENSOR=1 验证软件
Permission deniedsudo ./cmd_thermo

验收表

验收项通过条件
接线DHT 经 TXS 到 IO1_6;风扇沿用第三章
自己写的代码命令 + dispatch + main_loop 在学生文件中;make 通过
自动控温超温(或超湿)开 / 回落关可演示
status / set立刻回复;可改温度 bar 与湿度 bar
同时工作打字时采样不停更
完成标志:上表可勾;能口头说明 select 返回 0 与返回正数各走哪条路。