【TINY4412】LINUX移植笔记:(24)设备树EEPROM驱动

环境

宿主机 : 虚拟机 Ubuntu 16.04 LTS / X64
目标板[底板]: Tiny4412SDK - 1506
目标板[核心板]: Tiny4412 - 1412
LINUX内核: 4.12.0
交叉编译器: arm-none-linux-gnueabi-gcc(gcc version 4.8.3 20140320)
日期: 2017-9-9 20:30:09
作者: SY

简介

EEPROM 的型号为:24AA025E48 查看数据手册:

  • 容量:2Kbit
  • 总线:I2C
  • 页大小:16-Byte

1

2

备注地址:1010000 也就是 0x50A2 = 0 A1 = 0 A0 = 0 查看手册:

2

从上图看一看出最后一位为 R/W 位,在读写数据时用到,先不用管这个位,高 4 位定死为 10 ,那么决定 I2C 地址的只剩下 A2 A1 A0

综上所述:地址为 1010000 和原理图上的地址相符。

移植

知道上述的条件,已经可以移植了。I2C 既然称为总线,那么基本上不用你来写相关的总线驱动, Linux 内核肯定已经写好 I2C 框架,框架必然是一个与不依赖任何底层实现的东西,只定义接口,实现由各个具体的平台来实现。找到 drivers\i2c\busses\i2c-s3c2410.c ,这是三星的 I2C 底层实现。

在框架的基础上支持多个具体的 I2C 设备,找到 drivers\misc\eeprom\at24.c ,这个驱动支持 AT24C02AT24C64EEPROM ,其中 AT24C1624AA025E48 类似,都是 16Bytes 页大小。

设备树

写自己的 dts

1
2
3
4
5
6
7
8
9
10
11
&i2c_0 {
samsung,i2c-sda-delay = <100>;
samsung,i2c-max-bus-freq = <400000>;
status = "okay";
eeprom: eeprom@50 {
compatible = "atmel,24c16", "microchip, 24aa025e48";
reg = <0x50>;
pagesize = <16>;
};
};

reg 填写 I2C 地址。

1
2
3
4
5
6
7
8
Device Drivers --->
Misc devices --->
EEPROM support --->
<*> I2C EEPROMs / RAMs / ROMs from most vendors
I2C support --->
I2C Hardware Bus support --->
<*> S3C2410 I2C Driver

烧录

1
2
3
4
5
[ 0.398542] s3c-i2c 13860000.i2c: slave address 0x00
[ 0.398552] s3c-i2c 13860000.i2c: bus frequency set to 390 KHz
[ 0.398802] s3c-i2c 13860000.i2c: i2c-0: S3C I2C adapter
[ 2.849104] at24 0-0050: 2048 byte 24c16 EEPROM, writable, 16 bytes/write

查看设备节点

1
2
[root@TINY4412:~]# ls /dev/i2c-0
/dev/i2c-0

