在MCU中(如classic autosr或其他RTOS),一般可以直接通过往对应的寄存器(地址转为指针)写值,
或者调用一些硬件抽象层或者驱动接口来拉动芯片提供的GPIO。
但是在Linux中,可能不会让应用层直接去访问底层的物理地址,而且配置GPIO或者其他什么IO,往往不只一个寄存器,可能是一组,比较复杂。
SoC中嵌入式Linux控制I/O需要关注的问题
SoC相较于单片机或者MCU,往往复杂得多。读芯片手册和电路板原理图的时候,需要关注很多问题,
I/O种类
目前我看到的I/O有两种,一种是SoC芯片直接提供的GPIO. 一种是通过i2c总线,挂一些I/O扩展器,来实现的外部I/O。
对于SoC板而言,可能这两种同时存在。但是他们的控制方法是不同的。
当使用了总线 I/O扩展芯片来实现外部 I/O, 那么还需要获取和阅读对应扩展芯片的手册。
设备树
需要查阅、核对、修正设备树的能力。
SoC内部的配置和体系各家各自成一套,所以才有设备树。设备树和开发板不配套,可能会造成很多问题,比如/dev下面东西不对。地址不对等等。
另外,要是内核裁剪的有问题,比如一些驱动或者文件系统没有放进来,就更麻烦了,要重新编译内核和内核模块。
GPIO
需要先确认我们需要使用的GPIO芯片,以及该端口位于该GPIO芯片的编号。
然后可以使用gpio文件系统去配置和控制对应的IO。
这里可能需要使用设备树文件来识别控制芯片和偏移。
偏移请参阅SOC的芯片手册。
下面都以TDA4论坛上的问答为例:
[FAQ] TDA4VM/DRA829/AM65xx: Linux: Configuring GPIOs
查询gpio控制芯片和设备树信息:
root@j7-evm:~# find /proc/device-tree/__symbols__/ | grep gpio
/proc/device-tree/__symbols__/main_gpio_intr
/proc/device-tree/__symbols__/main_gpio7
/proc/device-tree/__symbols__/main_gpio5
/proc/device-tree/__symbols__/wkup_gpio0
/proc/device-tree/__symbols__/main_gpio3
/proc/device-tree/__symbols__/main_gpio1
/proc/device-tree/__symbols__/gpio_keys
/proc/device-tree/__symbols__/wkup_gpio_intr
/proc/device-tree/__symbols__/main_gpio6
/proc/device-tree/__symbols__/wkup_gpio1
/proc/device-tree/__symbols__/main_gpio4
/proc/device-tree/__symbols__/main_gpio2
/proc/device-tree/__symbols__/main_gpio0
root@j7-evm:~# cat /proc/device-tree/__symbols__/main_gpio0
/interconnect@100000/gpio@600000
然后对应地址来找驱动文件系统:
root@j7-evm:~# ls -la /sys/class/gpio/
lrwxrwxrwx 1 root root 0 Feb 17 08:38 gpiochip84 -> ../../devices/platform/interconnect@100000/600000.gpio/gpio/gpiochip84
根据对应的地址我们发现,控制芯片是84
如果我们要控制gpio0_127。
就得到:84+127=211.
然后:
root@j7-evm:~# cd /sys/class/gpio/
root@j7-evm:/sys/class/gpio# echo 211 > export
root@j7-evm:/sys/class/gpio# cd gpio211
root@j7-evm:/sys/class/gpio/gpio211# echo out > direction
root@j7-evm:/sys/class/gpio/gpio100# echo 1 > value
Above should export the GPIO and you can do read/writes.
I2C扩展I/O
参考:
TDA4VMXEVM: USER_LED1 & USER_LED2 through I2C GPIO EXPANDER2 (TCA6424ARGJR) - Processors forum - Processors - TI E2E support forums
以TDA4通用开发板为例:
开发板原理图中包含GPIO扩展MAP表。
查表可知I2C0 总线,0x22芯片地址的P26 P27有两个灯(I/O)可以被用户使用。
从图中可知,这是一个IO扩展芯片,和SOC之间通过I2C扩展。
这里写了地址0x22。和芯片型号。
查询对应型号的芯片手册,
8.6.2 Register Descriptions
从datasheet中可知,寄存器0 1 2三个字节,表示各个io的读取结果。
4 5 6是配置输出内容的地方。
8 9 10,配置极性反转。目前用不到。
12 13 14,配置I/O的方向。1 是输入,0 是输出。
那我们需要点亮LED P26 P27。
就如下操作:
查看i2c地址和对应的总线号。
root@j7-evm:~# dmesg | grep i2c
[ 1.082853] i2c /dev entries driver
[ 1.235041] omap_i2c 40b00000.i2c: bus 0 rev0.12 at 100 kHz
[ 1.235521] omap_i2c 40b10000.i2c: bus 1 rev0.12 at 100 kHz
[ 1.239971] omap_i2c 42120000.i2c: bus 2 rev0.12 at 100 kHz
[ 1.265639] omap_i2c 2000000.i2c: bus 3 rev0.12 at 400 kHz
[ 1.266549] omap_i2c 2010000.i2c: bus 4 rev0.12 at 400 kHz
[ 1.267005] omap_i2c 2020000.i2c: bus 5 rev0.12 at 100 kHz
[ 1.292154] omap_i2c 2030000.i2c: bus 6 rev0.12 at 400 kHz
[ 1.295636] omap_i2c 2040000.i2c: bus 7 rev0.12 at 100 kHz
[ 1.296040] omap_i2c 2050000.i2c: bus 8 rev0.12 at 100 kHz
[ 1.319763] omap_i2c 2060000.i2c: bus 9 rev0.12 at 400 kHz
root@j7-evm:/proc/device-tree/__symbols__# cat main_i2c0
/bus@100000/i2c@2000000
可知i2c0 的总线号是 3.
后面使用i2c 工具来操作扩展芯片的寄存器:
#查看设备
root@j7-evm:~# i2cdetect -y -r 3
0 1 2 3 4 5 6 7 8 9 a b c d e f
00: -- -- -- -- -- -- -- -- -- -- -- -- --
10: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
20: UU -- UU -- -- -- -- -- -- -- -- -- -- -- -- --
30: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
40: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
50: -- -- -- -- -- -- -- 57 -- -- -- -- -- -- -- --
60: -- -- -- -- -- -- -- -- -- -- -- -- -- 6d -- --
70: 70 71 -- -- -- -- 76 --
#UU似乎表示这里0x20 0x22是两个内核没有使用的设备,和手册里的i2c芯片地址对应了
# 读取当前寄存器内容
root@j7-evm:~# i2cdump -f -y 3 0x22 b
0 1 2 3 4 5 6 7 8 9 a b c d e f 0123456789abcdef
00: 0c 84 f1 XX 3f fd ff XX 00 00 00 XX 3b f9 ef XX ???X??.X...X;??X
10: XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XXXXXXXXXXXXXXXX
20: XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XXXXXXXXXXXXXXXX
30: XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XXXXXXXXXXXXXXXX
40: XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XXXXXXXXXXXXXXXX
50: XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XXXXXXXXXXXXXXXX
60: XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XXXXXXXXXXXXXXXX
70: XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XXXXXXXXXXXXXXXX
80: 0c 84 f1 XX 3f fd ff XX 00 00 00 XX 3b f9 ef XX ???X??.X...X;??X
90: XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XXXXXXXXXXXXXXXX
a0: XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XXXXXXXXXXXXXXXX
b0: XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XXXXXXXXXXXXXXXX
c0: XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XXXXXXXXXXXXXXXX
d0: XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XXXXXXXXXXXXXXXX
e0: XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XXXXXXXXXXXXXXXX
f0: XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XXXXXXXXXXXXXXXX
# 配置P26 为输出
#P27在14号寄存器上,现在是ef .P27配为0 输出后是6F
root@j7-evm:~# i2cset -y -f 3 0x22 0x0e 0x6f
root@j7-evm:~# i2cdump -f -y 3 0x22 b
0 1 2 3 4 5 6 7 8 9 a b c d e f 0123456789abcdef
00: 0c 84 f1 XX 3f fd ff XX 00 00 00 XX 3b f9 6f XX ???X??.X...X;?oX
10: XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XXXXXXXXXXXXXXXX
20: XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XXXXXXXXXXXXXXXX
30: XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XXXXXXXXXXXXXXXX
40: XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XXXXXXXXXXXXXXXX
50: XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XXXXXXXXXXXXXXXX
60: XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XXXXXXXXXXXXXXXX
70: XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XXXXXXXXXXXXXXXX
80: 0c 84 f1 XX 3f fd ff XX 00 00 00 XX 3b f9 6f XX ???X??.X...X;?oX
90: XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XXXXXXXXXXXXXXXX
a0: XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XXXXXXXXXXXXXXXX
b0: XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XXXXXXXXXXXXXXXX
c0: XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XXXXXXXXXXXXXXXX
d0: XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XXXXXXXXXXXXXXXX
e0: XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XXXXXXXXXXXXXXXX
f0: XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XXXXXXXXXXXXXXXX
#再将P26输出数据配置为0 在6号寄存器上 改成7F
root@j7-evm:~# i2cset -f -y 3 0x22 0x06 0x7F
root@j7-evm:~# i2cdump -f -y 3 0x22 b
0 1 2 3 4 5 6 7 8 9 a b c d e f 0123456789abcdef
00: 0c 84 71 XX 3f fd 7f XX 00 00 00 XX 3b f9 6f XX ??qX???X...X;?oX
10: XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XXXXXXXXXXXXXXXX
20: XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XXXXXXXXXXXXXXXX
30: XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XXXXXXXXXXXXXXXX
40: XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XXXXXXXXXXXXXXXX
50: XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XXXXXXXXXXXXXXXX
60: XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XXXXXXXXXXXXXXXX
70: XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XXXXXXXXXXXXXXXX
80: 0c 84 71 XX 3f fd 7f XX 00 00 00 XX 3b f9 6f XX ??qX???X...X;?oX
90: XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XXXXXXXXXXXXXXXX
a0: XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XXXXXXXXXXXXXXXX
b0: XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XXXXXXXXXXXXXXXX
c0: XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XXXXXXXXXXXXXXXX
d0: XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XXXXXXXXXXXXXXXX
e0: XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XXXXXXXXXXXXXXXX
f0: XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XXXXXXXXXXXXXXXX
可以看到一个灯亮了!!!