栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 系统运维 > 运维 > Linux

imx6ull i2c 控制器

Linux 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

imx6ull i2c 控制器

备注:部分知识参考百问网手册

一、overview

1.1 特性

① 兼容标准I2C总线,多主机运行。
② 64种不同的串行时钟频率之一的软件可编程性。
③ 软件可选择的应答位。
④ 中断驱动,逐字节数据传输。
⑤ 仲裁丢失中断与自动模式切换从主到从。
⑥ 启动和停止信号生成/检测。
⑦ 重复启动信号生成。
⑧ 应答位生成和检测。
⑨ 总线忙检测。

1.2 模式和操作

支持两种模式:标准模式和快速模式,标准模式下I2C数据传输速率最高是100Kbits/s,在快速模式下数据传输速率最高为400Kbits/s。
两种模式配置上没有区别只是数据传输速率不同来区分。

1.3 外部引脚

当相应的PADs被设置为I2C功能后,I2Cn_SCL 和 I2Cn_SDA 的输入需要通过设置 IOMUX里面的SION bit 来使能。
可查看对应寄存器了解:IOMUXC_SW_MUX_CTL_PAD_GPIO1_IO02

1.4 时钟

I2C 时钟频率 = (PERCLK_ROOT frequency)/(division factor corresponding to IFDR),默认情况下,IPG_CLK_ROOT 和 PERCLK_ROOT被设置为49.5Mhz,选择从PLL2 的PFD2作为根时钟源,时钟路径计算如下(时钟路径可参考CCM章节):

PLL2 = 528 MHz
PLL2_PFD2 = 528 MHz * 18 / 24 = 396 MHz
IPG_CLK_ROOT = (PLL2_PFD2 / ahb_podf )/ ipg_podf = (396 MHz/4)/2 = 49.5MHz
PER_CLK_ROOT = IPG_CLK_ROOT/perclk_podf = 49.5 MHz/1 = 49.5 MHz

要设置I2C的波特率为100K, I2C分频值 = 49500000/100000 = 495,参考I2Cx_IFDR寄存器对应的表格可知 该寄存器设为0x37比较接近。

二、I2C 寄存器 2.1 I2C Address Register (I2Cx_IADR)
位域读写描述
[15:8]reservedR总是为0
[7:1]ADRR/W作为I2C从设备时的地址;使用软件复位时,这个寄存器不受影响
[0]reservedR总是为0
2.2 I2C Frequency Divider Register (I2Cx_IFDR)
位域读写描述
[15:6]reservedR总是为0
[5:0]ICR/WI2C clock rate,用来设置SCL时钟,IC值对应的分频系数要查表。在数据传输过程中,IC值不能改变;但是在发出S信号之前,可以改变IC值。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-BRV8Ghl2-1632816362805)(E:u-bootpici2c_ic_taible.jpg)]

2.3 I2C Control Register (I2Cx_I2CR)
位域读写描述
[15:8]reservedR总是0
[7]IENR/WI2C使能,0:I2C控制器被禁止,但是还可以访问它的寄存器;1:I2C控制器使能,要想让本寄存器中其他位起效,此位必须先置1
[6]IIENR/WI2C中断使能,0:I2C中断禁止,但是中断状态位(I2C_I2SR[IIF])还是可以使用的;1:I2C中断使能,发生中断时,中断状态位(I2C_I2SR[IIF])也会被设置
[5]MSTAR/W主从模式选择,0:从设备模式,MSTA从1变0时,会发出STOP信号,并变为从设备模式;1:主机模式,MSTA从0变1时,会发即STAT信号,并变为主机模式。注意1:I2C主设备失去总线时,硬件会清除此位,但是不会发出STOP信号注意2:要修改此位时,要先提供I2C控制器时钟注意3:软件清除此位时,会发出STOP信号;如果失去总线,硬件会清除此位
[4]MTXR/W发送/接收模式,0:接收模式,1:发送模式。作为主机时,应该根据数据传输的方向设置MTX位,当然,发送I2C设备地址时MTX总是1。
[3]TXAKR/W发送响应使能,当I2C设备处于接收状态时,此位才有效,0:在第9个时钟,发送响应信号,即把SDA拉低;1:在第9个时钟,不发送响应信号
[2]RSTAR/WRepeat start,读该位时总得到0,写:0:不发送repeat start信号;1:发送repeat start信号
[1:0]reservedR总是0
2.4 I2C Status Register (I2Cx_I2SR)
位域读写描述
[15:8]reservedR总是0
[7]ICFR/WData transferring bit,数据正在传输标志,0:正在传输;1:传输结束,当第9个时钟发出后,硬件置位
[6]IAASR/WI2C address as a slave bit,I2C控制器作用从设备时,通过此位来判断是否有人来寻址它:0:没人找我;1:有别的I2C主机在找我了
[5]IBBR/WI2C bus busy bit,用来显示总线状态,0:总线空闲,发现STOP信号时,IBB就被清0;1:总线忙,发现START信号时,IBB就被置1
[4]IALR/WArbitration lost,此位由硬件设置,软件要写入0来清零,0:没有丢失总线,1:总线丢失了
[2]SRWR/WSlave read/write,作为从设备时,主机发送设备地址时会带有读写位,读写位的内容就写入SRW中,0:slave receive,主设备要写数据;1:slave transmit,主设备要读数据
[1]IIFI2C中断状态,写0清中断,0:没有I2C中断发生;1:I2C中断发生了,如果IIEN=1则会产生中断,有这些中断:一个字节传输完毕; 作为从设备,接收到了一个吻合的地址; 总线丢失。
[0]RXAKReceived acknowledge,是否接收到回应信号,0:收到了:在一个字节传完后第9个时钟周期,收到了回应信号;1:没收到
2.5 I2C Data I/O Register (I2Cx_I2DR)
位域读写描述
[15:8]reservedR总是0
[7:0]DATAR/WData Byte。对于接收:存有接收到的最新数据,软件把数据读出来;对于发送:存放下一个即将要发送的数据,这是软件写进去的。
三、应用 3.1 程序框架


