本章实验 · 三个实验
完整实验页(含板上录像):第四章 · 本章实验 · 三个实验
讲义:第四章 串口对话与温控 · 讲义 — RISC-V Linux 课程
第四章收口实验要同时干很多事:焊 TXS、接 DHT22、写命令处理、写 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 |
建议:先在板上把实验一、实验二跑通,再进实验三接线。
实验一 · 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 退出。
$ (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 可读」。
实验二 · 温控命令 REPL(cmd-repl)
实验三的另一半是命令处理。本实验用纯 stdin/stdout 练 status / set(没有传感器、没有 GPIO)。源码 chapters/ch04/code/cmd-repl/,只实现 state_init / cmd_status / cmd_set。
$ cd chapters/ch04/code/cmd-repl # 板上:cd ~/cmd-repl
$ make
$ ./build/cmd-repl # 未实现前:status 打不出、set 全报错 → 预期
主循环已给(读行 → 切首词 → 分发)。你写三个函数:初始 30.0/95.0;status 打印当前 bar;cmd_set 用 sscanf 解析 "temp 28.5" / "humidity 60"。
$ 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。
实验三 · 串口命令温控(真 DHT22)
把实验一的 select 和实验二的命令焊进真硬件:接 DHT22 采真温湿度,用命令改阈值,打字和采样同时及时。
- 工程:
chapters/ch04/code/(板上:~/ruyi-riscv-embedded/ch04-lab/) - 学生文件:
cmd_thermo.c(默认make) - 参考实现:
cmd_thermo-sol.c(make sol;先自己写,过关后再对照) - 脚:风扇 IO1_5;DHT22 IO1_6(经 TXS)
- 打字入口:SSH 终端标准输入(本课不另接物理串口线)
成功时你能演示:make 通过;后台静默采样控风扇;敲 status 立刻打出当前温湿度;set 改阈值立刻生效;打字时自动控温不停。
**安全:**风扇仍走继电器触点(第三章接法)。焊接注意烙铁温度与通风。
实验器材
| 东西 | 用途 |
|---|---|
| 荔枝派 4A | 跑 Linux;排针接线 |
| DHT22 模块 | 真温湿度输入 |
| TXS0108E | 1.8 V ↔ 3.3 V 电平转换(排针需焊接) |
| AMS1117-1.8V 小板 | 给 TXS 低压侧(VA)供电 |
| 继电器 + 风扇 + 杜邦线 | 沿用第三章 |
接线与焊接
先焊 TXS 排针:烙铁预热 → 排针插孔 → 背面逐脚上锡 → 检查连锡。
| 从 | 到 | 说明 |
|---|---|---|
| 排针 3.3V | DHT22 正(VCC) | 传感器供电 |
| 排针 GND | DHT22 负(GND);TXS / AMS 共地 | 共地 |
| DHT22 OUT | TXS B1 | 高压侧 3.3 V |
| TXS A1 | 排针 IO1_6 | 低压侧 1.8 V |
| AMS1117 IN | 排针 3.3V | 降压输入 |
| AMS1117 OUT | TXS VA | 低压侧电源 1.8 V |
| 排针 3.3V | TXS VB | 高压侧电源 |
| TXS OE | 接高 | 使能 |
继电器 / 风扇保持第三章:IO1_5 直连信号端,风扇走 NO–COM。
打开 cmd_thermo.c,确认 GPIO_CHIP_PATH、DHT_LINE=6、FAN_LINE=5。首次联调建议保持默认 SIMULATE_SENSOR=1;接好真 DHT 并稳定后再用 -DSIMULATE_SENSOR=0 重编。
**本步成功判据:**能指着线说出「地 / TXS A·B / VA·VB / IO1_6」;知道当前是模拟还是真传感器。整板照片见实验页。
实现命令与 select
打开 cmd_thermo.c。脚手架已提供 DHT 读取、风扇、sample_and_control、read_command_line、main。只改标了 STUDENT TODO 的区块。 先直接 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。
*/
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);
写的时候要想清楚:
dispatch_command为什么先拆词再查表?加一条help时你改哪里?set改的是内存里的阈值:下一轮sample_and_control才会用上。- 阻塞在
fgets上,或固定sleep(2)再读命令,分别饿死哪一边? - 处理完一条命令后若把超时重新设成完整 2 秒,采样节奏会怎样?
对照答案:cmd_thermo-sol.c。make sol 得到 cmd_thermo-sol。建议过关后再打开。
编译运行
主路径与第三章相同:在 x86 Linux 激活 venv-gnu-ruyisdk 后交叉编译,再拷到荔枝派运行;或在荔枝派上直接 make。访问 GPIO 需要 sudo。
# —— 在 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
联调验收
- 启动后等几秒,敲
status:应出现[STATUS] temp=… hum=… - 敲
status:立刻打印完整状态(或「尚无采样」) - 敲
set temp 30,再status:温度 bar 已变 - 敲
set humidity 80,再status:湿度 bar 已变 - 控温进行中连续敲
status:每次立刻出现[STATUS] - (可选,真 DHT)哈气提高湿度,观察是否按湿度 bar 开风扇
Ctrl+C:主程序会先拉低再释放。若被 kill -9 后风扇一直转:make fan-off && sudo ./fan_off。
常见故障
| 现象 | 先查什么 |
|---|---|
implicit declaration / 编译不过 |
四个符号是否写在 read_command_line 上方? |
| 能编译,敲命令没反应 | main_loop 是否把 cmd_fd 放进 select? |
| 命令有回复,风扇不再动作 | 是否卡在阻塞读或固定 sleep? |
真 DHT 一直 [ERR] |
TXS 焊接、VA/VB、OE、共地、IO1_6;先模拟模式验证软件 |
| Permission denied | sudo ./cmd_thermo |
验收表
| 验收项 | 通过条件 |
|---|---|
| 接线 | DHT 经 TXS 到 IO1_6;风扇沿用第三章 |
| 自己写的代码 | 命令 + dispatch + main_loop 在学生文件中;make 通过 |
| 自动控温 | 超温(或超湿)开 / 回落关可演示 |
| status / set | 立刻回复;可改温度 bar 与湿度 bar |
| 同时工作 | 打字时采样不停更 |
**完成标志:**上表可勾;能口头说明 select 返回 0 与返回正数各走哪条路。
板上录像与接线照片见实验页:第四章 · 本章实验 · 三个实验