I2C协议

I2C总线在嵌入式系统中占据重要的地位,由于占用的引脚少及其传输稳定,所以很多设备接口都使用I2C总线。

I2C是主从设备。典型的接口如下图。每个设备都有着唯一的地址(地址可以配置、有些芯片是通过配置硬件的引脚或者是配置相应的寄存器),以便和主设备进行通信。IIC属于主从结果,每次通信都是由master来发起,从方被动接受通信。通信过程中的时钟始终都是由master来产生。

IIC的协议:

  • 空闲状态:SDA和SCL都应该保持高电平。master发起起始条件后总线会进入busy状态,通信结束后立刻进入空闲。
  • 数据的有效性:在时钟为高电平期间,进行数据采集。时钟为低电平的时候数据线上的数据可以发生变化。
  • 起止条件:SCL为高电平时,SDA由高变低是起始信号,反则是停止信号。起止信号都是由主设备发出
  • 应答:每次传输完一个字节的数据都需要有ACK,应答信号由接收方发出,用来表示已经收到数据。是否需要ACK可以通过设置寄存器来控制。
  • 传输单位:每次都是以字节为单位进行传输的(MSB),没传输完一个字节,在没有新的数据写入IICDS前,SCL一直为低电平,只有在写入新的数据后,SCL才会被释放。接受也是同样的,只有在读出数据后SCL才会被释放。从而进行下次数据的传输。
  • 数据格式:在IIC数据线上传输数据是高位在前,起始地址后的数据是从机的地址(7bit+方向 w/r)。
  • 进入master模式:发送起始信号进入。SCL由主设备生成
  • 传输终止:①master发出从设备的地址后,没有收到从设备的地址ACK时。SDA保持高电平。这种情况下master应该生成结束条件用来终止传输。②在master接收要终止传输,传输完最后一个字节数据不生成ACK,slave释放SDA,master产生结束条件。

在很多处理器中都集成了IIC控制器,帮助我们实现了这些复杂的时序要求。我们可以通过控制IIC控制器来控制IIC的工作。

S3C2440中集成了IIC控制器,所以我们只需要操作寄存器就可以实现对IIC的通信了。下面的内容是针对s3c2440芯片的IIC操作。别的芯片也是类似的,需要参考芯片手册。

s3c2240 master rx和tx如下:

在rx/tx中接收到一个字节的数据会产生中断(都在中断里面处理,保持中断处理当前的数据)。①在发送数据模式中,一个字节的数据发送完后,产生中断。SCL线被拉低,在需要发送的新数据写入IICDS后,SCL才会释放。②在接收数据模式中,一个字节的数据接收完成后,也会产生中断,同样SCL线被拉低,在IICDS寄存器中数据被读取完成后,SCL线才会被释放,用来传输新的数据。

操作EEPROM时,在读取最后一个字节的数据后不产生ACK信号,这样是为了产生结束条件。在发送模式需要先写入数据到IICDS然后在清除IIC中断。s3c2440中每次操作IIC都需要往IICSTAT中bit4写入1.

 

#include "iic_controller.h"

#define IICCON 			(*(volatile unsigned int*)0x54000000)
#define IICSTAT 			(*(volatile unsigned int*)0x54000004)
#define IICADD 			(*(volatile unsigned int*)0x54000008)
#define IICDS 			(*(volatile unsigned int*)0x5400000C)
#define GPECON 			(*(volatile unsigned int*)0x56000040)

#define IIC_IRQ_NUM		(27)
#define IIC_NO_SLAVE	(-2)
#define IIC_ERR			(-1)
#define IIC_END			(0)

static P_iic_msg p_current_transfer_msg;

/*具体的芯片实现自己的iic控制器*/
/*name 
* init 		初始化函数
*master_xfer 	传输函数
*/

void iic_tx_end(int flags)
{
	IICSTAT = 0xd0;
	IICCON &=~(1<<4);
	p_current_transfer_msg->status = flags;
	head_delayms(1);
}

void iic_rx_end(int flags)
{
	IICSTAT = 0x90;
	IICCON &=~(1<<4);
	p_current_transfer_msg->status = flags;
	head_delayms(1);
}

int istransfer_last(void)
{
	if(p_current_transfer_msg->cnt_complete == p_current_transfer_msg->len-1){
		return 1;
	}else{
		return 0;
	}
}

