通信学习笔记1——I2C总线

通信学习笔记1——I2C总线

1.简介

1.由两条线缆,分别为串行时钟线(SCL)串行数据线(SDA)
2.一般这两条线需要接入上拉电阻(4.7K),总线空闲时候SCL和SDA处于高电平
3.传输速率一般分为:标准模式:100Kb/s,快速模式400Kb/s
4.I2C支持多从机,不同I2C从设备有不同器件地址(一般为7个bit位)
5.半双工

2.起始位

SCL高电平 SDA下降沿

3.结束位

SCL高电平 SDA上升沿

4.数据传输

IIC总线再数据传输时要保证在SCL高电平期间,SDA上数据稳定SDA数据变化只能在SCL低电平期间发生

5.应答信号

IIC发送完八位数据后会将SDA设置为输入状态,等待从机应答,主机在发送完八位数据后紧跟着一个时钟信号就是用于应答的,此时从机通过将SDA拉低表示发出应答信号,则通信成功;

6.写时序

1.开始信号
2.发送IIC设备地址,高七位为地址,最后一位为读写位(为1的为读操作,为0为写操作)
3.读写位 1为读 0 为写
4.从机发送应答ACK
5.开始信号
6.发送写入数据寄存器地址
7.从机发送应答ACK
8.发送数据
9.从机发送应答ACK
10.停止信号

7.读时序

1.开始信号
2.发送I2C设备地址,高7位为地址,最后一位为读写位(为1为读操作,0为写操作)
3.读写位 为1读操作 0为写操作 此时是写信号
4.从机发送应答ACK
5.开始信号
6.主机发送要读取的寄存器地址
7.从机发送应答ACK
8.开始信号
9.主机发送要读取的I2C从设备地址
10.读写位 为1为读 为0是写操作
11.从机发送应答ACK
12.从I2C器件读取数据
13.主机发出 NO ACK信号, 表示读取完成,从机不需要发送ACK信号
14.主机发送stop信号,停止通信

8. I.MX6ULL裸机I2C实例

#ifndef __BSP_I2C_H__
#define __BSP_I2C_H__

#include "imx6ul.h"

// 相关宏定义
#define I2C_STATUS_OK              (0)// ok标志
#define I2C_STATUS_BUSY            (1)// 忙位标志
#define I2C_STATUS_IDLE            (2)// 空闲标志
#define I2C_STATUS_NAK             (3)// 应答标志
#define I2C_STATUS_ARBITRATIONLOST (4)// 仲裁丢失标志
#define I2C_STATUS_TIMEOUT         (5)// 超时标志
#define I2C_STATUS_ADDRNAK         (6)

// I2C方向枚举类型
enum i2c_direction
{
	kI2C_Write = 0, // 主机向从机写数据
	kI2C_Read  = 1  // 主机通过从机读数据
};

// I2C传输结构
struct i2c_transfer
{
	unsigned char slaveAddress;     // 从机地址(7bits)
	enum i2c_direction direction;   // 数据传输方向
	unsigned int subaddress;        // 寄存器地址
	unsigned char subaddressSize;   // 寄存器地址字节长
	unsigned char * volatile data;  // 数据缓冲区
	volatile unsigned char dataSize;// 数据缓冲区长度
};
#include"bsp_i2c.h"

/*
*@description   : 初始化I2C,设置波特率为100KHz
*@Paaram - base : 需要初始化的I2C
*@return        :无
*/
void i2c_init(I2C_Type * base)
{
	/*
	关闭I2C
	I2CR寄存器
	bit7: 0 关闭I2C
    */
    base->I2CR &= ~(1 << 7);
	
	/*
	设置I2C波特率为100KHz
	I2C时钟为66MHz
	当分频值为640时。I2C时钟约为 66000000/640 100K
	当IFDR寄存器bit[5:0]为0x15时,分频系数为640分频
    */
	base->IFDR &= (0x15 << 0)
	
	/*
	打开I2C
	I2CR寄存器
	bit7: 1 开启I2C
	*/
	base->I2CR |= (1 << 7);
}

