前言

打算把手上的OrangePi 3B刷成电视盒子给家里用,但是OrangePi 3B没有板载红外模块,于是从官方店铺下单了外置红外模块和遥控器,结果到手发现官方并没有进行适配,没办法只能自己适配了

Tips:本篇教程有点长,并且不适合小白使用

准备环节

Tips:不要买OrangePi官方店铺的那个红外传感器,那是红外光电二极管 (IR Photodiode),不是集成红外接收头 (Integrated IR Receiver)!!!

下载工具

进入OrangePi官网 http://www.orangepi.cn/

进入OrangePi 3B的下载页面 http://www.orangepi.cn/html/hardWare/computerAndMicrocontrollers/service-and-support/Orange-Pi-3B.html

Tips:OrangePi官方发布的各种资料目前基本上都在百度网盘上,建议提前准备一个SVIP账户

找到“官方工具”,打开链接,需要下载的有:

  • Android和Linux镜像烧录工具-RKDevTool和驱动程序

下载该文件夹内所有内容。

下载 Android Image Kitchen 用于打包和解包镜像:https://xdaforums.com/t/tool-android-image-kitchen-unpack-repack-kernel-ramdisk-win-android-linux-mac.2073775/

下载文档

下载最新的用户手册

下载镜像

下载最新带有“Android11-box”字样的镜像

下载并解压Android源码

下载总共25G+的Android源码分卷压缩包

校验文件md5:

1
md5sum -c RK356X_Android11.tar.gz.md5sum

解压压缩包

1
cat RK356X_Android11.tar.gz0* | sudo tar -xvzf - -C /目标/目录

准备编译环境

准备一个空余硬盘空间至少有200G+的硬盘

准备Linux编译环境,建议Ubuntu22.04,当然也可以是更新的系统,只是编译脚本会用到python2之类的,到时候也能改脚本

按照用户手册中“Android11 源码的编译方法”这节,配置好编译依赖

1
2
3
4
5
sudo apt-get update
sudo apt-get install -y git gnupg flex bison gperf build-essential \
zip curl zlib1g-dev gcc-multilib g++-multilib libc6-dev-i386 libncurses5 \
lib32ncurses5-dev x11proto-core-dev libx11-dev lib32z1-dev ccache \
libgl1-mesa-dev libxml2-utils xsltproc unzip liblz4-tool

如果系统较新,可能有些软件包已失效,我使用Debian13编译,替换了部分软件包:

liblz4-tool -> lz4
libncurses5 -> libncurses6 (或 libncurses-dev)
python -> python3 + python-is-python3

1
2
3
4
5
6
sudo dpkg --add-architecture i386
sudo apt-get update
sudo apt-get install -y git gnupg flex bison gperf build-essential \
zip curl zlib1g-dev gcc-multilib g++-multilib libc6-dev-i386 \
libncurses-dev lib32ncurses-dev x11proto-dev libx11-dev lib32z1-dev ccache \
libgl1-mesa-dev libxml2-utils xsltproc unzip lz4 python3 python-is-python3 libssl-dev

开始

OrangePi官方并没有对红外模块进行适配,因此需要自行编译,不过仅用编译内核,不用编译Android本体,单次编译时间压缩至2~3分钟。

安装系统

这里我们采用修补boot的方式,所以首先需要安装完整系统。

按照用户手册中“烧录 Android 镜像到 TF 卡中的方法”这节方法,安装对应驱动,使用RKDevTool将官方原版镜像烧录OrangePi 3B中。

烧录完成后启动OrangePi 3B,插入显示器,确保系统已经能正常启动。

开启开发者模式

像普通手机那样,在设置里找到版本号多次快速点击开启开发者模式,进入开发者模式菜单,为了方便起见,这里使用网络ADB而不是USB ADB

解包官方镜像

使用RKDevTool中“高级功能”内的“解包”功能,将boot.img从官方镜像中分离,重命名为boot-official.img备用

准备红外模块

红外模块一般是三个引脚,分别是VCC,GND和Signal

我使用的OrangePi官方的红外模块描述写的是VCC接3.3V,但是实测下来3.3V貌似有问题无法正常工作,需要接到5V

