【TINY4412】LINUX移植筆記:(24)裝置樹EEPROM驅動
【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: [email protected]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
檢視裝置節點
[[email protected]:~]# ls /dev/i2c-0
/dev/i2c-0
APP
/*
* eeprom driver for tiny4412
*
* Copyright (c) 2017
* Author: SY <[email protected]>
*
* 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;
}
測試
[[email protected]:~]# ./tmp/eeprom
Usage:
read: ./eeprom r [i2c_addr] [dev_addr] [lenth]
write: ./eeprom w [i2c_addr] [dev_addr] [lenth] ...
[[email protected]:~]# ./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
[[email protected]:~]#
[[email protected]:~]#
[[email protected]:~]# ./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
[[email protected]:~]#
[[email protected]:~]#
[[email protected]:~]#
[[email protected]:~]# ./tmp/eeprom w 50 0 5 12345
> i2c_addr = 50, dev_addr = 0, lenth = 5
WRITE
31 32 33 34 35
[[email protected]:~]# ./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
成功的話,斷電重啟後應該可以讀取到之前寫入的資料。重新上電…
[[email protected]:~]# ./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
仍然和之前讀取的結果一致!