对于user application 根据业务需求来进行编写即可。

3.2 I2C 控制器驱动程序 3.2.1 i2c_init
void i2c_init(I2C_REGISTERS *I2C_base)
{
    
	I2C_base->I2CR &= ~(1 << 7);
    
	I2C_base->IFDR = 0x37;
	I2C_base->I2CR |= (1<<7);
}
3.2.2 i2c_check
uint8_t i2c_check(I2C_REGISTERS *I2C_base, uint32_t status)
{
	
	if(status & (1<<4))
	{
		I2C_base->I2SR &= ~(1<<4);	

		I2C_base->I2CR &= ~(1 << 7);	
		I2C_base->I2CR |= (1 << 7);	
		return I2C_ARBITRATIONLOST;
	} 
	else if(status & (1 << 0))     	
	{
		return I2C_NAK;		
	}
	return I2C_OK;
}
3.3 i2c_start
uint8_t i2c_start(I2C_REGISTERS *I2C_base, uint8_t ucSlaveAddr, uint32_t ulOpcode)
{

	if(I2C_base->I2SR & (1 << 5))			
		return 1;
	
	I2C_base->I2CR |=  (1 << 5) | (1 << 4);
     //设置数据寄存器I2DR: bit[7:0] : 要发送的数据, START信号后第一个数据是从设备地址 
	I2C_base->I2DR = ((uint32_t)ucSlaveAddr << 1) | ((I2C_READ == ulOpcode)? 1 : 0);
	return 0;
}
3.4 i2c_stop
uint8_t i2c_stop(I2C_REGISTERS *I2C_base)
{

	uint16_t usTimeout = 0xffff;

	
	I2C_base->I2CR &= ~((1 << 5) | (1 << 4) | (1 << 3));

	
	while((I2C_base->I2SR & (1 << 5)))
	{
		usTimeout--;
		if(usTimeout == 0)	
			return I2C_TIMEOUT;
	}
	return I2C_OK;
}
3.5 i2c_restart
uint8_t i2c_restart(I2C_REGISTERS *I2C_base, uint8_t ucSlaveAddr, uint32_t ulOpcode)
{
	
	if(I2C_base->I2SR & (1 << 5) && (((I2C_base->I2CR) & (1 << 5)) == 0))		
		return 6;
     //设置控制寄存器I2CR:  bit[4]: 1 发送(transmit)  bit[2]: 1 产生重新开始信号(Repeat start)
	    I2C_base->I2CR |=  (1 << 4) | (1 << 2);
	//设置数据寄存器I2DR:  bit[7:0] : 要发送的数据,  START信号后第一个数据是从设备地址
	I2C_base->I2DR = ((uint32_t)ucSlaveAddr << 1) | ((I2C_READ == ulOpcode)? 1 : 0);
	return 0;
}