void  iic_irq_handler(int irq)
{
	puts_("s3c2440 iic irq handler\n\r");
	/*中断服务函数,在中断中处理数据的传输,每传输完一个字节的数据都会产生一个中断*/
	p_current_transfer_msg->cnt_complete++;
	/*每次传输的第一个中断都是处理从设备地址的ACK*/
	if(p_current_transfer_msg->flags ==0){/*write*/
		/*对于第一个中断是检测从设备是否存在*/
		/*如果ACK,则存在从设备,继续传输,否则结束传输
		*/
		if(p_current_transfer_msg->cnt_complete ==0){/*第一次传输的ACK*/
			if(IICSTAT &1){
				/*没有ACK结束传输
				*往IICSTAT中写入0XD0,清除中断pending。等待停止信号的产生。
				*/
				iic_tx_end(IIC_NO_SLAVE);
				puts_("---no ack---\n\r");
				return ;
			}
		}

		if(p_current_transfer_msg->cnt_complete < p_current_transfer_msg->len){
			if(IICSTAT &1){/*没有产生ACK错误结束*/
				iic_tx_end(IIC_ERR);
				puts_("---tx is no ack---\n\r");
				return;
			}else{/*有ACK,继续传输数据*/
				/*把数据写入到IICDS中,然后清除pending*/
				IICDS = p_current_transfer_msg->buf[p_current_transfer_msg->cnt_complete];
				IICCON &=~(1<<4);
			}
		}else{
			/*数据完成结束*/
			iic_tx_end(IIC_END);
			puts_("---tx is complete---\n\r");
			return ;
		}
		
	}else{/*read*/
		if(p_current_transfer_msg->cnt_complete ==0){
			/*第一次中断,如果有ACK则恢复传输,否则的结束*/
			if(IICSTAT&1){
				iic_rx_end(IIC_NO_SLAVE);
				puts_("---rx is no ack---\n\r");
				return ;
			}else{
				/*恢复继续传输*/
				IICCON &=~(1<<4);
				return ;
			}
		}else{
			if(p_current_transfer_msg->cnt_complete < p_current_transfer_msg->len){
				/*没有传输结束继续传输*/
				p_current_transfer_msg->buf[p_current_transfer_msg->cnt_complete-1] = IICDS;
				if(istransfer_last()){
					/*最后一个数据传输,不用ACK*/
					IICCON&=~(1<<7);
				}
				IICCON &= ~(1<<4);
				return;
			}else{
				/*传输完成*/
				iic_rx_end(IIC_END);
				puts_("---rx is complete---\n\r");
				return;
			}
		}
	}
}

int s3c2440_i2c_con_init(void)
{
	puts_("s3c2440 iic init\n\r");
	/*初始化IIC使用的引脚,配置成IIC*/
	GPECON &=~(0xf << 28);
	GPECON |= (0xa <<28);
	/*设置时钟*/
	/*bit 7 :ACK是否使能,bit6选择IIC的时钟源bit5:IIC中断是否使能 bit 中断挂起bit[3:0]:时钟预分频*/
	IICCON = (1<<7) | (0<<6) | (1<<5) |(7<<0);

	/*注册中断服务函数*/
	register_irq(IIC_IRQ_NUM,iic_irq_handler);

}

int do_master_tx(P_iic_msg msg)
{
	msg->cnt_complete = -1;
	msg->status = 0;
	p_current_transfer_msg = msg;
	/*设置寄存器启动传输*/
	/*1.配置为tx模式*/
	IICSTAT =(1<<4);
	/*2.把从设备的地址写入数据寄存器*/
	/*3.往IICSTAT中写入0xf0数据即将被发送出去,将导致中断产生*/
	IICDS    = (msg->addr<<1) | 0;
	IICSTAT = 0Xf0;
	/*后续的传输由中断驱动*/

	/*循环等待中断处理完毕*/
	while(!msg->status && msg->cnt_complete < msg->len);
	if(msg->status){
		return -1;
	}else{
		return 0;
	}
}

int do_master_rx(P_iic_msg msg)
{
	msg->cnt_complete = -1;
	msg->status = 0;
	p_current_transfer_msg = msg;
	/*设置寄存器启动传输*/
	/*1.配置为rx模式*/
	IICSTAT =(1<<4);
	/*2.把从设备的地址写入数据寄存器*/
	/*3.往IICSTAT中写入0xb0数据即将被发送出去,将导致中断产生*/
	IICDS    = (msg->addr<<1) | 1;
	IICSTAT = 0Xb0;
	/*后续的传输由中断驱动*/
	
	/*循环等待中断处理完毕*/
	while(!msg->status && msg->cnt_complete <msg->len) ;
	if(msg->status){
		return -1;
	}else{
		return 0;
	}
}

int s3c2440_master_xfer(P_iic_msg msgs, int num)
{
	int i,ret ;
	for(i = 0;i< num;i++){
		if(msgs[i].flags ==0){
			ret =do_master_tx(&msgs[i]);
			if(ret){return ret;}
		}else{
			ret =do_master_rx(&msgs[i]);	
			if(ret){return ret;}
		}
	}
	return 0;
}

static iic_controller_t s3c2440_iic_con={
	.name = "s3c2440",
	.init = s3c2440_i2c_con_init,
	.master_xfer = s3c2440_master_xfer,
};

void add_s3c2440_con(void)
{
	register_i2c_controller(&s3c2440_iic_con);
}

 

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值