APP

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
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
/*
* eeprom driver for tiny4412
*
* Copyright (c) 2017
* Author: SY <1530454315@qq.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <signal.h>
#include <stdbool.h>
#include <pthread.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/epoll.h>
#include <sys/ioctl.h>
#include <sys/mman.h>
#include <linux/i2c-dev.h>
#include <linux/i2c.h>
#if 1
static void help(void)
{
printf("Usage:\n");
printf(" read: ./eeprom r [i2c_addr] [dev_addr] [lenth]\n");
printf(" write: ./eeprom w [i2c_addr] [dev_addr] [lenth] ...\n");
}
#endif
#pragma pack(1)
struct i2c_data {
uint8_t addr;
uint8_t data[0];
};
#pragma pack()
bool i2c_write(int fd, uint8_t i2c_addr, uint16_t dev_addr, uint8_t *buf, uint16_t len)
{
bool ret = true;
int i;
struct i2c_rdwr_ioctl_data i2c;
i2c.nmsgs = 1;
i2c.msgs = (struct i2c_msg *)calloc(i2c.nmsgs, sizeof(struct i2c_msg));
struct i2c_msg *p = &i2c.msgs[0];
p->addr = i2c_addr;
p->flags = 0;
p->len = sizeof(struct i2c_data) + len;
p->buf = calloc(1, p->len);
struct i2c_data *data = (struct i2c_data *)p->buf;
data->addr = dev_addr;
memcpy(data->data, buf, len);
int res = ioctl(fd, I2C_RDWR, &i2c);
if (res < 0) {
perror("Write ioctl error");
ret = false;
goto error;
}
printf("WRITE\n");
for (i=0; i<len; ++i) {
printf("%x ", buf[i]);
}
printf("\n");
error:
free(p->buf);
free(i2c.msgs);
return ret;
}
bool i2c_read(int fd, uint8_t i2c_addr, uint16_t dev_addr, uint8_t *buf, uint16_t len)
{
bool ret = true;
struct i2c_rdwr_ioctl_data i2c;
i2c.nmsgs = 2;
i2c.msgs = (struct i2c_msg *)calloc(i2c.nmsgs, sizeof(struct i2c_msg));
/* First Write addr */
struct i2c_msg *p1 = &i2c.msgs[0];
p1->addr = i2c_addr;
p1->flags = 0;
p1->len = sizeof(struct i2c_data);
p1->buf = calloc(1, p1->len);
((struct i2c_data *)p1->buf)->addr = dev_addr;
/* Read */
struct i2c_msg *p2 = &i2c.msgs[1];
p2->addr = i2c_addr;
p2->flags = I2C_M_RD;
p2->len = len;
p2->buf = buf;
int res = ioctl(fd, I2C_RDWR, &i2c);
if (res < 0) {
perror("Read ioctl error");
ret = false;
goto error;
}
error:
free(p1->buf);
free(i2c.msgs);
return ret;
}
int main(int argc, char **argv)
{
if (argc < 2) {
help();
exit(0);
}
char rw = *argv[1];
if (rw == 'r') {
if (argc != 5) {
help();
exit(0);
}
} else if (rw == 'w') {
if (argc != 6) {
help();
exit(0);
}
} else {
help();
exit(0);
}
int i2c_addr;
sscanf(argv[2], "%x", &i2c_addr);
int dev_addr;
sscanf(argv[3], "%x", &dev_addr);
uint16_t len = atoi(argv[4]);
printf("> i2c_addr = %x, dev_addr = %x, lenth = %d\n", i2c_addr, dev_addr, len);
int fd = open("/dev/i2c-0", O_RDWR);
if(!fd) {
printf("open /dev/i2c-0 return error\n");
exit(0);
}
int i;
switch (rw) {
case 'r': {
uint8_t *buff = calloc(1, len);
if (buff == NULL) {
printf("calloc error!\n");
goto error1;
}
if (i2c_read(fd, i2c_addr, dev_addr, buff, len) == false) {
free(buff);
goto error1;
}
printf("READ:\n");
for (i=0; i<len; ++i) {
printf("%x ", buff[i]);
}
printf("\n");
free(buff);
break;
}
case 'w': {
uint8_t *buff = calloc(1, len);
if (buff == NULL) {
printf("calloc error!\n");
goto error1;
}
memcpy(buff, argv[5], len);
if (i2c_write(fd, i2c_addr, dev_addr, buff, len) == false) {
free(buff);
goto error1;
}
free(buff);
break;
}
default:
help();
break;
}
error1:
close(fd);
return 0;
}

测试

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
[root@TINY4412:~]# ./tmp/eeprom
Usage:
read: ./eeprom r [i2c_addr] [dev_addr] [lenth]
write: ./eeprom w [i2c_addr] [dev_addr] [lenth] ...
[root@TINY4412:~]# ./tmp/eeprom w 50 0 10 0000000000
> i2c_addr = 50, dev_addr = 0, lenth = 10
WRITE
30 30 30 30 30 30 30 30 30 30
[root@TINY4412:~]#
[root@TINY4412:~]#
[root@TINY4412:~]# ./tmp/eeprom r 50 0 10
> i2c_addr = 50, dev_addr = 0, lenth = 10
READ:
30 30 30 30 30 30 30 30 30 30
[root@TINY4412:~]#
[root@TINY4412:~]#
[root@TINY4412:~]#
[root@TINY4412:~]# ./tmp/eeprom w 50 0 5 12345
> i2c_addr = 50, dev_addr = 0, lenth = 5
WRITE
31 32 33 34 35
[root@TINY4412:~]# ./tmp/eeprom r 50 0 10
> i2c_addr = 50, dev_addr = 0, lenth = 10
READ:
31 32 33 34 35 30 30 30 30 30

如果数据写入到 EEPROM 成功的话,断电重启后应该可以读取到之前写入的数据。重新上电…

1
2
3
4
[root@TINY4412:~]# ./tmp/eeprom r 50 0 10
> i2c_addr = 50, dev_addr = 0, lenth = 10
READ:
31 32 33 34 35 30 30 30 30 30

仍然和之前读取的结果一致!

参考

详解Linux-I2C驱动

SY wechat
扫一扫,用手机访问本站