3.6 i2c_read
void i2c_read(I2C_REGISTERS *I2C_base, uint8_t *pbuf, uint32_t len)
{
	volatile uint8_t dummy = 0;
	dummy++; 	

	
	while(!(I2C_base->I2SR & (1 << 7))); 
	
	I2C_base->I2SR &= ~(1 << 1); 			   
	I2C_base->I2CR &= ~((1 << 4) | (1 << 3));	
	
	
	if(len == 1)
        I2C_base->I2CR |= (1 << 3);
	dummy = I2C_base->I2DR; 

	while(len--)
	{
		while(!(I2C_base->I2SR & (1 << 1))); 		
		I2C_base->I2SR &= ~(1 << 1);			
	 	if(len == 0)
        {
        	i2c_stop(I2C_base); 			
        }
        if(len == 1)
        {
            I2C_base->I2CR |= (1 << 3);
        }
		*pbuf++ = I2C_base->I2DR;
	}
}
3.7 i2c_write
void i2c_write(I2C_REGISTERS *I2C_base, const uint8_t *pbuf, uint32_t len)
{
	
	while(!(I2C_base->I2SR & (1 << 7))); 
	
	I2C_base->I2SR &= ~(1 << 1); 	  
	I2C_base->I2CR |= 1 << 4;	      
	while(len--)
	{
		I2C_base->I2DR = *pbuf++; 	    	
		while(!(I2C_base->I2SR & (1 << 1)));  	
		I2C_base->I2SR &= ~(1 << 1);			

		
		if(i2c_check(I2C_base, I2C_base->I2SR))
			break;
	}
	I2C_base->I2SR &= ~(1 << 1);     
	i2c_stop(I2C_base); 	         
}
3.8 i2c_transfer
uint8_t i2c_transfer(I2C_REGISTERS *I2C_base, I2C_TRANSFER *transfer)
{
	uint32_t ulRet = 0;
	uint32_t ulOpcode = transfer->ulOpcode;

	//开始前准备工作,清除标志位*bit-4 IAL 仲裁位,bit-1 IIF 中断标志位
	I2C_base->I2SR &= ~((1 << 1) | (1 << 4));
	
	while(!((I2C_base->I2SR >> 7) & 0X1)){}; 
	//如果要读某个寄存区,寄存器地址要先"写"给从设备 所以方向要"先写","后读"
    if ((transfer->ulSubAddressLen > 0) && (transfer->ulOpcode == I2C_READ))
    {
        ulOpcode = I2C_WRITE;
    }
	ulRet = i2c_start(I2C_base, transfer->ucSlaveAddress, ulOpcode);
	if (ulRet)
	{
		return ulRet;
	}
	
	while(!(I2C_base->I2SR & (1 << 1))){};
	
	ulRet = i2c_check(I2C_base, I2C_base->I2SR);
	if (ulRet)
	{
	    i2c_stop(I2C_base); 			
		return ulRet;
	}
	
	if (transfer->ulSubAddressLen)
	{
		do
		{
			
		    I2C_base->I2SR &= ~(1 << 1); 
			
			transfer->ulSubAddressLen--;
			I2C_base->I2DR = ((transfer->ulSubAddress) >> (8 * transfer->ulSubAddressLen)); 
			while(!(I2C_base->I2SR & (1 << 1)));  	
            
            ulRet = i2c_check(I2C_base, I2C_base->I2SR);
            if(ulRet)
            {
             	i2c_stop(I2C_base); 				
             	return ulRet;
            }
		}
		while ((transfer->ulSubAddressLen > 0) && (ulRet == I2C_OK));

		if (I2C_READ == transfer->ulOpcode)
		{
            I2C_base->I2SR &= ~(1 << 1);			
            i2c_restart(I2C_base, transfer->ucSlaveAddress, I2C_READ); 
    		while(!(I2C_base->I2SR & (1 << 1))){}; 
            
			ulRet = i2c_check(I2C_base, I2C_base->I2SR);
            if(ulRet)
            {
             	ulRet = I2C_ADDRNAK;
                i2c_stop(I2C_base); 		
                return ulRet;  
            }
		}
	}
    
    if ((I2C_WRITE == transfer->ulOpcode) && (transfer->ulLenth > 0))
    {
    	i2c_write(I2C_base, transfer->pbuf, transfer->ulLenth);
	}

    
    if ((I2C_READ == transfer->ulOpcode) && (transfer->ulLenth > 0))
    {
       	i2c_read(I2C_base, transfer->pbuf, transfer->ulLenth);
	}
	return 0;	
}
3.3 挂载的从机驱动程序如eeprom

1.初始化I2C 控制器对于的引脚
2.调用I2C 控制器驱动接口去读写eeprom 里面的寄存器的内容

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/277876.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号