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;
}

猜你喜欢

转载自blog.csdn.net/qq_16054639/article/details/106557391