【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-7 19:12:14
作者: SY
简介
EEPROM
的型号为:24AA025E48
查看数据手册:
- 容量:
2Kbit
- 总线:
I2C
- 页大小:
16-Byte
备注地址:1010000
也就是 0x50
, A2 = 0
A1 = 0
A0 = 0
查看手册:
从上图看一看出最后一位为 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
,这个驱动支持 AT24C02
、 AT24C64
等 EEPROM
,其中 AT24C16
和 24AA025E48
类似,都是 16Bytes
页大小。
设备树
写自己的 dts
&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
地址。
menuconfig
Device Drivers --->
Misc devices --->
EEPROM support --->
<*> I2C EEPROMs / RAMs / ROMs from most vendors
I2C support --->
I2C Hardware Bus support --->
<*> S3C2410 I2C Driver
烧录
[ 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
查看设备节点
[root@TINY4412:~]# ls /dev/i2c-0
/dev/i2c-0
APP
/*
* 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;
}
测试
[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
成功的话,断电重启后应该可以读取到之前写入的数据。重新上电…
[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
仍然和之前读取的结果一致!