Signal则是按照描述接在PIN37,也就是引脚图上的GPIOO3_D3脚(后面的教程均按该引脚来写)

GND引脚随便找个GND PIN就行

修改前准备

处理环境变量

1
2
3
export BOARD=orangepi3b
source build/envsetup.sh
lunch rk356x_box-userdebug

修改内核设备树 (DTS)

kernel/arch/arm64/boot/dts/rockchip/中找到rk3566-orangepi-3b.dts打开

/ {...}内部新增下面配置:

1
2
3
4
5
6
7
ir: ir-receiver {
compatible = "gpio-ir-receiver";
gpios = <&gpio3 RK_PD3 GPIO_ACTIVE_LOW>;
pinctrl-names = "default";
pinctrl-0 = <&ir_int>;
status = "okay";
};

&pinctrl{...}中新增下面配置:

1
2
3
4
5
ir {
ir_int: ir-int {
rockchip,pins = <3 RK_PD3 RK_FUNC_GPIO &pcfg_pull_up>;
};
};

修改菜单配置

加载默认配置

1
cd kernel
1
make ARCH=arm64 rockchip_defconfig

进入菜单配置页面

1
make ARCH=arm64 menuconfig

进入Device Drivers

进入Remote Controller support(这一项本身需要打星 *)

进入Remote controller decoders

建议全部勾选(或者至少勾选 Enable IR raw decoder, NEC protocol, RC-6 protocol)或者你知道你的遥控器是什么协议,官方的遥控器经测试是NEC

进入Remote Controller devices

找到GPIO IR remote control打星 *

按右方向键选择<Save>,回车保存为.config(默认名即可)

<Exit>一路退出。

编译测试

开始编译

1
make ARCH=arm64 Image rockchip/rk3566-orangepi-3b.dtb -j$(nproc)

编译完成后,找到如下文件:

  • Image 位于kernel/arch/arm64/boot/
  • rk3566-orangepi-3b.dtb 位于kernel/arch/arm64/boot/dts/rockchip/

制作resource.img

这里需要用到源码下kernel/scripts/resource_tool这个工具,不过官方提供的源码并没有二进制程序,需要手动从resource_tool.c编译

1
2
gcc scripts/resource_tool.c -o scripts/resource_tool
chmod +x scripts/resource_tool

最后使用dtb制作resource.img

1
./scripts/resource_tool arch/arm64/boot/dts/rockchip/rk3566-orangepi-3b.dtb logo.bmp logo_kernel.bmp

关闭AVB校验

首先在源码里找到avbtool

1
find . -name "avbtool"

通常是external/avb/avbtool

然后生成禁用AVB校验的img

1
2
chmod +x external/avb/avbtool
python3 external/avb/avbtool make_vbmeta_image --flag 2 --padding_size 4096 --output vbmeta_disabled.img

不过这个源码里带的avbtool是用python2写的,如果你的编译环境是python2没什么问题,如果是python3则会出现一些错误,可以将报错信息询问AI修改脚本解决

最后会在当前目录下生成一个几KB大小的vbmeta_disabled.img文件

制作boot镜像

回到Windows,将boot-official.img拖到Android Image Kitchenunpackimg.bat文件上进行解包

解包完成后当前目录会多出ramdisksplit_img两个文件夹

进入split_img文件夹,按照下面对应表进行重命名后替换

  • rk3566-orangepi-3b.dtb -> boot-official.img-dtb
  • Image -> boot-official.img-kernel
  • resource.img -> boot-official.img-second

然后用记事本打开boot-official.img-board文件,填入orangepi3b保存

最后双击repackimg.bat进行打包,得到image-new.img新boot镜像

制作Misc镜像(可选)

如果进入Android Recovery模式次数过多,可能会导致直接进入Android Recovery模式,可以通过擦除Misc分区解决

生成空白misc_wipe.img:

1
fsutil file createnew misc_wipe.img 49152

刷入boot.img镜像

启动RKDevTool,和之前一样,跟着用户手册让OrangePi 3B进入MaskRom模式

将MiniLoaderAll烧录至SPINOR,然后切换存储至SD卡

