您的位置:首页 > 运维架构 > Linux

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

2018-01-16 08:46 513 查看

【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 &
4000
lt; 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


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

参考

详解Linux-I2C驱动
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: