ITPub博客

首页 > 架构设计 > 操作系统 > 内核 6.18 上的 Pi pwm-ir-tx 不发送任何内容,而 ir-ctl 仍报告成功

内核 6.18 上的 Pi pwm-ir-tx 不发送任何内容,而 ir-ctl 仍报告成功

操作系统 作者:ITPUB社群 时间:1970-01-01 08:33:46 0 删除 编辑

内核 6.18 上的 Pi pwm-ir-tx 不发送任何内容,而 ir-ctl 仍报告成功

TL;DR:在 Raspberry Pi OS 的 6.18 内核上,使用 dtoverlay=pwm-ir-tx 的红外发射器不会发送任何可用的内容。一个 100 ms 脉冲、100 ms 间隔、100 ms 脉冲的测试码被施加到 PWM 上,表现为 7 微秒内的四次状态变化,全部来自一次定时器中断,因此 LED 总共只亮了几微秒。ir-ctl --send 仍然以 0 退出,并且仍然耗时约 350 ms,因为 LIRC 核心在返回前会等待信号的长度。因此,无论是退出码还是耗时都无法暴露问题。原因是 6.18 的一次清理,被描述为切换到“一个等效的更高层辅助函数”,它反转了驱动定时器回调中的循环条件。在同一引脚上切换到 dtoverlay=gpio-ir-tx 后,发送出了正确计时的 100 ms 脉冲串。该工具包的 check-ir-tx.sh 可以找到受影响的发射器并对其进行测量。上游报告:raspberrypi/linux#7678 。

症状

Raspberry Pi 4B,Raspberry Pi OS(Debian 13,arm64),内核 6.18.34+rpt-rpi-v8 。在 /boot/firmware/config.txt 中添加了一行:

dtoverlay
=
pwm
-
ir
-
tx

重启后一切看起来都正常:

rc rc0: PWM IR Transmitter as /devices/platform/pwm-ir-transmitter/rc/rc0
rc rc0: lirc_dev: driver pwm-ir-tx registered at minor = 0, no receiver, raw IR transmitter

$ pinctrl get 18
18: a5    pd | lo // GPIO18 = PWM0_0

$ ir-ctl -d /dev/lirc0 -f
Send features /dev/lirc0:
 - Device can send raw IR
 - IR scancode encoder
 - Set carrier
 - Set duty cycle

一个 100 ms 开、100 ms 关、100 ms 开、38 kHz 的测试信号:

$
printf
'pulse 100000\nspace 100000\npulse 100000\n'
>
ir300.txt
$
ir-ctl
-d
/dev/lirc0
--carrier
=
38000
--send
=
ir300.txt
send 1:
rc
=
0
elapsed_ms
=
359
send 2:
rc
=
0
elapsed_ms
=
345
send 3:
rc
=
0
elapsed_ms
=
349

退出码 0,耗时约为信号长度,dmesg 中没有任何内容。内核的 pwm_apply 跟踪事件显示了 PWM 实际被要求做什么:

ir-ctl-1230 [002] d.h1.  42.847753: pwm_apply: pwmchip0.0: period=26316 duty_cycle=13158 polarity=0 enabled=1 err=0
ir-ctl-1230 [002] d.h1.  42.847758: pwm_apply: pwmchip0.0: period=26316 duty_cycle=13158 polarity=0 enabled=0 err=0
ir-ctl-1230 [002] d.h1.  42.847759: pwm_apply: pwmchip0.0: period=26316 duty_cycle=13158 polarity=0 enabled=1 err=0
ir-ctl-1230 [002] d.h1.  42.847760: pwm_apply: pwmchip0.0: period=26316 duty_cycle=13158 polarity=0 enabled=0 err=0

载波设置是正确的(26,316 ns 的周期是 38 kHz,占空比 50%)。时序不对:两个 100 ms 脉冲分别只亮了 5 µs 和 1 µs,整个码在 7 µs 内就结束了。d.h1. 中的 h 表示所有四次都是在硬中断上下文中应用的,来自同一个回调。重启后,同样的测试在 6 µs 内给出了相同的四个边沿。

上游的报告来自一台运行 32 位内核( 6.18.50+rpt-rpi-v7 )的 Pi 3B+,其 LED 一直不亮。这里是运行 64 位内核的 Pi 4B,因此它不限于某一型号或架构。

为什么容易被忽略

你通常会做的每一项检查都能通过。overlay 加载了,/dev/lirc0 以正确的特性出现,引脚处于 PWM 模式,并且 ir-ctl 返回 0。

时序也无济于事。人们很容易认为,一次传输若坍缩到微秒级,就会让 ir-ctl 立即返回,而测量它就能捕捉到问题。事实并非如此:无论驱动做了什么,LIRC 核心(lirc_dev.c 中的 lirc_transmit())都会休眠,直到信号的完整持续时间过去后,write 才返回。对于 300 ms 的信号,ir-ctl 耗时 345 到 359 ms,看起来完全正常。

