I2C控制器编程_框架_i2c模块的四种工作模式


来源:百问网_嵌入式Linux wiki_jz2440 新1期视频维基教程 (视频文字版)

作者:韦东山

本文字数:3776,阅读时长:5分钟

我们现在来讲I2C控制器怎么写,它是I2C程序中最核心的地方,我们要先构造几个结构体,这几个结构体放在i2c_controller.h里面。

我们要发出I2c传输时,要构造出i2c_msg,把构造出的i2c_msg扔给下面的i2c_controller.c,i2c_controller.c会选择某一个i2c控制器,使用里面的master_xfer来传输数据, 所以我们需要构造出一个i2c_controller结构体。

i2c_controller.h文件

文件的内容如下所示:

#ifndef _I2C_CONTROLLER_H
#define _I2C_CONTROLLER_H

typedef struct i2c_msg {
	unsigned int addr;  /* 7bits */
	int flags;  /* 0 - write, 1 - read */
	int len;
	int cnt_transferred;
	unsigned char *buf;
}i2c_msg, *p_i2c_msg;

typedef struct i2c_controller {
	int (*int)(void);
	int (*master_xfer)(i2c_msg msgs, int num);
	char *name;
}i2c_controller, *p_i2c_controller;


#endif /* _I2C_CONTROLLER_H */

解析:我们构造这两个结构体,我们要把它放在i2c_controller.c把它用起来,

i2c_controller.c文件

文件的内容如下所示:

include "i2c_controller.h"

#define I2C_CONTROLLER_NUM 10

/* 有一个i2c_controller数组用来存放各种不同芯片的操作结构体 */
static p_i2c_controller p_i2c_controllers[I2C_CONTROLLER_NUM];
static p_i2c_controller p_i2c_con_selected;


void register_i2c_controller(p_i2c_controller *p)
{
	int i;
	for (i = 0; i < I2C_CONTROLLER_NUM; i++)
	{
		if (!p_i2c_controllers[i])
		{
			p_i2c_controllers[i] = p;
			return;
		}
	}
}

解析:register_i2c_controller函数用于把参数中的结构体指针,注册到p_i2c_controllers指针数组中。

/* 根据名字来选择某款I2C控制器 */
int select_i2c_controller(char *name)
{
	int i;
	for (i = 0; i < I2C_CONTROLLER_NUM; i++)
	{
		if (p_i2c_controllers[i] && !strcmp(name, p_i2c_controllers[i]->name))
		{
			p_i2c_con_selected = p_i2c_controllers[i];
			return 0;
		}
	}
	return -1;
}

解析:select_i2c_controller函数根据参数中的名字(name) 从p_i2c_controllers指针数组中取出对应的结构体指针复制给p_i2c_con_selected结构体指针(静态全局变量)。

/* 实现 i2c_transfer 接口函数 */

int i2c_transfer(i2c_msg msgs, int num)
{
	return p_i2c_con_selected->master_xfer(msgs, num);
}

解析:i2c_transfer接口函数,调用选择的p_i2c_con_selected成员中master_xfer函数。

void i2c_init(void)
{
	/* 注册下面的I2C控制器 */
	s3c2440_i2c_con_add();

	/* 选择某款I2C控制器 */

	/* 调用它的init函数 */
}

解析:s3c2440_i2c_con_add()函数,把定义的s3c2440_i2c_con结构体注册到p_i2c_controllers数组中。

s3c2440_i2c_controller.c文件

中断服务函数,当发成中断时,就会调用中断服务函数,代码如下

void i2c_interrupt_func(int irq)
{
	/* 每传输完一个数据将产生一个中断 */

	/* 对于每次传输, 第1个中断是"已经发出了设备地址" */
}

s3c2440_i2c_con_init函数,用来初始化I2C,控制器代码如下:

void s3c2440_i2c_con_init(void)
{
	/* 设置时钟 */
	/* [7] : IIC-bus acknowledge enable bit, 1-enable in rx mode
	 * [6] : 时钟源, 0: IICCLK = fPCLK /16; 1: IICCLK = fPCLK /512
	 * [5] : 1-enable interrupt
	 * [4] : 读出为1时表示中断发生了, 写入0来清除并恢复I2C操作
	 * [3:0] : Tx clock = IICCLK/(IICCON[3:0]+1).
	 * Tx Clock = 100khz = 50Mhz/16/(IICCON[3:0]+1)
	 */
	IICCON = (0<<6) | (1<<5) | (30<<0);

	/* 注册中断处理函数 */
	register_irq(27, i2c_interrupt_func);
}

解析:

1).IICCON = (0<<6) | (1<<5) | (30<<0); 设置IICCON控制寄存器。选择发送时钟,使能中断。

2).register_irq(27, i2c_interrupt_func):注册中断处理函数,当发生I2C中断的时候就会调用i2c_interrupt_func中断处理函数。

初始化完成后,就可以调用do_master_tx写I2C从机了,这个函数仅仅启动I2C传输,然后等待,直到数据在中断服务程序中传输完毕后再返回。函数代码如下:

void do_master_tx(p_i2c_msg msg)
{
	msg->cnt_transferred = 0;
	
	/* 设置寄存器启动传输 */
	/* 1. 配置为 master tx mode */
		
	/* 2. 把从设备地址写入IICDS */
	IICDS = msg->addr<<1;
	
	/* 3. IICSTAT = 0xf0 , 数据即被发送出去, 将导致中断产生 */
	IICSTAT = 0xf0;
	

	/* 后续的传输由中断驱动 */

	/* 循环等待中断处理完毕 */
	while (msg->cnt_transferred != msg->len);
}

解析:

1).IICDS = msg->addr<<1: 把从机地址(高7位,所以需要向右移一位)写入到IICDS寄存器中。

2).IICSTAT = 0xf0:设置IICSTAT寄存器,将s3c2440设为主机发送器,并发出S信号后,紧接着就发出从机地址。后续的传输工作将在中断服务程序中完成。

do_master_rx函数的实现和do_master_tx函数类似,代码如下:

void do_master_rx(p_i2c_msg msg)
{
	msg->cnt_transferred = 0;
	
	/* 设置寄存器启动传输 */
	/* 1. 配置为 Master Rx mode */
		
	/* 2. 把从设备地址写入IICDS */
	IICDS = (msg->addr<<1)|(1<<0);
	
	/* 3. IICSTAT = 0xb0 , 从设备地址即被发送出去, 将导致中断产生 */
	IICSTAT = 0xb0;
	

	/* 后续的传输由中断驱动 */

	/* 循环等待中断处理完毕 */
	while (msg->cnt_transferred != msg->len);
}

解析: 1).IICDS = (msg->addr<<1)|(1<<0):把从设备地址写入IICDS,前7位是从机地址,第8位表示传输方向(0表示写操作,1表示读操作)。

s3c2440传输函数,根据标志位flags,来指明是读/写(1:读 0:写)。代码如下:

int s3c2440_master_xfer(p_i2c_msg msgs, int num)
{
	int i;
	for (i = 0; i < num; i++)	
	{
		if (msgs[i]->flags == 0)/* write */
			do_master_tx(msgs[i]);
		else
			do_master_rx(msgs[i]);
	}
}

我们定义一个i2c_controller结构体s3c2440_i2c_con。下面的代码对他进行初始化。

static i2c_controller s3c2440_i2c_con = {
	.name = "s3c2440",
	.init = s3c2440_i2c_con_init,
	.master_xfer = s3c2440_master_xfer,
};

s3c2440_i2c_con_add函数把上面定义的s3c2440_i2c_con结构体注册到上层的i2c_controller数组中。

void s3c2440_i2c_con_add(void)
{
	register_i2c_controller(&s3c2440_i2c_con);
}

「新品首发」STM32MP157开发板火爆预售!首批仅300套

原文链接:,转发请注明来源!