IMX6ULL裸机篇之I2C实验主控代码说明二

一.  I2C实验

I2C实验内容:

学习如何使用 I.MX6U 的 I2C 接口来驱动 AP3216C,读取 AP3216C 的传感器数据。

I2C实验中,I2C主控制器的部分代码实现如下:

IMX6ULL裸机篇之I2C实验主控代码说明一_凌雪舞的博客-CSDN博客

本文主要介绍 I2C写数据与读数据实现。

I2C读写数据时序图:

I2C写一个字节数据的时序图,如下:

IMX6ULL裸机篇之I2C实验主控代码说明二_第1张图片

I2C读一个字节数据的时序图,如下:

IMX6ULL裸机篇之I2C实验主控代码说明二_第2张图片

二.   I2C主控读写时序

1.   读数据与写数据

本文学习 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中拷贝所得。

2.  读 / 写数据代码

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代码编写。

你可能感兴趣的:(嵌入式C开发,裸机开发,arm开发,linux,c语言)