而且红外是不可见的。除非你把手机摄像头对准 LED,否则唯一的症状就是电视或功放没有反应,这看起来像是代码错误、LED 太弱或角度不对。那些用了多年正常工作的红外发射器的人,会在更新内核后开始调试他们的代码。

如果你用长信号测试,还有一个陷阱:一个 600 ms 的测试代码(三个 200 ms 段)被直接拒绝,报错 failed to send: Invalid argument (exit 74),因为 LIRC 限制单次传输总时长为 500 ms(IR_MAX_DURATION)。这是一个诚实的错误,但它会让你去错误的地方查找。

到底发生了什么

pwm-ir-tx 有两种发送方式。如果 PWM 驱动可以从原子上下文调用,它就从 hrtimer 回调中编程每个边沿;否则它在边沿之间休眠。树莓派的 PWM 驱动是原子的,并且 dmesg 中没有显示 "TX will not be accurate as PWM device might sleep" 这一行,所以树莓派使用的是定时器路径。

定时器回调应用一个边沿,安排下一个到期时间,如果已经落后就循环,以在延迟尖峰后追赶。直到 6.17,循环条件一直是:

now
=
timer
->
base
->
get_time
();
}
while
(
hrtimer_get_expires_tv64
(
timer
)
<
now
);

也就是说,只要下一个到期时间已经过去就继续。提交 44d7fb8acdf8("media: pwm-ir-tx: Avoid direct access to hrtimer clockbase",首次出现在 v6.18)将其替换为其提交信息所称的 "一个等价的高层辅助函数":

}
while
(
hrtimer_expires_remaining
(
timer
)
>
0
);

hrtimer_expires_remaining() 是到期时间减去当前时间。当下一个到期时间在未来时,它是正值,而在添加下一个边沿的持续时间之后,这总是成立的。因此,过去只在落后时才运行的循环,现在只要准时就会运行:第一个回调依次应用每个边沿,完成传输并停止定时器。这正是上面的跟踪结果。同一行代码也存在于 rpi-6.18.y 和当前主线中。

修复方法

在条件被恢复之前,在同一引脚上使用 bit-banged 驱动:

dtoverlay
=
gpio
-
ir
-
tx
,
gpio_pin
=
18

而不是 dtoverlay=pwm-ir-tx,然后重启。同样的测试信号,通过 gpio_value 事件跟踪:

rc=0 elapsed_ms=331
burst length_ms=105.98 toggles=7604
burst length_ms=99.99  toggles=7600
gap between bursts ms=100.01

两个约 100 ms 的突发,相隔 100 ms,每个有 7,600 次翻转,即 100 ms 的 38 kHz。gpio-ir-tx 通过在 CPU 上忙等待来生成载波,因此在发送代码时会消耗 CPU 时间,其时序可能会受到其他负载的干扰。对于偶尔发送代码的红外发射器来说,与什么都不发送相比,这是一个公平的权衡。

要检查一台机器,check-ir-tx.sh 会列出红外发射器,在 6.18 或更高版本内核上标记 pwm-ir-tx,并在使用 --probe(以 root 身份)时发送一个 30 ms 的测试码,并测量其 PWM 边沿在 trace 中的分布。在这台带有 pwm-ir-tx 的 Pi 上,它报告在 7 µs 内有 4 个边沿。使用 gpio-ir-tx 时则通过。

可推广的习惯

当一项变更被描述为等价时,检查那个会使它不等价的关键点。这里的关键点是比较的方向:“过期时间早于现在”和“剩余时间为正”听起来像是同一个问题,实际上却是相反的。一次只交换了值的获取方式的重构,仍然可能翻转条件的含义,而提交信息和代码审查都把它读作无操作。

另一个习惯是关于什么才算作测试。退出码和持续时间都是软件对自身做出的声明。对于任何离开机器的内容——红外码、数据包、对磁盘的写入——都要测量实际发生了什么:一个 trace 事件、一台摄像头、另一端的抓包。

这是 Pi 的 6.18 内核第二次悄悄拿走了在 6.12 上还能工作的东西:旧版 iptables 模块不再被构建,而配置仍然声称它们已启用。

工具包

本文的修复方案已在工具包中作为经过测试、可直接运行的脚本提供。


转载声明:本文为转载整理,原文版权归原作者及原站点所有。

原文来源:DEV.to

原创地址:https://dev.to/homelabpm/pi-pwm-ir-tx-on-kernel-618-sends-nothing-and-ir-ctl-still-reports-success-5gc5

原文作者:The Homelab Postmortem;如有侵权,请联系管理员删除。

请登录后发表评论 登录
全部评论

注册时间:2022-11-21

  • 博文量
    1090
  • 访问量
    546908