I2C相关实验

I2C简介

I2C是很常见的一种总线协议,I2C是NXP公司设计的,I2C使用两条线在主控制器和从机之间进行数据通信。一条是SCL(串行时钟线),另外一条是SDA(串行数据线),这两条数据线需要接上拉电阻,总线空闲的时候SCL和SDA处于高电平。I2C总线标准模式下速度可以达到100Kb/S,快速模式下可以达到400Kb/S。

I2C是支持多从机的:

在这里插入图片描述

一般会接一个4.7k的上拉电阻(VDD)。

I2C总线工作是按照一定的协议来运行的 ,其中相关术语:

起始位

I2C通信起始标志,在SCL为高电平的时候,SDA出现下降沿就表示为起始位。

停止位 

停止I2C通信的标志位,和起始位的功能相反。在SCL位高电平的时候,SDA出现上升沿就表示为停止位。

在这里插入图片描述

数据传输 

I2C总线在数据传输的时候要保证在SCL高电平期间,SDA上的数据稳定,因此SDA上的数据变化只能在SCL低电平期间发生。

在这里插入图片描述

 应答信号

当I2C主机发送完8位数据以后会将SDA设置为输入状态,等待I2C从机应答,也就是等到I2C从机告诉主机它接收到数据了。应答信号是由从机发出的,主机需要提供应答信号所需的时钟,主机发送完8位数据以后紧跟着的一个时钟信号就是给应答信号使用的。从机通过将SDA拉低来表示发出应答信号,表示通信成功,否则表示通信失败。
I2C主机与从机之间的操作只有读与写两个操作。其时序分别为:

在这里插入图片描述

1)、开始信号。
2)、发送I2C设备地址,每个I2C器件都有一个设备地址,通过发送具体的设备地址来决定访问哪个I2C器件。这是一个8位的数据,其中高7位是设备地址,最后1位是读写位,为1的话表示这是一个读操作,为0的话表示这是一个写操作。
3)、 I2C器件地址后面跟着一个读写位,为0表示写操作,为1表示读操作。
4)、从机发送的ACK应答信号。
5)、重新发送开始信号。
6)、发送要写写入数据的寄存器地址。
7)、从机发送的ACK应答信号。
8)、发送要写入寄存器的数据。
9)、从机发送的ACK应答信号。
10)、停止信号。

在这里插入图片描述

1)、主机发送起始信号。
2)、主机发送要读取的I2C从设备地址。
3)、读写控制位,因为是向I2C从设备发送数据,因此是写信号。
4)、从机发送的ACK应答信号。
5)、重新发送START信号。
6)、主机发送要读取的寄存器地址。
7)、从机发送的ACK应答信号。
8)、重新发送START信号。
9)、重新发送要读取的I2C从设备地址。
10)、读写控制位,这里是读信号,表示接下来是从I2C从设备里面读取数据。
11)、从机发送的ACK应答信号。
12)、从I2C器件里面读取到的数据。
13)、主机发出NO ACK信号,表示读取完成,不需要从机再发送ACK信号了。
14)、主机发出STOP信号,停止I2C通信。
操作I2C需要几个重要的寄存器:

在这里插入图片描述

I2Cx_IADR(x=1~4)寄存器:寄存器I2Cx_IADR只有ADR(bit7:1)位有效,用来保存I2C从设备地址数据。当我们要访问某个I2C从设备的时候就需要将其设备地址写入到ADR里面。

在这里插入图片描述

寄存器I2Cx_IFDR:寄存器I2Cx_IFDR也只有IC(bit5:0)这个位,用来设置I2C的波特率,I2C的时钟源可以选择IPG_CLK_ROOT=66MHz,I.MX6U的I2C支持两种模式:标准模式和快速模式,标准模式下I2C数据传输速率最高是100Kbits/s,在快速模式下数据传输速率最高为400Kbits/s。通过设置IC位既可以得到想要的I2C波特率。 

在这里插入图片描述

这里可以设置为0x15或0x38,即分频为640。

在这里插入图片描述

寄存器I2Cx_I2CR:这个是I2C控制寄存器,其各个位的作用:

IEN(bit7):I2C使能位,为1的时候使能I2C,为0的时候关闭I2C。
IIEN(bit6):I2C中断使能位,为1的时候使能I2C中断,为0的时候关闭I2C中断。
MSTA(bit5):主从模式选择位,设置IIC工作在主模式还是从模式,为1的时候工作在主模式,为0的时候工作在从模式。
MTX(bit4):传输方向选择位,用来设置是进行发送还是接收,为0的时候是接收,为1的时候是发送。
TXAK(bit3):传输应答位使能,为0的话发送ACK信号,为1的话发送NO ACK信号。
RSTA(bit2):重复开始信号,为1的话产生一个重新开始信号。