/*
*@description        : I2C主机开始信号及从机地址发送
*@Paaram - base      : 需要初始化的I2C
*@Paaram - address   : 从机地址
*@Paaram - direction : 读/写操作位
*@return             :0 正常 其他: 异常
*/
unsigned char i2C_master_start(I2C_Type * base, unsigned char address, enum i2C_direction direction)
{
	// 判断I2C处于是否处于忙
	if(base->I2SR & (1 << 5))
	{
		return 1;
	}
	/*
	设置I2C工作模式为主机模式且为发送状态
	I2CR寄存器
	bit5: 1 主机模式
	bit4: 1 发送
	*/
	base->I2CR |=1 << 5| (1 << 4);
	/*
	设置要发送的从机地址
	I2DR寄存器
	bit[7:0]:要发送的数据(从机地址)
	*/
	base->I2DR = (address << 1| ((address == kI2C_Read) ? 1 : 0);

	return 0;
}
/*
*@description        : I2C主机发送停止信号
*@Paaram - base      : 需要初始化的I2C
*@return             :0 正常 其他: 异常
*/**
unsigned char i2C_master_stop(I2C_Type * base)
{
	unsigned short timeout = 0xFFFF;
	/*
	清除I2CR寄存器bit[5:3]
	bit5:工作模式
	bit4:发送/接收模式
	bit3:ACK/NO ACK
	*/
	base->I2CR &= ~((1 << 5) | (1 << 4) | (1 << 3));
	
	// 等待忙结束
	while(base->I2SR & (1 << 5))
	{
		timeout--;
		if(0 == timeout)
		{
			return I2C_STATUS_BUSY;
		}
	}
	return I2C_STATUS_OK;
}

/*
*@description        : I2C主机重新开始信号及从机地址发送
*@Paaram - base      : 需要初始化的I2C
*@Paaram - address   : 从机地址
*@Paaram - direction : 读/写操作位
*@return             :0 正常 其他: 异常
*/
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;
	}
	/*
	设置I2C工作模式发送状态且发送的是重新开始信号
	I2CR寄存器
	bit4: 1 发送
	bit2: 1 产生重新开始信号
	*/
    base->I2CR |= (1 << 5) | (1 << 2);
    /*
	设置要发送的从机地址
	I2DR寄存器
	bit[7:0]:要发送的数据(从机地址)
	*/
	base->I2DR = (address << 1) | ((direction == kI2C_Read) ? 1 : 0);
	return 0;
}
/*
*@description        : 检查并清除错误
*@Paaram - base      : 需要初始化的I2C
*@Paaram - status    : 需要检查的内容
*@return             :0 正常 其他: 异常
*/
unsigned char i2c_cheack_and_clean_error(I2C_Type * base, unsigned char status)
{
	// 判断是否发生仲裁丢失
	if(status & (1 << 4))
	{
		base->I2SR &= ~(1 << 5);// 清除标志
		
		// 重启I2C
		base->I2CR &= ~(1 << 7);// 关闭I2C
		base->I2CR |= (1 << 7);// 打开I2C
		return I2C_STATUS_ARBITRATIONLOST;
	}
	else if(status &1 << 0))
	{
		return I2C_STATUS_NAK;
	}
	else
	{
		;
	}
	return I2C_STATUS_OK;
}
/*
*@description        : 主机向从机发送数据
*@Paaram - base      : 需要初始化的I2C
*@Paaram - buf       : 发送数据地址
*@Paaram - size      : 发送数据字节长度
*@return             :0 正常 其他: 异常
*/
void i2c_master_write(I2C_Type * base, unsigned char * buf, unsigned int size)
{
	// 等待总线数据完成
	while((base->I2SR & (1 << 7)) == 0);

	// 清除标志位
	base->I2SR &= ~(1 << 1);

	// 设置为发送模式
	base->I2CR |= (1 << 4);
	while(size--)
	{
		// 将发送数据写入寄存器I2DR
		base->I2DR = *buf++;
		// 等待总线数据完成
		while((base->I2SR & (1 << 1)) == 0);
		// 	清除标志位
		base->I2SR &= ~(1 << 1)// 检查I2C从机应答ACK
		if(0 != i2c_cheack_and_clean_error(base, base->I2SR))
		{
			break;
		}
	}
	// 	清除标志位
	base->I2SR &= ~(1 << 1);
	// 发送停止信号
	i2C_master_stop(base)}
