18.【linux驅動】IIC驅動(4418讀取EEPROM:24AA025E48T-I/OT)

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
  1. 初始化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));
}
  1. 找一下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);
    }
  1. 還需要一個設別忙等判斷
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關鍵操作邏輯

  1. 開始傳輸
    四種傳輸模式的開始部分合並一下其實就是設置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)
  • 是否結束通信
  1. 清除中斷
	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));
  1. 判斷中斷是否異常
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;
}
  1. 裝填數據
裝填數據向ICDR寫入數據即可,祥見下文
  1. 繼續傳輸
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));
}
  1. 停止傳輸
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;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章