在这里插入图片描述

寄存器I2Cx_I2SR:I2C的状态寄存器 。

寄存器I2Cx_I2SR的各位含义如下:
ICF(bit7):数据传输状态位,为0的时候表示数据正在传输,为1的时候表示数据传输完成。
IAAS(bit6):当为1的时候表示I2C地址,也就是I2Cx_IADR寄存器中的地址是从设备地址。
IBB(bit5):I2C总线忙标志位,当为0的时候表示I2C总线空闲,为1的时候表示I2C总线忙。
IAL(bit4):仲裁丢失位,为1的时候表示发生仲裁丢失。
SRW(bit2):从机读写状态位,当I2C作为从机的时候使用,此位用来表明主机发送给从机的是读还是写命令。为0的时候表示主机要向从机写数据,为1的时候表示主机要从从机读取数据。
IIF(bit1):I2C中断挂起标志位,当为1的时候表示有中断挂起,此位需要软件清零。
RXAK(bit0):应答信号标志位,为0的时候表示接收到ACK应答信号,为1的话表示检测到NO ACK信号。
最后一个寄存器就是I2Cx_I2DR,这是I2C的数据寄存器,此寄存器只有低8位有效,当要发送数据的时候将要发送的数据写入到此寄存器,如果要接收数据的话直接读取此寄存器即可得到接收到的数据。
了解后开始写:

  #include "bsp_i2c.h"
  #include "bsp_delay.h"
  #include "stdio.h"
 

 void i2c_init(I2C_Type *base)
  {
      /* 1、配置I2C */
      base->I2CR &= ~(1 << 7); /* 要访问I2C的寄存器,首先需要先关闭I2C */
  

     base->IFDR = 0X15 << 0;
  
      /* 设置寄存器I2CR,开启I2C */
      base->I2CR |= (1<<7);
  }
  

  unsigned char i2c_master_repeated_start(I2C_Type *base, 
unsigned char address,  
enum i2c_direction direction)  {
     /* I2C忙并且工作在从模式,跳出 */
      if(base->I2SR & (1 << 5) && (((base->I2CR) & (1 << 5)) == 0))       
          return 1;
  
      /*
       * 设置寄存器I2CR
       * bit[4]: 1 发送
       * bit[2]: 1 产生重新开始信号
       */
      base->I2CR |=  (1 << 4) | (1 << 2);
  