进入“下载镜像”标签页,勾选“Boot”分区,并点击一下最右边方框,选择之前制作的image-new.img,然后右键空白处,选择添加项,在名字一栏写Vbmeta并勾选,同样的选择vbmeta_disabled.img

如果制作了misc_wipe.img想要擦除Misc分区,同样的右键新建项,名字一栏写Misc并勾选,选择misc_wipe.img即可

接着点击“设备分区表”按钮,读取设备分区地址信息,这里会有几个分区没有匹配到地址,例如ResourceKernelSystem分区,这是正常现象,只要Boot分区和Vbmeta分区(Misc分区可选)的地址能正常读出即可

最后点击“执行”按钮刷入镜像

调试

如果系统顺利启动,就已经成功一半了

确保开启了开发者模式并打开了网络ADB

如果电脑没有安装ADB,源码里的RKTool文件夹里也有ADB工具

和OrangePi 3B处于同一局域网下,连接ADB设备

不用进行配对,直接连接就行

1
adb connect <OrangePi 3B的IP地址>

连接上后进入shell

1
adb shell

切换至root

1
su

首先检查内核配置

1
zcat /proc/config.gz | grep -i "CONFIG_IR_GPIO_CIR"

正确情况下会输出CONFIG_IR_GPIO_CIR=y,代表内核已经启用

然后查看设备节点是否有红外设备

1
ls -d /sys/firmware/devicetree/base/ir*

正确情况下会输出ir-receiver之类的设备文件夹

进入该文件夹,读取compatiblestatus

1
2
3
4
cat compatible
# 输出应该是 gpio-ir-receiver
cat status
# 输出应该是 okay

如果满足上方两个条件,查看ir事件

1
getevent -l

一般会有类似下面的输出,包含gpio_ir_recv输入

1
2
3
4
5
6
7
8
9
10
11
12
13
 rk356x_box:/ $ getevent -l

add device 1: /dev/input/event1

name: "gpio_ir_recv"

add device 2: /dev/input/event2

name: "rk-headset"

add device 3: /dev/input/event0

name: "rk805 pwrkey"

执行cat /sys/kernel/debug/gpio | grep "gpio-123"也会有下面输出,表示正在使用的GPIO是哪个驱动

1
gpio-123 (                    |ir-receiver         ) in  hi 

然后开始选择ir协议

1
cd /sys/class/rc/rc0/protocols

会看到当前支持的所有协议,选择要监听的协议

1
echo "+nec" > /sys/class/rc/rc0/protocols

当然也可以开启一堆协议

1
echo "+rc-5 +rc-6 +jvc +sony +sanyo +sharp +mce_kbd +xmp +imon" > /sys/class/rc/rc0/protocols

开启的协议会用[]包围。

执行getevent -l用遥控器对着红外设备按下键,正常情况下会有类似下面的输出:

1
2
3
4
/dev/input/event1: EV_MSC       MSC_SCAN             0000045c
/dev/input/event1: EV_SYN SYN_REPORT 00000000
/dev/input/event1: EV_MSC MSC_SCAN 0000045c
/dev/input/event1: EV_SYN SYN_REPORT 00000000

如果满足上面所有情况,则代表硬件已经设置正确并且系统能正常读取解析来自遥控器的信号

Tips:这里建议明确出遥控器使用的是哪种协议,因为后面会使用到

制作Keymap

最后,需要制作Keymap用于将遥控器信号解析出来的十六进制的键值码映射到对应的按键上

同样是执行getevent -l,然后将遥控器上所有的按键按一遍,并记录对应的键值码,只用记录后三位,例如45c

经测试,OrangePi官方的遥控器键值码如下:

按键 键值码
电源键 41a
TV 44d
VOD 443
音量- 419
音量+ 445
menu 45d
mouse 41b
444
41c
OK 45c
448
41d
home 41f
return 40a
1 413
2 410
3 411
4 40f
5 40c
6 40d
7 40b
8 408
9 409
TV-SYS 458
0 447
SETUP 453

拿到对应的键值码后,开始编写keymap文件

回到Linux编译环境,在kernel/drivers/media/rc/keymaps/下新建一个文件叫rc-orangepi-ir.c

