一,前言
爽11选东东花费了我一周的业余时间,趁着周末又调整回了常规的学习状态。之前做的applepaper是虚拟的platform设备,最近把10年前配合单片机以前买的电子元器件都翻出来看看能否资源利用。接着就找到了eeprom和电阻,最适合的就是先研究i2c子系统了。
设备驱动开发详解中的i2c子系统看完后,5.4内核i2c源码看了下。基本上了解了i2c自系统的框架。就是包括i2c总线驱动(adapter+algorithm)和i2c设备驱动(client,driver)。另外,我一开始理解框架的时候最大的疑惑就是at24.c中为什么没有file_operate的read,write等,只有probe和release。原因是read,write都在i2c-dev.c文件中,它实现的client是虚拟的,它的主体是i2c_driver成员函数+字符设备驱动。
APP测试工程路径在我的gitee,可参考10_i2c_eeprom文件夹。
二,配置eeprom驱动
在Document中我看到了i2c-tools,所以我理解只要i2c的eeprom驱动完成后,我不写用户程序也可以操控eeprom了。而对于eeprom驱动及i2c总线驱动只要简单修改下设备树即可实现。
1,先设计原理图
因为i2c0已经被bb black用了连接at24c32,并且写保护。所以bb black开发板引出的是i2c1和i2c2,由于已经定于了i2c2的功能脚,所以我直接用i2c2来设计电路,地址为0x51
2. 编译dts
am335x-bone-common.dtsi删除原来的i2c2修改为如下
&i2c2 {
pinctrl-names = "default";
pinctrl-0 = <&i2c2_pins>;
status = "okay";
clock-frequency = <100000>;
apple_eeprom0: apple_eeprom0@51 {
compatible = "atmel,24c02";
reg = <0x51>;
#address-cells = <1>;
#size-cells = <1>;
apple_eeprom0_data: apple_eeprom0_data@0 {
reg = <0 0x100>;
};
};
};
3.遇到的问题
a, 由于万用表的9v电池失效了,所以电阻不能测量,只能看色环来辨别。找了个4.7k的。
b,由于万用表没有电,面包板我忘记了哪些是连通哪些是断开的,只能拿示波器来测量。
c,一开始modprobe at24.ko后没有识别出eeprom,在at24.c中添加printk发现是read timeout导致的,接着我就检查引脚,怕杜邦线断掉,用示波器测试sda的波形居然没有。接着突然发现at24c02的引脚我全部都连反了。重新连线后解决。
4. 驱动成功
能在i2c总线设备下找到2-0051设备,modprobe at24.ko后能绑定到at24驱动。并且通过i2c-tools能像eeprom写值和读值。
# cd /sys/bus/i2c/devices/
# ls
0-0024 0-0050 0-0070 2-0051 i2c-0 i2c-2
# cd 2-0051
# ls -al
total 0
drwxr-xr-x 3 root root 0 Jan 1 00:00 .
drwxr-xr-x 5 root root 0 Jan 1 00:00 ..
-r--r--r-- 1 root root 4096 Jan 1 00:00 modalias
-r--r--r-- 1 root root 4096 Jan 1 00:00 name
lrwxrwxrwx 1 root root 0 Jan 1 00:00 of_node -> ../../../../../../../../../firmware/devicetree/base/ocp/interconnect@48000000/segment@100000/target-module@9c000/i2c@0/apple_eeprom0@51
drwxr-xr-x 2 root root 0 Jan 1 00:00 power
lrwxrwxrwx 1 root root 0 Jan 1 00:00 subsystem -> ../../../../../../../../../bus/i2c
-rw-r--r-- 1 root root 4096 Jan 1 00:00 uevent
# modprobe at24.ko
[ 38.083623] at24 0-0050: GPIO lookup for consumer wp
[ 38.088645] at24 0-0050: using device tree for GPIO lookup
[ 38.094306] of_get_named_gpiod_flags: can't parse 'wp-gpios' property of node '/ocp/interconnect@44c00000/segment@200000/target-module@b000/i2c@0/baseboard_eeprom@50[0]'
[ 38.109556] of_get_named_gpiod_flags: can't parse 'wp-gpio' property of node '/ocp/interconnect@44c00000/segment@200000/target-module@b000/i2c@0/baseboard_eeprom@50[0]'
[ 38.124702] at24 0-0050: using lookup tables for GPIO lookup
[ 38.130395] at24 0-0050: No GPIO consumer wp found
[ 38.137966] at24 0-0050: 4096 byte 24c32 EEPROM, writable, 1 bytes/write
[ 38.145393] at24 2-0051: GPIO lookup for consumer wp
[ 38.150399] at24 2-0051: using device tree for GPIO lookup
[ 38.156028] of_get_named_gpiod_flags: can't parse 'wp-gpios' property of node '/ocp/interconnect@48000000/segment@100000/target-module@9c000/i2c@0/apple_eeprom0@51[0]'
[ 38.171101] of_get_named_gpiod_flags: can't parse 'wp-gpio' property of node '/ocp/interconnect@48000000/segment@100000/target-module@9c000/i2c@0/apple_eeprom0@51[0]'
[ 38.186069] at24 2-0051: using lookup tables for GPIO lookup
[ 38.191761] at24 2-0051: No GPIO consumer wp found
[ 38.223103] at24 2-0051: 256 byte 24c02 EEPROM, writable, 1 bytes/write
# ls -al
total 0
drwxr-xr-x 4 root root 0 Jan 1 00:00 .
drwxr-xr-x 5 root root 0 Jan 1 00:00 ..
drwxr-xr-x 3 root root 0 Jan 1 00:00 2-00510
lrwxrwxrwx 1 root root 0 Jan 1 00:00 driver -> ../../../../../../../../../bus/i2c/drivers/at24
-rw------- 1 root root 256 Jan 1 00:00 eeprom
-r--r--r-- 1 root root 4096 Jan 1 00:00 modalias
-r--r--r-- 1 root root 4096 Jan 1 00:00 name
lrwxrwxrwx 1 root root 0 Jan 1 00:00 of_node -> ../../../../../../../../../firmware/devicetree/base/ocp/interconnect@48000000/segment@100000/target-module@9c000/i2c@0/apple_eeprom0@51
drwxr-xr-x 2 root root 0 Jan 1 00:00 power
lrwxrwxrwx 1 root root 0 Jan 1 00:00 subsystem -> ../../../../../../../../../bus/i2c
-rw-r--r-- 1 root root 4096 Jan 1 00:00 uevent
# i2cdetect -r -y 2
0 1 2 3 4 5 6 7 8 9 a b c d e f
00: -- -- -- -- -- -- -- -- -- -- -- -- --
10: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
20: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
30: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
40: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
50: -- UU -- -- -- -- -- -- -- -- -- -- -- -- -- --
60: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
70: -- -- -- -- -- -- -- --
# i2cdump -f -y 2 0x51
0 1 2 3 4 5 6 7 8 9 a b c d e f 0123456789abcdef
00: ff ab ff ff ff ff ff ff ff ff ff ff ff ff ff ff .?..............
10: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ................
20: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ................
30: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ................
40: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ................
50: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ................
60: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ................
70: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ................
80: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ................
90: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ................
a0: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ................
b0: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ................
c0: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ................
d0: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ................
e0: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ................
f0: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ................
# i2cset -f -y 2 0x51 0x01 0x12
# i2cget -f -y 2 0x51 0x01
0x12
二,eeprom读写用户程序
为eeprom的0地址写值4,读取值也为4。读写一致。
# ./i2c w 4
wrdata:0x4
# ./i2c r
writeaddress return: 1
readdata return:1
rddata:0x4
# i2cdump -f -y 2 0x51
0 1 2 3 4 5 6 7 8 9 a b c d e f 0123456789abcdef
00: 04 12 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ??..............
10: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ................
20: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ................
30: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ................
40: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ................
50: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ................
60: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ................
70: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ................
80: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ................
90: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ................
a0: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ................
b0: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ................
c0: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ................
d0: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ................
e0: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ................
f0: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ................
1. 遇到的问题
用自己写的app测试的代码,能正常读取,但是写入错误。原因是看了datasheet读取的时候要发2次设备地址,写入的时候只要发一次设备地址。如下是at24c32的。at24c02的地址是8byte的。
所以参考了网址linux下eeprom的读写改成了iotrl方式来写eeprom后成功写入。
2. 关于驱动和app的分工的设计
上面的网址中eeprom相关的处理都写到了用户程序,其实也可以写到驱动中,这样用户程序就会比较简单。我参考了其它传感器设备也是用i2c总线的,他们的设备驱动就是自己写的和我之前的applepaper一样,只不过不是注册到平台虚拟总线,而是将驱动注册到i2c总线。也就是说我也可以自己写一个i2c eeprom设备驱动,而不用默认的at24.c及i2c-dev.c。
另外,eeprom可以当做二进制文件,通过sysfs文件系统访问I2C设备,这样的话,app写起来就比较简单了,等于是在操作二进制文件,参考网址i2c子系统-eeprom实例分析
#define EEPROM_DEVICE "/sys/devices/platform/ocp/48000000.interconnect/48000000.interconnect:segment@100000/4819c000.target-module/4819c000.i2c/i2c-2/2-0051"
三,i2c子系统理论学习
设备驱动第15章节描述了i2c子系统还是很清晰的。关于总线就是适配器用来选择设备,并且适配器会绑定总线协议,因为i2c和SMbus协议不同。网上有很多i2c子系统的详细说明。
i2c_client和i2c_driver就是i2c设备驱动。他们的关系如下。
另外,关于master_xfer是比较重要的函数,所以我看了下它的调用关系,里面的函数都是cpu芯片级别控制i2c。
# cat trace | head -30
# tracer: function
#
# entries-in-buffer/entries-written: 32/32 #P:1
#
# _-----=> irqs-off
# / _----=> need-resched
# | / _---=> hardirq/softirq
# || / _--=> preempt-depth
# ||| / delay
# TASK-PID CPU# |||| TIMESTAMP FUNCTION
# | | | |||| | |
i2c-126 [000] .... 372.888227: omap_i2c_xfer_common <-omap_i2c_xfer_irq
i2c-126 [000] .... 372.888245:
=> omap_i2c_xfer_irq
=> __i2c_transfer
=> i2c_transfer
=> i2c_transfer_buffer_flags
=> i2cdev_read
=> __vfs_read
=> vfs_read
=> ksys_read
=> sys_read
=> ret_fast_syscall
=> 0xbee3ecd0
另外也监控了at24probe的过程
# cat trace | head -40
# tracer: function
#
# entries-in-buffer/entries-written: 30/30 #P:1
#
# _-----=> irqs-off
# / _----=> need-resched
# | / _---=> hardirq/softirq
# || / _--=> preempt-depth
# ||| / delay
# TASK-PID CPU# |||| TIMESTAMP FUNCTION
# | | | |||| | |
modprobe-123 [000] .... 614.268663: device_property_present <-at24_probe
modprobe-123 [000] .... 614.268682:
=> at24_probe
=> i2c_device_probe
=> really_probe
=> driver_probe_device
=> device_driver_attach
=> __driver_attach
=> bus_for_each_dev
=> driver_attach
=> bus_add_driver
=> driver_register
=> i2c_register_driver
=> at24_init
=> do_one_initcall
=> do_init_module
=> load_module
=> sys_finit_module
=> ret_fast_syscall
=> 0xbed48b20
at24_read函数在app读取的时候不会被调用的
# mount -t debugfs nodev /sys/kernel/debug
# cd /sys/kernel/debug/tracing
# echo 0 > tracing_on
# echo at24_read > set_ftrace_filter
# echo function > current_tracer
# echo func_stack_trace > trace_options
# echo 1 > tracing_on
# cd /usr/study/
# ./i2c r
writeaddress return: 1
readdata return:1
rddata:0x4
# cd /sys/kernel/debug/tracing
# echo 0 > tracing_on
# cat trace | head -40
# tracer: function
#
# entries-in-buffer/entries-written: 0/0 #P:1
#
# _-----=> irqs-off
# / _----=> need-resched
# | / _---=> hardirq/softirq
# || / _--=> preempt-depth
# ||| / delay
# TASK-PID CPU# |||| TIMESTAMP FUNCTION
# | | | |||| | |