/*
*@description        : 主机从从机读取数据
*@Paaram - base      : 需要初始化的I2C
*@Paaram - buf       : 数据缓冲区
*@Paaram - size      : 读取数据字节长度
*@return             :0 正常 其他: 异常
*/
void i2c_master_read(I2C_Type * base, unsigned char * buf, unsigned int size)
{
	volatile unsigned char dummy = 0;
	dummy++;
	// 等待总线数据完成
	while((base->I2SR & (1 << 7)) == 0);
	// 清除中断标志位
	base->I2SR &= ~(1 << 1);
	
	/*
	设置工作模式为读取模式,且无ACK
	bit4: 0读取模式
	bit3: 0 发送ACK
	*/
	base->I2CR &= ~((1 << 3) | (1 << 4));

	if(1 == size)
	{
		// 发送 NO ACK
		base->I2CR |= (1 << 3);
	}
	dummy = base->I2DR; // 假读
	
	while(size--)
	{
		// 等待总线数据传输完成
		while((base->I2SR & (1 << 1)) == 0);
		// 清除标志位
		base->I2SR &= (1 << 1);
		if(0 == size)
		{
			// 发送停止信号
			i2C_master_stop(base)}
		if(1 == size)
		{
			// 发送NO ACK
			base->I2SR |= (1 << 3);
		}
		*buf++ = base->I2DR;// 读取数据
	}

}
/*
*@description           : I2C数据传输,包括读和写
*@Paaram - i2c_transfer : I2C传输结构
*@return                :0 正常 其他: 异常
*/
void i2c_master_read(I2C_Type * base, struct i2c_transfer *xfer)
{
	unsigned char ret = 0;
	enum i2c_direction direction;

	/*
	清除标志位,配置寄存器I2SR
	bit4: 0 清除仲裁丢失标志位
	bit1: 0 清除中断标志位
	*/
	base->I2SR &= ~((1 << 4) | (1 << 1));

	// 等待总数数据传输完成
	while((base->I2SR & (1 << 7)) == 0);
	
	// 判断是读数据还是写数据
	if(kI2C_Read == xfer->direction && xfer->datasize > 0)
	{
		direction = kI2C_Write;// 设置位写
	}
	
	// 发送起始信号
	ret = i2C_master_start(base, xfer->slaveAddress, direction);
	if(0 != ret)
	{
		return ret;
	}
	
	// 等待总线数据传输完成
	while((base->I2SR & (1 << 1)) == 0);

	// 检查传输过程中状态寄存器
   ret = i2c_cheack_and_clean_error(base, base->I2SR);
   if(0 != ret)
   {
		// 发送停止信号
		i2C_master_stop(base)return ret;
   }

	// 发送寄存器地址
	if(xfer->subaddressSize > 0)
	{
		// 进入寄存器地址发送循环
		do{
			// 清除中断标志
			base->I2SR &= (1 << 1);
			xfer->subaddressSize -;// 按字节发送,每发送一次减一
			// 将寄存器地址写入数据寄存器,按照从高字节到低字节发送
			base->I2DR = (xfer->subadderss >> (8 * subaddressSize));
			
			// 等待总数数据传输完成
			while((base->I2SR & (1 << 1)) == 0);
			// 检查传输过程中状态寄存器
			ret = i2c_cheack_and_clean_error(base, base->I2SR);
			if(0 == ret)
			{
				// 发送停止信号
				i2C_master_stop(base)return ret;
			}
		}while(xfer->subaddressSize > 0 && I2C_STATUS_OK == ret);
	}
	// 发送数据
	if(kI2C_Write == xfer->direction)
	{
		i2c_master_write(base, xfer->data, xfer->datasize);
	}
	// 读取数据
	if(kI2C_Read == xfer->direction)
	{
		i2c_master_read(base, xfer->data, xfer->datasize);
	}
	return 0;
}

  • 10
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值