I2C实验内容:
学习如何使用 I.MX6U 的 I2C 接口来驱动 AP3216C,读取 AP3216C 的传感器数据。
I2C实验中,I2C主控制器的部分代码实现如下:
IMX6ULL裸机篇之I2C实验主控代码说明一_凌雪舞的博客-CSDN博客
本文主要介绍 I2C写数据与读数据实现。
I2C读写数据时序图:
I2C写一个字节数据的时序图,如下:
I2C读一个字节数据的时序图,如下:
本文学习 I2C的三个接口:
(1) 写数据函数
其中数据写入之前,有清除中断标志位 I2C1->I2SR [IIF] 位的操作。
数据传输完成后,也有以 等待 I2C1->I2SR [IIF] 位作为数据传输完成的判断操作。
以上代码实现的两个部分,都是以 恩智浦提供的 SDK代码关于 i2c的写数据函数作为依据。即参考自 “ I.MX6U参考资料\I.MX6ULL SDK包\devices\MCIMX6Y2\drivers ” 目录下 fsl_i2c.c 文件:I2C_MasterWriteBlocking() 函数。
(2) 读数据函数
读数据函数实现相对写数据接口比较复杂,从写数据时序图与读数据时序图比较可以知道。
(3) I2C数据传输接口:包括数据发送与接收
这个函数调用 以上的两个写数据与读数据函数,实现 I2C数据的发送与接收。
这个接口是从恩智浦提供关于 i2c代码的 SDK中拷贝所得。
I2C主控制器代码在 /bsp/i2c 目录下,文件分别为 bsp_i2c.h 与 bsp_i2c.c 文件。
bsp_i2c.h文件如下:
#ifndef _BSP_I2C_H_
#define _BSP_I2C_H_
#include "imx6ull.h"
//定义相关宏
#define I2C_STATUS_OK (0)
#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)
//传输方向结构体
enum i2c_direction {
kI2C_Write = 0x00, //主机向从机写数据
kI2C_Read = 0x01,
};
//主机传输结构体
struct i2c_transfer
{
unsigned char slaveAddress; /* 7位从机地址 */
enum i2c_direction direction; /* 传输方向 */
unsigned int subaddress; /* 寄存器地址 */
unsigned char subaddressSize; /* 寄存器地址长度 */
unsigned char *volatile data; /* 数据缓冲区 */
volatile unsigned int dataSize; /* 数据缓冲区长度 */
};
void i2c_init(I2C_Type* base);
unsigned char i2c_master_start(I2C_Type* base, unsigned char slave_addr, enum i2c_direction data_direction);
unsigned char i2c_master_stop(I2C_Type* base);
unsigned char i2c_master_repeated_start(I2C_Type* base, unsigned char slave_addr, enum i2c_direction data_direct);
unsigned char i2c_check_and_clear_error(I2C_Type* base, unsigned int isr_status);
void i2c_master_write(I2C_Type* base, unsigned char* buffer, unsigned int size);
void i2c_master_read(I2C_Type* base, unsigned char* buffer, unsigned int size);
unsigned char i2c_master_transfer(I2C_Type *base, struct i2c_transfer *xfer);
#endif
bsp_i2c.c文件新增的函数如下:
/*I2C写数据*/
void i2c_master_write(I2C_Type* base, unsigned char* buffer, unsigned int size)
{
//等待上一次数据传输完成
while(!(base->I2SR &(1 << 7)));
//清除标志位 I2SR[IIF]
base->I2SR &= ~(1 << 1);
//设置发送模式
base->I2CR |= (1 << 4);
//发送数据
while(size--)
{
base->I2DR = *buffer++;
while(!(base->I2SR &( 1 << 1))); //等待传输完成
base->I2SR &= ~(1 << 1); //清除标志位 I2SR[IIF]
/* 检查ACK */
if(i2c_check_and_clear_error(base, base->I2SR))
break;
}
base->I2SR &= ~(1 << 1); //清除标志位 I2SR[IIF]
i2c_master_stop(base);
}
/*I2C读数据*/
void i2c_master_read(I2C_Type* base, unsigned char* buffer, unsigned int size)
{
//
volatile uint8_t dummy = 0; //假读,为了防止编译报错
dummy++;
//等待上一次数据传输完成
while(!(base->I2SR & (1 << 7)));
//清除标志位I2SR[IIF]
base->I2SR &= ~(1 << 1);
//设置接收模式
base->I2CR &= ~(1 << 4 | 1<< 3);
if(size == 1) //如果只接收一个字节数据,则发送 NO-ACK
{
base->I2CR |= (1 << 3);
}
/*假读*/
dummy = base->I2DR;
while(size--)
{
//等待数据传输完成
while(!(base->I2SR & (1 << 1)));
base->I2SR &= ~(1 << 1); //清除标志位I2SR[IIF]
if(size == 0) //数据发送完成,则发送stop信号
{
i2c_master_stop(base);
}
else if(size == 1) //倒数第二个字节的数据,则发送 NO-ACK
{
base->I2CR |= 1 << 3;
}
*buffer++ = base->I2DR;
}
}
/* I2C数据传输,包括读和写 */
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--; /* 地址长度减一 */
//向I2DR寄存器写入子地址
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;
}
本文学习了 I2C主控制器端代码的实现,而 I2C实验还没有全部实现。
下一篇学习 从设备 AP3216C传感器设备的 I2C代码编写。