OrangePi Zero 3学习笔记(Android篇)9 - I2C和从设备
目录
1. Ubuntu X86中实现
1. 1 编译内核
1.2 ko的方式(首选)
1.3 I2C设备的匹配
1.3.1 ACPI方式
1.3.2 new_device/delete_device
1.4 验证
2. Android ARM上实现
2.1 内核配置
编辑
2.2 修改设备树
2.3 修改权限
3. Android AT24C32支持
通过FT4232H的i2c接口驱动OLED,作为系统console的屏幕显示(失败)。最后是使用EEPROM验证。
1. Ubuntu X86中实现
1. 1 编译内核
先在X86虚拟机里面驱动OLED(SSD1307)128x64单色屏作为屏幕显示。
下载好Linux的源代码,
uname -r
apt-cache search linux-source
sudo apt-get install linux-source-6.5.0
净化内核源码
sudo make mrproper
将现有的内核版本的config配置信息复制到现在目录下的 .config里面
cp /boot/config-6.8.0-49-generic ./.config
配置选择Device Drivers > Graphics support > Frame buffer Devices -> Solomon SSD1307 framebuffer support
安装缺失的库(把debian文件夹拷贝到源代码文件夹内),编译
sudo make
sudo make bzImage
sudo make install
sudo make modules_install
切换内核的步骤,先执行:
sudo update-initramfs -c -k 6.5.13
这里的6.5.13是新编译的Linux内核版本号,可以看modules_install输出信息,例如:INSTALL /lib/modules/6.5.13/kernel/ubuntu/ubuntu-host/ubuntu-host.ko
然后更新grub
sudo update-grub
系统复位按ESC,进Advance...菜单(第二个),选择6.5.13内核
1.2 ko的方式(首选)
为了避免编译内核,可以将内核源码中的linux-source-6.5.0/drivers/video/fbdev/ssd1307fb.c文件拷贝出来,新建一个makefile文件
ifeq ($(KERNELRELEASE), )
KERNELDIR := /lib/modules/$(shell uname -r)/build
PWD :=$(shell pwd)
default:$(MAKE) -C $(KERNELDIR) M=$(PWD)
clean:rm -rf *.mk .tmp_versions Module.symvers *.mod.c *.o *.ko .*.cmd Module.markers modules.order *.a *.mod
load:insmod ssd1307fb.ko
unload:rmmod ssd1307fb
install: defaultrmmod ssd1307fb || true
# insmod ssd1307fb.ko || truemkdir -p /lib/modules/$(shell uname -r)/kernel/drivers/video/fbdev/ || truecp -f ./ssd1307fb.ko /lib/modules/$(shell uname -r)/kernel/drivers/video/fbdev/ || truemodprobe ssd1307fb || true
# depmod -a
uninstall:rmmod ssd1307fb || truerm -rf /lib/modules/$(shell uname -r)/kernel/drivers/video/fbdev/ssd1307fb.ko || true
# depmod -a
elseobj-m := ssd1307fb.o
endif
编译make,安装sudo make install,安装后可以查看是否有对应的驱动(驱动名为ssd1307fb)
1.3 I2C设备的匹配
1.3.1 ACPI方式
对于X86/AMD64平台,匹配机制采用的是ACPI(Advanced Configuration and Power Interface)匹配表
参考文档:基于 ACPI 的设备枚举 — Linux 内核文档
不过这种方式资料好少,没找到具体怎么改的。
1.3.2 new_device/delete_device
参考文档:linux源码中的文档Documentation/i2c/slave-eeprom-backend.rst
例如在i2c-1下挂载了AT24C32(使用FT4232H的i2c接AT24C32,比OLED实现要简单的多),地址为0x50,新增这个设备的命令:
echo slave-24c32 0x1050 | sudo tee /sys/bus/i2c/devices/i2c-1/new_device
执行完这个后可以在/sys/bus/i2c/devices里面新增这个设备
pq@pq-VirtualBox:/sys/bus/i2c/devices$ ls
1-1050 i2c-0 i2c-1 i2c-2 i2c-3 i2c-4
但是在1-1050里面没有eeprom。
删除这个设备
echo 0x1050 | sudo tee /sys/bus/i2c/devices/i2c-1/delete_device
参考at24.c里面的at24_ids数组,里面字符串"24c32"为关键字。重新添加
echo 24c32 0x50 | sudo tee /sys/bus/i2c/devices/i2c-1/new_device
这时候在1-0050下可以看到eeprom
:/sys/bus/i2c/devices/1-0050$ ls
1-00501 eeprom name subsystem uevent
driver modalias power supplier:regulator:regulator.0
写数据到EEPROM:
sudo sh -c 'echo "Hello World!" > /sys/bus/i2c/devices/1-0050/eeprom'
读回来保存到bin文件中
sudo cat eeprom >> ~/1.bin
所以对于ssd1306 oled来说,方法虽然类似,不过会出错,需要改源代码中数组ssd1307fb_i2c_id
static const struct i2c_device_id ssd1307fb_i2c_id[] = {{ "ssd1305fb", (kernel_ulong_t)&ssd1307fb_ssd1305_deviceinfo },{ "ssd1306fb", (kernel_ulong_t)&ssd1307fb_ssd1306_deviceinfo },{ "ssd1307fb", (kernel_ulong_t)&ssd1307fb_ssd1307_deviceinfo },{ "ssd1309fb", (kernel_ulong_t)&ssd1307fb_ssd1309_deviceinfo },{ }
};
编译安装新驱动后,新增设备
echo ssd1306fb 0x3c | sudo tee /sys/bus/i2c/devices/i2c-1/new_device
由于ssd1307fb.c里面只实现了设备树的方式匹配I2C,所以需要添加这种方式的方式,否则会出现错误,添加失败。参考at24.c
#include <linux/of_device.h>struct device_node *of_node = dev->of_node;
const struct i2c_device_id *id;id = i2c_match_id(ssd1307fb_i2c_id, to_i2c_client(dev));//par->device_info = device_get_match_data(dev);
if (of_node && of_match_device(ssd1307fb_of_match, dev))par->device_info = of_device_get_match_data(dev);
else if (id)par->device_info = (void *)id->driver_data;
elsepar->device_info =acpi_device_get_match_data(dev);
if(!par->device_info) {dev_err(dev, "failed to get device info\n");goto fb_alloc_error;
}
1.4 验证
先改变用户组,改完后重启系统
sudo usermod -aG video $USER
写入数据
cat /dev/urandom | sudo tee /dev/fb1 > /dev/null
提示"tee: /dev/fb1: 无效的参数",不知道如何解决。
2. Android ARM上实现
一般arm上实现,需要使用设备树的方式。
2.1 内核配置
除了和X86方式一样的配置,应该还需要打开Framebuffer Console Support.
2.2 修改设备树
在编译内核信息部分,可以找到dts对应的位置
不过文件夹内没有这个board.dts文件
:~/Project/H618-Android12-Src/longan/kernel/linux-5.4/arch/arm64/boot/dts/sunxi$ ls
Makefile sun50iw10p1.dtsi sun50iw9.dtsi
在运行soure build/envsetup.sh的提示信息可以看到:
cbd -- goto longan/device/config/chips/{ic}/configs/{board} dir
执行命令cbd,进入文件夹/longan/device/config/chips/h618/configs/p2,可以看到文件board.dts。里面的配置信息能对应OrangePi Zero3
model = "Orange Pi Zero 3";compatible = "allwinner,h616", "arm,sun50iw9p1";
另外,全志SOC对i2c总线命名为twi(n),对于OrangePi Zero3,开放给用户使用的i2c为i2c3
用到的IO口为PH4和PH5,在H618-Android12-Src/longan/device/config/chips/h618/configs/p2/board.dts找到twi3,
&twi3 {clock-frequency = <400000>;pinctrl-0 = <&twi3_pins_a>;pinctrl-1 = <&twi3_pins_b>;pinctrl-names = "default", "sleep";twi_drv_used = <1>;status = "okay";
};
然后再找一下twi3_pins_a,可以对应上板子的信息
twi3_pins_a: twi3@0 {pins = "PH4", "PH5";function = "twi3";drive-strength = <10>;bias-pull-up;};
在twi3里面添加ssd1306的设备信息:
ssd1306: oled@3c {compatible = "solomon,ssd1306fb-i2c";reg = <0x3c>;solomon,height = <64>;solomon,width = <128>;};
@3c和reg = <0x3c>表示该oled的i2c地址为0x3c(7位地址)
compatible是关键字,配合ssd1307fb.c中ssd1307fb_of_match数组,当一致时获取后面的属性数组,例如这里的配置和下面的属性是对应的。
{.compatible = "solomon,ssd1306fb-i2c",.data = (void *)&ssd1307fb_ssd1306_deviceinfo,
},
属性width和height表示屏幕的宽和高。 其他属性可以参考代码中的ssd1307fb_probe函数。编译内核烧录后进入系统可以看到多了对应的设备:
console:/sys/bus/i2c/devices/3-003c $ ls
modalias name of_node power subsystem uevent waiting_for_supplier
console:/sys/bus/i2c/devices/3-003c $ cat name
ssd1306fb-i2c
然后设备树中找到disp分支,按照屏幕修改参数
fb1_format = <0>;fb1_width = <128>;fb1_height = <64>;
2.3 修改权限
修改i2c-3的权限,在文件device/softwinner/apollo/common/system/ueventd.sun50iw9p1.rc中添加:
/dev/i2c-3 0777 system system
/dev/fb1 0770 system system
不过dev下找不到fd1这个设备,似乎现在android上不支持framebuffer,需要使用drm的方式实现。
3. Android AT24C32支持
内核里面选择NVMe Support(这一步和Linux下不同)
和I2C EEPROMs
然后在设备数twi3里面添加新的从设备
at24c32: eeprom@50 {status = "okay";compatible = "atmel,24c32";reg = <0x50>;};
此时可以在设备下看到这个设备
console:/ $ ls /sys/bus/i2c/devices/
3-003c 3-0050 5-0036 i2c-3 i2c-5
但是在android端需要通过nvmem读写这个设备
console:/sys/bus/nvmem/devices/3-00500 $ ls
nvmem of_node power subsystem type uevent waiting_for_supplier
添加这个设备的权限,在文件device/softwinner/apollo/common/system/init.sun50iw9p1.rc中的“on post-fs-data”里面添加:
chmod 0644 /sys/bus/nvmem/devices/3-00500/nvmem