1. 程式人生 > >【TINY4412】LINUX移植筆記:(24)裝置樹EEPROM驅動

【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 也就是 0x50A2 = 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 ,這個驅動支援 AT24C02AT24C64EEPROM ,其中 AT24C1624AA025E48 類似,都是 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 地址。

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 

仍然和之前讀取的結果一致!

參考