      /*
       * 设置寄存器I2DR,bit[7:0] : 要发送的数据,这里写入从设备地址
       */ 
      base->I2DR = ((unsigned int)address << 1) | 
((direction == kI2C_Read)? 1 : 0);
      return 0;
  }

  unsigned char i2c_master_start(I2C_Type *base, 
unsigned char address,  
enum i2c_direction direction)
  {
      if(base->I2SR & (1 << 5))           /* I2C忙 */
          return 1;
  
     /*
       * 设置寄存器I2CR
       * bit[5]: 1 主模式
       * bit[4]: 1 发送
       */
      base->I2CR |=  (1 << 5) | (1 << 4);
  
      /*
       * 设置寄存器I2DR,bit[7:0] : 要发送的数据,这里写入从设备地址
       */ 
      base->I2DR = ((unsigned int)address << 1) | 
((direction == kI2C_Read)? 1 : 0);
      return 0;
  }

  unsigned char i2c_check_and_clear_error(I2C_Type *base, 
unsigned int status)
  {
      if(status & (1<<4)) 				/* 检查是否发生仲裁丢失错误 	*/
      {
          base->I2SR &= ~(1<<4);      	/* 清除仲裁丢失错误位      	*/
          base->I2CR &= ~(1 << 7);    	/* 先关闭I2C               	*/
          base->I2CR |= (1 << 7);     	/* 重新打开I2C             	*/
          return I2C_STATUS_ARBITRATIONLOST;
      } 
      else if(status & (1 << 0))      	/* 没有接收到从机的应答信号 	*/
      {
          return I2C_STATUS_NAK;      	/* 返回NAK(No acknowledge) */
      }
      return I2C_STATUS_OK;
  }

 unsigned char i2c_master_stop(I2C_Type *base)
 {
     unsigned short timeout = 0XFFFF;
 
     /* 清除I2CR的bit[5:3]这三位 */
     base->I2CR &= ~((1 << 5) | (1 << 4) | (1 << 3));
     while((base->I2SR & (1 << 5)))	/* 等待忙结束 	*/
     {
         timeout--;
         if(timeout == 0)    			/* 超时跳出 		*/
             return I2C_STATUS_TIMEOUT;
     }
     return I2C_STATUS_OK;
 }

 void i2c_master_write(I2C_Type *base, const unsigned char *buf, 
unsigned int size)
 {   
     while(!(base->I2SR & (1 << 7))); 		/* 等待传输完成 	*/
     base->I2SR &= ~(1 << 1);    				/* 清除标志位 	*/
     base->I2CR |= 1 << 4;       				/* 发送数据 		*/
     while(size--)
     {
         base->I2DR = *buf++;    /* 将buf中的数据写入到I2DR寄存器 */
         while(!(base->I2SR & (1 << 1))); 	/* 等待传输完成 	*/    
         base->I2SR &= ~(1 << 1);            	/* 清除标志位 	*/
 
         /* 检查ACK */
         if(i2c_check_and_clear_error(base, base->I2SR))
             break;
     }
     base->I2SR &= ~(1 << 1);
     i2c_master_stop(base);  					/* 发送停止信号 	*/
 }

 void i2c_master_read(I2C_Type *base, unsigned char *buf, 
unsigned int size)
 {
     volatile uint8_t dummy = 0;
 
     dummy++;   			 				/* 防止编译报错 		*/
     while(!(base->I2SR & (1 << 7))); 	/* 等待传输完成 		*/
     base->I2SR &= ~(1 << 1);            	/* 清除中断挂起位 	*/
     base->I2CR &= ~((1 << 4) | (1 << 3));   /* 接收数据 	*/
     if(size == 1)   	/* 如果只接收一个字节数据的话发送NACK信号 */
         base->I2CR |= (1 << 3);
 
     dummy = base->I2DR; 					/* 假读 			*/
     while(size--)
     {
         while(!(base->I2SR & (1 << 1)));	/* 等待传输完成 	*/    
         base->I2SR &= ~(1 << 1);            	/* 清除标志位 	*/
 
         if(size == 0)
             i2c_master_stop(base);          	/* 发送停止信号 	*/
         if(size == 1)
             base->I2CR |= (1 << 3);
         *buf++ = base->I2DR;
     }
 }
 

 unsigned char i2c_master_transfer(I2C_Type *base, 
struct i2c_transfer *xfer)
 {
     unsigned char ret = 0;
     enum i2c_direction direction = xfer->direction; 
 
     base->I2SR &= ~((1 << 1) | (1 << 4));		/* 清除标志位 	*/
     while(!((base->I2SR >> 7) & 0X1)){};    	/* 等待传输完成 	*/
     /* 如果是读的话,要先发送寄存器地址,所以要先将方向改为写 */
     if ((xfer->subaddressSize > 0) && (xfer->direction == 
kI2C_Read))
         direction = kI2C_Write;
     ret = i2c_master_start(base, xfer->slaveAddress, direction); 
     if(ret)
         return ret;
     while(!(base->I2SR & (1 << 1))){}; 	/* 等待传输完成 */
     ret = i2c_check_and_clear_error(base, base->I2SR); 
     if(ret)
     {
         i2c_master_stop(base);           		/* 发送出错,发送停止信号 */
         return ret;
     }     
     /* 发送寄存器地址 */
     if(xfer->subaddressSize)
   {
         do
         {
             base->I2SR &= ~(1 << 1);            	/* 清除标志位 	*/
             xfer->subaddressSize--;           	/* 地址长度减一 	*/
             base->I2DR =  ((xfer->subaddress) >> (8 * 
xfer->subaddressSize)); 
             while(!(base->I2SR & (1 << 1))); 	/* 等待传输完成 */
             /* 检查是否有错误发生 */
             ret = i2c_check_and_clear_error(base, base->I2SR);
             if(ret)
             {
                 i2c_master_stop(base);       		/* 发送停止信号 */
                 return ret;
             }  
         } while ((xfer->subaddressSize > 0) && (ret == 
I2C_STATUS_OK));
 
         if(xfer->direction == kI2C_Read)    	/* 读取数据		 */
         {
             base->I2SR &= ~(1 << 1);            	/* 清除中断挂起位 */
             i2c_master_repeated_start(base, xfer->slaveAddress, 
kI2C_Read);
            while(!(base->I2SR & (1 << 1))){};	/* 等待传输完成 	*/
             /* 检查是否有错误发生 */
             ret = i2c_check_and_clear_error(base, base->I2SR);
             if(ret)
             {
                 ret = I2C_STATUS_ADDRNAK;
                 i2c_master_stop(base);      		/* 发送停止信号 */
                 return ret;  
             }         
         }
     }   
 
     /* 发送数据 */
     if ((xfer->direction == kI2C_Write) && (xfer->dataSize > 0))
         i2c_master_write(base, xfer->data, xfer->dataSize);
     /* 读取数据 */
     if ((xfer->direction == kI2C_Read) && (xfer->dataSize > 0))
         i2c_master_read(base, xfer->data, xfer->dataSize);
     return 0;   
 }

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值