IIC系列文章
1.【linux驅動】IIC驅動-硬件、協議
2.【linux驅動】IIC驅動OLED屏(GPIO 模擬)
3.【linux驅動】IIC驅動(4418讀取EEPROM:24AA025E48T-I/OT)
4.【linux驅動】IIC驅動OLED屏
概述
本文以24AA025E48T-I/OT EEPROM讀取爲例講解IIC下的Linux驅動
電路原理圖
24AA025E48T-I/OT連接在了IIC0上
IIC0連接在GPIOD2/3上
4418芯片手冊:IIC
先看一下4418的芯片手冊關於IIC的介紹以及寄存器地址,4418內部集成IIC控制器,不需要我們手動一位一位的去操作IO口。
1.IIC控制器發送接收操作
意思是IIC控制器工作在發送模式發送數據的時候會等待CPU向位移寄存器寫入需要發送的數據,在這之前SCL將一直保持低電平。在接收模式的時候,IIC控制器會等待CPU讀取位移寄存器的值,這時候SCL會被拉低,保證下一字節數據不被傳輸。直到CPU讀取完數據之後纔會釋放SCL時鐘等待下一字節的傳輸。(閱讀4412手冊翻譯的,4418手冊還真是輕描淡寫)。總結一下就是每次傳輸一字節後會用SCL佔住總線。
主機寫入模式寄存器操作指導:
主機讀取模式寄存器操作指導:
從機發送模式寄存器操作指導:
從機接收模式寄存器操作指導:
中斷號查找
寄存器詳解
一共5個寄存器,ICCR是配置寄存器,ICSR可以理解爲控制寄存器。這兩個寄存器另外都帶有狀態標誌,是主要關注的寄存器。IAR寄存器主要是存放自己的地址,可以通過軟件設置。IDSR是用來存放發送和接收的數據寄存器,他是一個位移寄存器。STOPCON這裏不關心。
ICCR:
先看ICCR的[3:0]和[6],這兩個是設置iic時鐘頻率的,SCL的時鐘頻率可以實時改變。我的fpclk=800Mhz,這裏我的[6]=1,[3:0]=15 我的iic頻率就是800Mhz/256/15 = 208Khz處在iic速度範圍內就好。
ICCR [4]爲中斷掛起標誌位,當收到ACK、收到本機地址或者仲裁失敗的時候會發出中斷,這裏只考慮ACK。當這個位爲1的時候,iic操作暫停,SCL將會被拉低,總線會被佔住,寫入0將恢復總線傳輸。不能寫入1。
ICCR [5]爲中斷使能設置位
ICCR [7]爲ACK使能設置位
ICCR [8]爲中斷掛起標誌位,當發生中斷的時候需要寫入0來清除中斷標誌,否則將會無限收到中斷。
ICSR:
ICSR [7:6] 四種模式設置
ICSR [5] 讀取它可以知道總線是否空閒,寫入1表示發送開始信號,並且傳輸IDSR中設置好的數據。寫入0表示發送停止信號。
ICSR [4] 啓用禁用傳輸
ICSR [3] 仲裁狀態(只讀)
ICSR [2] 收到地址與自己相同,也就是由主機選中自己了(只讀)
ICSR [1] 收到0地址請求,也就是廣播請求(只讀)
ICSR [0] 收到ACK(只讀)
驅動代碼
先定義一下基本的寄存器地址
#define CLK_BASE 0xc00ae000 // 時鐘配置寄存器
#define RESET_BASE 0xc0012000 // 系統復位寄存器
#define IIC_BASE 0xc00a4000 // IIC寄存器
定義一些寄存器位置
/*
* I2C control macro
*/
#define I2C_TXRXMODE_SLAVE_RX 0 ///< Slave Receive Mode
#define I2C_TXRXMODE_SLAVE_TX 1 ///< Slave Transmit Mode
#define I2C_TXRXMODE_MASTER_RX 2 ///< Master Receive Mode
#define I2C_TXRXMODE_MASTER_TX 3 ///< Master Transmit Mode
#define I2C_ICCR_OFFS 0x00
#define I2C_ICSR_OFFS 0x04
#define I2C_IDSR_OFFS 0x0C
#define I2C_STOP_OFFS 0x10
#define ICCR_IRQ_CLR_POS 8
#define ICCR_ACK_ENB_POS 7
#define ICCR_IRQ_ENB_POS 5
#define ICCR_IRQ_PND_POS 4
#define ICCR_CLK_SRC_POS 6
#define ICCR_CLK_VAL_POS 0
#define ICSR_MOD_SEL_POS 6
#define ICSR_SIG_GEN_POS 5
#define ICSR_BUS_BUSY_POS 5
#define ICSR_OUT_ENB_POS 4
#define ICSR_ARI_STA_POS 3 /* Arbitration */
#define ICSR_ACK_REV_POS 0 /* ACK */
#define STOP_ACK_GEM_POS 2
#define STOP_DAT_REL_POS 1 /* only slave transmode */
#define STOP_CLK_REL_POS 0 /* only master transmode */
#define NOSTOP_GPIO (1)
#define I2C_CLOCK_RATE (100000) /* wait 50 msec */
#define WAIT_ACK_TIME (500) /* wait 50 msec */
struct i2c_register {
unsigned int ICCR; ///< 0x00 : I2C Control Register
unsigned int ICSR; ///< 0x04 : I2C Status Register
unsigned int IAR; ///< 0x08 : I2C Address Register
unsigned int IDSR; ///< 0x0C : I2C Data Register
unsigned int STOPCON; ///< 0x10 : I2C Stop Control Register
};
const static int i2c_gpio [2] = { (PAD_GPIO_D + 2), (PAD_GPIO_D + 3) };
#define RESET_ID_I2C0 20
- 初始化IIC控制器
根據芯片手冊介紹,需要使能IIC時鐘、初始化GPIO功能、重置IIC控制器
base = (void *)ioremap(IIC_BASE,0xf0); //映射地址
clk_base = (void *)ioremap(CLK_BASE,0xf0); //映射地址
reset_addr = (void *)ioremap(RESET_BASE,0xf0); //映射地址
nxp_soc_gpio_set_io_pull_enb(i2c_gpio[0],0);//禁用上拉
nxp_soc_gpio_set_io_pull_enb(i2c_gpio[1],0);//禁用上拉
nxp_soc_gpio_set_io_func(i2c_gpio[0], 1);//設置gpio功能
nxp_soc_gpio_set_io_func(i2c_gpio[1], 1);//設置gpio功能
writel(0x1 << 3,clk_base);// 使能時鐘
unsigned int reset = readl(reset_addr);
reset &= ~(0x1 << 20);
writel(reset,reset_addr);// 復位硬件
mdelay(1);
reset |= 0x1 << 20;
writel(reset,reset_addr);// 結束復位
設置IIC時鐘,時鐘頻率爲800Mhz/256/(15+1),時鐘設置一定要在硬件復位之後
static inline void iic_set_clock(void)
{
int cksrc = 1; //pclk/256
int ckscl = 0xf;
unsigned int ICCR = 0;
ICCR = readl(base+I2C_ICCR_OFFS);
ICCR &= ~( 0x0f | 1 << ICCR_CLK_SRC_POS); //清除發送時鐘相關寄存器
ICCR |= ((cksrc << ICCR_CLK_SRC_POS) | (ckscl)); //設置時鐘源
writel(ICCR,(base+I2C_ICCR_OFFS));
}
- 找一下IIC中斷號
硬件中斷號爲15
內核中斷號還是15,註冊中斷,這裏request_irq函數註冊中斷處理的屬性必須是IRQF_DISABLED | IRQF_SHARED,且最後一位dev_id不可以爲NULL
if (!request_irq(IRQ_PHY_I2C0,iic_irq,IRQF_DISABLED | IRQF_SHARED,"iic_irq0",1)){
printk("irq registed %d\n", IRQ_PHY_I2C0);
iic_int = IRQ_PHY_I2C0;
}else{
printk("irq regist fail %d\n",IRQ_PHY_I2C0);
}
- 還需要一個設別忙等判斷
static inline int i2c_wait_dev(int wait)
{
unsigned int ICSR = 0;
do {
ICSR = readl(base+I2C_ICSR_OFFS);
if ( !(ICSR & (1<<ICSR_BUS_BUSY_POS)) && !(ICSR & (1<<ICSR_ARI_STA_POS)) )
return 0;
mdelay(1);
} while (wait-- > 0);
return -1;
}
IIC關鍵操作邏輯
- 開始傳輸
四種傳輸模式的開始部分合並一下其實就是設置IIC頻率、配置ICCR寄存器,寫入目標地址IDSR,配置ICSR寄存器啓動傳輸。
static inline void start_dev(unsigned int mode){
unsigned int ICCR = 0,ICSR = 0;
ICSR = readl(base+I2C_ICSR_OFFS);
ICSR = (1<<ICSR_OUT_ENB_POS);
writel(ICSR,(base+I2C_ICSR_OFFS)); // 這裏設置一遍,確保輸出啓用下面數據纔可以寫入IDSR
writel(iic_dev.addr,(base+I2C_IDSR_OFFS));// 設置從機地址
ICCR = readl(base+I2C_ICCR_OFFS);
ICCR &= ~(1<<ICCR_ACK_ENB_POS); // 發送數據不需要回復ACK
ICCR |= 1 << ICCR_IRQ_ENB_POS; // 開啓中斷
writel(ICCR,(base+I2C_ICCR_OFFS));
ICSR = readl(base+I2C_ICSR_OFFS);
ICSR |= (mode & 3) << ICSR_MOD_SEL_POS; // 主機發送
ICSR |= 1 << ICSR_SIG_GEN_POS; // 開始傳輸
ICSR |= 1 << ICSR_OUT_ENB_POS; // 使能輸出
writel(ICSR,(base+I2C_ICSR_OFFS)); // 啓動發送數據
}
當尋址信息發出後就等着ACK中斷就行,然後進入如下循環
- (ACK/收數據)中斷,自動暫停總線(拉低SCL)
- 清除中斷,禁用中斷
- 裝填發送數據/讀取接收數據
- 繼續傳輸,啓用中斷(回覆ACK)
- 是否結束通信
- 清除中斷
unsigned int ICCR = 0, ICSR = 0;
ICCR = readl((base+I2C_ICCR_OFFS));
ICCR &= ~ (1 << ICCR_IRQ_ENB_POS); //禁用中斷
ICCR |= 1<<ICCR_IRQ_CLR_POS; //清除中斷
writel(ICCR, (base+I2C_ICCR_OFFS));
- 判斷中斷是否異常
static bool is_ack(){
unsigned int ICSR = 0xff;
ICSR= readl(base+I2C_ICSR_OFFS);
if ((ICSR & 1) == 0){
return true;
}else{
printk("ack not receive\n");
}
ICSR = ICSR >> 1;
if ((ICSR & 1) == 0){
printk("start/stop\n");
}else{
printk("0x00 addr\n");
}
ICSR = ICSR >> 1;
if ((ICSR & 1) == 0){
printk("start/stop 2\n");
}else{
printk("match addr\n");
}
ICSR = ICSR >> 1;
if ((ICSR & 1) == 0){
printk("arbitration ok\n");
}else{
printk("arbitration fail\n");
}
return false;
}
- 裝填數據
裝填數據向ICDR寫入數據即可,祥見下文
- 繼續傳輸
static inline void trans_dev(unsigned int ack)
{
unsigned int ICCR = 0, STOP;
ICCR = readl(base+I2C_ICCR_OFFS);
ICCR &= ~(1<<ICCR_ACK_ENB_POS);
ICCR |= ack << ICCR_ACK_ENB_POS;
writel(ICCR, (base+I2C_ICCR_OFFS));
ICCR = readl((base+I2C_ICCR_OFFS));
ICCR &= (~ (1 << ICCR_IRQ_PND_POS));
ICCR |= 1<<ICCR_IRQ_CLR_POS;
ICCR |= 1<<ICCR_IRQ_ENB_POS;
writel(ICCR, (base+I2C_ICCR_OFFS));
}
- 停止傳輸
static void stop_sig(void)
{
unsigned int ICSR = 0, ICCR = 0, STOP = 0;
trans_dev(0); //啓動傳輸
udelay(1);
STOP = readl(base+I2C_STOP_OFFS);
STOP |= 1<<STOP_ACK_GEM_POS;
writel(STOP, base+I2C_STOP_OFFS); //發送停止信號
udelay(1);
ICSR = iic_dev.mode << ICSR_MOD_SEL_POS;
writel(ICSR, (base+I2C_ICSR_OFFS)); //清除寄存器
}
整體驅動代碼
#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <asm/io.h>
#include <linux/gpio.h>
#include <mach/soc.h>
#include <linux/delay.h>
#include <mach/platform.h>
#include <linux/fs.h>
#include <linux/device.h>
#include <linux/cdev.h>
#include <linux/slab.h>
#include <asm/uaccess.h>
#include <linux/errno.h>
#include <linux/i2c.h>
#include <linux/irq.h> //含有IRQ_HANDLED\IRQ_TYPE_EDGE_RISING
#include <linux/interrupt.h> //含有request_irq、free_irq函數
#include "iic.h"
MODULE_LICENSE("GPL");
dev_t devid;
struct cdev char_dev;
struct class * char_class;
int buffer_size = 100;
char * char_data;
int iic_int = 0;
#define _SETDATA(p, d) (((struct i2c_register *)p)->IDSR = d)
#define _GETDATA(p) (((struct i2c_register *)p)->IDSR)
void * base;
void * reset_addr;
void * clk_base;
struct IIC_DEV{
u8 addr; //對方硬件地址
u8 mode; //傳輸模式
u8 *data; //寫入或者讀取的數據
int data_count; //傳輸數據數量
int data_idx; //數據讀寫位置
bool is_irq; //是否收到中斷
};
struct IIC_DEV iic_dev;
static inline void iic_set_clock(void)
{
int cksrc = 1; //pclk/256
int ckscl = 0xe;
unsigned int ICCR = 0;
ICCR = readl(base+I2C_ICCR_OFFS);
ICCR &= ~( 0x0f | 1 << ICCR_CLK_SRC_POS); //清除發送時鐘相關寄存器
ICCR |= ((cksrc << ICCR_CLK_SRC_POS) | (ckscl)); //設置時鐘源
writel(ICCR,(base+I2C_ICCR_OFFS));
}
static inline void start_dev(unsigned int mode){
unsigned int ICCR = 0,ICSR = 0;
ICSR = readl(base+I2C_ICSR_OFFS);
ICSR = (1<<ICSR_OUT_ENB_POS);
writel(ICSR,(base+I2C_ICSR_OFFS)); // 這裏設置一遍,確保輸出啓用下面數據纔可以寫入IDSR
writel(iic_dev.addr,(base+I2C_IDSR_OFFS));// 設置從機地址
ICCR = readl(base+I2C_ICCR_OFFS);
ICCR &= ~(1<<ICCR_ACK_ENB_POS);
ICCR |= 1 << ICCR_IRQ_ENB_POS;
writel(ICCR,(base+I2C_ICCR_OFFS));
ICSR = readl(base+I2C_ICSR_OFFS);
ICSR |= (mode & 3) << ICSR_MOD_SEL_POS; // 主機發送
ICSR |= 1 << ICSR_SIG_GEN_POS; // 開始傳輸
ICSR |= 1 << ICSR_OUT_ENB_POS; // 使能輸出
writel(ICSR,(base+I2C_ICSR_OFFS));// 啓動發送數據
}
static inline int i2c_wait_dev(int wait)
{
unsigned int ICSR = 0;
do {
ICSR = readl(base+I2C_ICSR_OFFS);
if ( !(ICSR & (1<<ICSR_BUS_BUSY_POS)) && !(ICSR & (1<<ICSR_ARI_STA_POS)) )
return 0;
mdelay(1);
} while (wait-- > 0);
return -1;
}
static inline void trans_dev(unsigned int ack)
{
unsigned int ICCR = 0;
ICCR = readl(base+I2C_ICCR_OFFS);
ICCR &= ~(1<<ICCR_ACK_ENB_POS);
ICCR |= ack << ICCR_ACK_ENB_POS;
writel(ICCR, (base+I2C_ICCR_OFFS));
ICCR = readl((base+I2C_ICCR_OFFS));
ICCR &= (~ (1 << ICCR_IRQ_PND_POS));
ICCR |= 1<<ICCR_IRQ_CLR_POS;
ICCR |= 1<<ICCR_IRQ_ENB_POS;
writel(ICCR, (base+I2C_ICCR_OFFS));
}
static void stop_sig(void)
{
unsigned int ICSR = 0, STOP = 0;
trans_dev(0); //啓動傳輸
udelay(1);
STOP = readl(base+I2C_STOP_OFFS);
STOP |= 1<<STOP_ACK_GEM_POS;
writel(STOP, base+I2C_STOP_OFFS); //發送停止信號
udelay(1);
ICSR = iic_dev.mode << ICSR_MOD_SEL_POS;
writel(ICSR, (base+I2C_ICSR_OFFS)); //清除寄存器
}
static bool is_ack(int is_ack){
unsigned int ICSR = 0xff;
ICSR= readl(base+I2C_ICSR_OFFS);
if ((ICSR & 1) == 0 || !is_ack){
return true;
}else{
printk("ack not receive\n");
}
ICSR = ICSR >> 1;
if ((ICSR & 1) == 0){
printk("start/stop\n");
}else{
printk("0x00 addr\n");
}
ICSR = ICSR >> 1;
if ((ICSR & 1) == 0){
printk("start/stop 2\n");
}else{
printk("match addr\n");
}
ICSR = ICSR >> 1;
if ((ICSR & 1) == 0){
printk("arbitration ok\n");
}else{
printk("arbitration fail\n");
}
return false;
}
static irqreturn_t iic_irq(int irq,void* dev_id)
{
unsigned int ICCR = 0;
ICCR = readl((base+I2C_ICCR_OFFS));
ICCR &= (~ (1 << ICCR_IRQ_ENB_POS)); //禁用中斷
ICCR |= 1<<ICCR_IRQ_CLR_POS; //清除中斷
writel(ICCR, (base+I2C_ICCR_OFFS));
if(is_ack(iic_dev.data_idx == 0)){
int ack = (iic_dev.data_count <= iic_dev.data_idx + 1) ? 0: 1;// 不是最後一個都要發ack
if (iic_dev.mode == I2C_TXRXMODE_SLAVE_TX \
|| iic_dev.mode == I2C_TXRXMODE_MASTER_TX){
if(iic_dev.data_idx == iic_dev.data_count){
// stop
stop_sig();
}else{
//write
writel(iic_dev.data[iic_dev.data_idx],(base+I2C_IDSR_OFFS));// 設置數據
trans_dev(0);
}
}else{
//read
if(iic_dev.data_idx == iic_dev.data_count){
// stop
iic_dev.data[iic_dev.data_idx - 1] = _GETDATA(base);
stop_sig();
}else{
if(iic_dev.data_idx == 0) // 第一次ack
{
}else{
iic_dev.data[iic_dev.data_idx - 1] = _GETDATA(base);
}
trans_dev(ack);
}
}
iic_dev.data_idx++;
}else{
printk("canot find device:%x\n", iic_dev.addr);
}
return (IRQ_HANDLED);
}
static int write(u8 slave_addr,u8 * data,u8 count)
{
int wait_time = 500;
iic_dev.addr = (slave_addr & ~0x1);
iic_dev.data = data;
iic_dev.mode = I2C_TXRXMODE_MASTER_TX;
iic_dev.data_count = count;
iic_dev.data_idx = 0;
iic_dev.is_irq = false;
iic_set_clock(); //設置iic時鐘
if (i2c_wait_dev(wait_time) != -1){
start_dev(I2C_TXRXMODE_MASTER_TX);
}else{
stop_sig();
printk("device is busy\n");
return -1;
}
wait_time = 0;
while(iic_dev.data_idx <= iic_dev.data_count\
&& WAIT_ACK_TIME > wait_time){
mdelay(10);
wait_time += 10;
}
if(iic_dev.data_idx <= iic_dev.data_count)
{
stop_sig();
printk("write time out\n");
}
return 0;
}
static int read(u8 slave_addr,u8 * read_buffer, u8 count)
{
int wait_time = 500;
iic_dev.addr = (slave_addr | 0x1);
iic_dev.data = read_buffer;
iic_dev.mode = I2C_TXRXMODE_MASTER_RX;
iic_dev.data_count = count;
iic_dev.data_idx = 0;
iic_dev.is_irq = false;
iic_set_clock(); //設置iic時鐘
if (i2c_wait_dev(wait_time) != -1){
start_dev(I2C_TXRXMODE_MASTER_RX);
}else{
stop_sig();
printk("device is busy\n");
return -1;
}
wait_time = 0;
while(iic_dev.data_idx <= iic_dev.data_count\
&& WAIT_ACK_TIME > wait_time){
mdelay(10);
wait_time += 10;
}
if(iic_dev.data_idx <= iic_dev.data_count)
{
stop_sig();
printk("read time out\n");
}
return 0;
}
static inline void i2c_bus_off(void)
{
unsigned int ICSR = 0;
ICSR &= ~(1<<ICSR_OUT_ENB_POS);
writel(ICSR, (base+I2C_ICSR_OFFS));
}
static long ioctl(struct file * fl, unsigned int cmd, unsigned long arg){
unsigned int dir,size,i;
unsigned char dev_addr;
dir = _IOC_DIR(cmd);
size = _IOC_SIZE(cmd);
i = copy_from_user(&dev_addr,(unsigned char *)arg,1);
i = size - 1;
if(dir == _IOC_WRITE){
while(i > 0){
i = copy_from_user(char_data,(unsigned char *)arg + 1,i);
}
write(dev_addr,char_data,size - 1);
}else{
read(dev_addr,char_data,size - 1);
while(i > 0){
i = copy_to_user((unsigned char *)arg + 1, char_data, i);
}
}
return 0;
}
struct file_operations my_opts = {
.owner = THIS_MODULE,
.unlocked_ioctl = ioctl
};
static int __init iic_init(void){
unsigned int reset;
int ret = 0;
devid = MKDEV(241, 1); //換算設備號
ret = register_chrdev_region(devid, 1, "char_test");//註冊設備,在/proc/drivers下面可以看到
if (ret < 0)
goto err0;
cdev_init(&char_dev,&my_opts); //綁定opt結構體
char_dev.owner = THIS_MODULE;
ret = cdev_add(&char_dev,devid,1); //註冊字符設備驅動
if (ret < 0)
goto err1;
char_class = class_create(THIS_MODULE,"char_test"); //在/sys/class中創建文件夾
device_create(char_class,NULL,devid,NULL,"char_test_dev_%d",1);//在上一步文件夾中創建char_test_dev_1
char_data = kzalloc(buffer_size,GFP_KERNEL);
base = (void *)ioremap(IIC_BASE,0xf0); //映射地址
clk_base = (void *)ioremap(CLK_BASE,0xf0); //映射地址
reset_addr = (void *)ioremap(RESET_BASE,0xf0); //映射地址
nxp_soc_gpio_set_io_pull_enb(i2c_gpio[0],0);//禁用上拉
nxp_soc_gpio_set_io_pull_enb(i2c_gpio[1],0);//禁用上拉
nxp_soc_gpio_set_io_func(i2c_gpio[0], 1);//設置gpio功能
nxp_soc_gpio_set_io_func(i2c_gpio[1], 1);//設置gpio功能
writel(0x1 << 3,clk_base);// 使能時鐘
reset = readl(reset_addr);
reset &= ~(0x1 << 20);
writel(reset,reset_addr);// 復位硬件
mdelay(1);
reset |= 0x1 << 20;
writel(reset,reset_addr);// 結束復位
i2c_bus_off();
if (!request_irq(IRQ_PHY_I2C0,iic_irq,IRQF_DISABLED | IRQF_SHARED,"iic_irq0",(void *)1)){
printk("irq registed %d\n", IRQ_PHY_I2C0);
iic_int = IRQ_PHY_I2C0;
}else{
printk("irq regist fail %d\n",IRQ_PHY_I2C0);
}
printk("iic init\n");
return 0;
err1:
unregister_chrdev_region(devid, 1);
err0:
return ret;
}
static void __exit iic_exit(void){
if(iic_int){
free_irq(IRQ_PHY_I2C0,(void *)1);
}
nxp_soc_gpio_set_io_func(i2c_gpio[0], 0);//恢復gpio功能
nxp_soc_gpio_set_io_func(i2c_gpio[1], 0);//恢復gpio功能
iounmap(base);
iounmap(clk_base);
iounmap(reset_addr);
unregister_chrdev_region(devid, 1);
cdev_del(&char_dev);
device_destroy(char_class,devid);
class_destroy(char_class);
printk("iic exit\n");
}
module_init(iic_init);
module_exit(iic_exit);
24AA025E48T-I/OT芯片手冊
1.器件地址
24AA025E48T-I/OT器件地址前五位位10100,六七位有電路決定。看一下電路圖
A0,A1分別是低電平和高電平。最後得地址是1010001X 也就是 0xa2
整體應用層代碼
#include <stdio.h>
#include <linux/ioctl.h>
#include <sys/ioctl.h>
#include <unistd.h>
#include <fcntl.h>
#include <unistd.h>
int fd;
int set_addr(){
int ret,cmd;
unsigned char data[2];
data[0] = 0xa2;
data[1] = 0xfb;
cmd = _IOC(_IOC_WRITE,0x00,0x00,0x02);
ret = ioctl(fd,cmd,data);
if(ret < 0){
perror("ioctl error");
return ret;
}
return 0;
}
int eeprom_read(){
int ret,cmd;
unsigned char data[2];
data[0] = 0xa2;
cmd = _IOC(_IOC_READ,0x00,0x00,0x02);
ret = ioctl(fd,cmd,data);
if(ret < 0){
perror("ioctl error");
return ret;
}else{
printf("value:%x\n", data[1]);
}
return 0;
}
int main(){
int ret;
fd = open("/dev/char_test_dev_1",O_RDWR);
ret = fd;
if(ret < 0){
perror("open /dev/char_test_dev_1 error");
return ret;
}
set_addr();
eeprom_read();
close(ret);
return 0;
}