写入下方内容:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
// 文件路径: kernel/drivers/media/rc/keymaps/rc-orangepi-ir.c
#include <media/rc-map.h>
#include <linux/module.h>

static struct rc_map_table orangepi_ir_table[] = {
{ 0x41a, KEY_POWER }, // 电源键
{ 0x445, KEY_VOLUMEUP }, // 音量+

{ 0x44d, KEY_TV }, // TV
{ 0x443, KEY_VIDEO }, // VOD (映射为 VIDEO)
{ 0x419, KEY_VOLUMEDOWN }, // 音量-

{ 0x45d, KEY_MENU }, // menu
{ 0x41b, KEY_SWITCHVIDEOMODE }, // mouse (通常映射为切换鼠标模式,或 KEY_FN)

{ 0x444, KEY_UP }, // 上
{ 0x41c, KEY_LEFT }, // 左
{ 0x45c, KEY_ENTER }, // OK (Android 确定键通常用 ENTER)
{ 0x448, KEY_RIGHT }, // 右
{ 0x41d, KEY_DOWN }, // 下

{ 0x41f, KEY_HOME }, // home
{ 0x40a, KEY_BACK }, // return

{ 0x413, KEY_1 }, // 1
{ 0x410, KEY_2 }, // 2
{ 0x411, KEY_3 }, // 3
{ 0x40f, KEY_4 }, // 4
{ 0x40c, KEY_5 }, // 5
{ 0x40d, KEY_6 }, // 6
{ 0x40b, KEY_7 }, // 7
{ 0x408, KEY_8 }, // 8
{ 0x409, KEY_9 }, // 9
{ 0x447, KEY_0 }, // 0

{ 0x458, KEY_TV2 }, // TV-SYS (映射为 TV2 或其他功能键)
{ 0x453, KEY_SETUP }, // SETUP
};

static struct rc_map_list orangepi_ir_map = {
.map = {
.scan = orangepi_ir_table,
.size = ARRAY_SIZE(orangepi_ir_table),
.rc_proto = RC_PROTO_NEC, // 这里指定协议为 NEC
.name = "rc-orangepi-ir", // 名字必须和 DTS 里的 linux,rc-map-name 一致
}
};

static int __init init_rc_map_orangepi(void)
{
return rc_map_register(&orangepi_ir_map);
}

static void __exit exit_rc_map_orangepi(void)
{
rc_map_unregister(&orangepi_ir_map);
}

module_init(init_rc_map_orangepi)
module_exit(exit_rc_map_orangepi)

MODULE_LICENSE("GPL");
MODULE_AUTHOR("OrangePiUser");

接着在kernel/drivers/media/rc/keymaps/Makefile中按照格式接上rc-orangepi-ir.o

然后修改设备树 (DTS)

回到kernel/arch/arm64/boot/dts/rockchip/rk3566-orangepi-3b.dts,在ir-receiver{...}内添加一条:

1
linux,rc-map-name = "rc-orangepi-ir";

保险起见,完全重新生成boot

1
2
cd kernel
make distclean

最后从修改前准备这节重新编译内核和制作boot.img

测试

重新刷入boot.img等镜像后,开机测试红外功能,这会应该能正常使用遥控器遥控了

同样的使用getevent -l查看ir事件

这会可以看到有下面之类的事件出现:

1
2
/dev/input/event1: EV_KEY       KEY_DOWN             DOWN 
/dev/input/event1: EV_KEY KEY_DOWN UP

后记

OrangePi官方的红外模块貌似有点不好用,遥控器稍微远一点信号质量就急剧下降,S脚点位拉不下来,识别不到,这会手上没电阻电容,不方便处理,只能等之后再玩了。

更新:继续和Gemini聊了一会,才知道tmd那玩意是红外光电二极管 (IR Photodiode),不是用来接收遥控器信号的“接收头”🤣,虽然也是被我用来当信号接收器了,我说怎么tm那么难用,遥控器离30cm以上就收不到信号了。。。应该是买集成红外接收头 (Integrated IR Receiver)。。。不过教程没问题,有问题的是那个逆天硬件🤣