I2C实验

news2024/9/28 15:32:20

目录

一、I2C简介

二、硬件原理

1、看原理图,找到I2C

2、查看使用设备

3、查看使用的IO

 4、查数据手册看复用位

三、查看寄存器

1、I2C Address Register (I2Cx_IADR)

2、I2C Frequency Divider Register (I2Cx_IFDR)

3、I2C Control Register (I2Cx_I2CR)

4、I2C Status Register (I2Cx_I2SR)

5、I2C Data I/O Register (I2Cx_I2DR)

四、I2C协议

1、起始位start

 2、停止位stop

 3、数据传输

 4、应答信号ack

5、 I2C 写时序

6、 I2C 读时序

7、start信号产生

8、stop信号产生

五、AP3216C

1、查数据手册看设备地址

2、查看寄存器

六、代码编写

1、创建工作目录

2、编写bsp_i2c.h

3、编写bsp_i2c.c

4、编写ap3216c.h

5、ap3216c.c


一、I2C简介

I2C 是很常见的一种总线协议,  I2C 使用两条线在主控制器和从机之间进行数据通信。一条是 SCL(串行时钟线),另外一条是 SDA(串行数据线),这两条数据线需要接上拉电阻,总线空闲的时候 SCL 和 SDA 处于高电平。I.MX6U 的 I2C 支持两种模式:标准模式和快速模式,标准模式下 I2C 数据传输速率最高是 100Kbits/s,在快速模式下数据传输速率最高为 400Kbits/s。I2C总线支持多从机,通过从机地址来区分访问对应的从机。

二、硬件原理

1、看原理图,找到I2C

 这里使用I2C1,搜索它接到哪里

2、查看使用设备

 AP3216C,这是一个IIC接口的器件,这是一个环境光传感器,AP3216C连接到了I2C1上

3、查看使用的IO

       I2C1_SCL: 使用的是UART4_TXD这个IO

       I2C1_SDA: 使用的是UART4_RXD这个IO

 4、查数据手册看复用位

UART4_TXD 复用位ALT2

 UART4_RXD复用为ALT2

三、查看寄存器

I2C寄存器如下

 可以看到有五个,下面逐一看一下各个寄存器

1、I2C Address Register (I2Cx_IADR)

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

2、I2C Frequency Divider Register (I2Cx_IFDR)

 这个是 I2C 的分频寄存器,寄存器 I2Cx_IFDR 也只有 IC(bit5:0)这个位,用来设置 I2C 的波特率, I2C 的时钟源可以选择 IPG_CLK_ROOT=66MHz,通过设置 IC 位既可以得到想要的 I2C 波特率,IC 位可选的设置如下

 假如现在需要100kbit的速率,那么时钟源66000000/100000=660。经过查找上图IC位设置位0X38或0X15的时候,为640分频,66000000/640=103.125Kbit,较为接近

3、I2C Control Register (I2Cx_I2CR)

 这个是 I2C 控制寄存器,寄存器 I2Cx_I2CR 的各位含义如下:
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 的话产生一个重新开始信号。

4、I2C Status Register (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 信号。

5、I2C Data I/O Register (I2Cx_I2DR)

 这是 I2C 的数据寄存器,此寄存器只有低 8 位有效,当要发送数据的时候将要发送的数据写入到此寄存器,如果要接收数据的话直接读取此寄存器即可得到接收到的数据

四、I2C协议

I2C 总线工作是按照一定的协议来运行的

1、起始位start

I2C 通信起始标志,通过这个起始位就可以告诉 I2C 从机,“我”要开始进行 I2C 通信了。在 SCL 为高电平的时候, SDA 出现下降沿就表示为起始位

 2、停止位stop

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

 3、数据传输

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

 4、应答信号ack

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

5、 I2C 写时序

主机通过 I2C 总线与从机之间进行通信不外乎两个操作:写和读, I2C 总线单字节写时序如下

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

6、 I2C 读时序

I2C 单字节读时序比写时序要复杂一点,读时序分为 4 大步,第一步是发送设备地址,第二步是发送要读取的寄存器地址,第三步重新发送设备地址,最后一步就是 I2C 从器件输出要读取的寄存器值,具体如下
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 通信。

7、start信号产生

 初始化过程完成后,串行数据可以通过选择主传输模式。在多主站总线系统上,繁忙总线(I2C_I2SR[IBB]) 必须进行判断确定串行总线是否可用。如果总线是free(IBB = 0)空闲,可以发送开始信号和第一个字节(从地址),不需要控制某个寄存器,数据写入数据寄存器包括所需从机的地址和LSB指示传输方向,start信号就会同步产生

8、stop信号产生

 当主设备发出停止信号时,数据传输结束,这可能在发送所有数据后发生。要使主接收器终止数据传输,它必须通过以下方式通知从发送器不确认最后一个数据字节。这是通过设置传输确认来完成的位 (I2C_I2CR[TXAK]),然后再读取倒数第二个字节(也就是传输的第五个数据)。在读取最后一个字节之前,必须生成停止信号。

五、AP3216C

 I2C1 连接了一个三合一环境传感器: AP3216C, AP3216C是由敦南科技推出的一款传感器,其支持环境光强度(ALS)、接近距离(PS)和红外线强度(IR)这三个环境参数检测。该芯片可以通过 IIC 接口与主控制相连,并且支持中断。

1、查数据手册看设备地址

打开 AP3216C数据手册

 可以看到AP3216C的从机地址位0X1E

2、查看寄存器

AP3216C 内部也有一些寄存器,通过这些寄存器可以配置 AP3216C 的工作模式,并且读取相应的数据,部分寄存器如下

 这里简单看几个,比如0X00是系统配置寄存器,bit2:0设置AP3216C使用那些传感器,这里需要设置位011,也就是0x3,表示开始ALS+PS+IR;

0X0A是IR Ddata low(IR 低位数据)。Bit7为0的时候表示IR和PS数据有效,为1的时候IR和PS数据无效。Bit1:0是IR的低2位数据;

0X0B是IR Data high(IR 高位数据),big7:0是高字节。与0X0A一起组成10bit的数据"IR byte of ADC output";

 0X0C和0X0D分别为ALS的低8位和高8位“ALSbyte of ADC output”;

0X0E的bit3:0是低4位数据,0X0F的bit5:0是高6位数据。加起来就是10位PS byte of ADC output

到此基本了解完I2C及AP3216C,下面开始编程

六、代码编写

1、创建工作目录

分别是i2c和ap3216c

2、编写bsp_i2c.h

#ifndef __BSP_I2C_H
#define __BSP_I2C_H

#include "imx6ul.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 = 0x0, /* 主机向从机写数据 */
    kI2C_Read = 0x1, /* 主机从从机读数据 */
};

/*主机传输结构体*/
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 address,
                                enum i2c_direction direction);

unsigned char i2c_master_stop(I2C_Type *base);

unsigned char i2c_master_repeated_start(I2C_Type *base,
                                        unsigned char address,
                                        enum i2c_direction direction);

unsigned char i2c_check_and_clear_error(I2C_Type *base , unsigned int status);

void i2c_master_write(I2C_Type *base , 
                       const unsigned char *buf,
                       unsigned int size);

void i2c_master_read(I2C_Type *base , 
                     unsigned char *buf,
                     unsigned int size);

unsigned char i2c_master_transfer(I2C_Type *base, struct i2c_transfer *xfer);

#endif

3、编写bsp_i2c.c

#include "bsp_i2c.h"

/*初始化I2C
 * @description : 初始化 I2C,波特率 100KHZ
 * @param – base : 要初始化的 IIC 设置
 * @return : 无
 */
void i2c_init(I2C_Type *base)
{/* 要访问I2C的寄存器,首先需要先关闭I2C */
    base->I2CR &= ~(1 << 7); 
    /* 设置波特率为100K
     * I2C的时钟源来源于IPG_CLK_ROOT=66Mhz
 	 * IC2 时钟 = PERCLK_ROOT/dividison(IFDR寄存器)
	 * 设置I2C的波特率为100K, 因此当分频值=66000000/100000=660.
	 * 在IFDR寄存器给出的表里面查找,没有660这个值,但是有640,因此就用640,
	 * 即寄存器IFDR的IC位设置为0X15
	 */
    base->IFDR = 0X15;
    /* 使能I2C */
    base->I2CR |= (1 << 7); 
}

/*start信号产生和从机地址发送*/
unsigned char i2c_master_start(I2C_Type *base,
                                unsigned char address,
                                enum i2c_direction direction)
{
    if(base->I2SR & (1 << 5)) /*判断I2C忙*/
        return 1;/*0代表正常,其他值均为不正常*/
    
    /*设置主机发送模式
     * 设置寄存器 I2CR
     * bit[5]: 1 主模式
     * bit[4]: 1 发送
     */
    base->I2CR |= (1 << 5) | (1 << 4); 

    /*产生start信号
     *寄存器 I2DR, bit[7:0]因为高七位为地址,最后一位为读写位,所以要左移1
     *判断1或0,1为读,0为写
    */
    base->I2DR = ((unsigned int)address << 1) | ((direction == kI2C_Read) ? 1 : 0);
    return 0;
}   

/*stop信号
* @description : 停止信号
* @param - base : 要使用的 IIC
* @param : 无
* @return : 状态结果
*/
unsigned char i2c_master_stop(I2C_Type *base)
{
    unsigned short timeout = 0xffff;
    /*清除I2CR的bit5:3*/
    base->I2CR &= ~((1 << 5) | (1 <<4) | (1 << 3));
    /*等待I2C忙结束*/
    while((base->I2SR & (1<<5))){
        timeout --;
        if(timeout ==0)/*超时跳出*/
            return I2C_STATUS_TIMEOUT;
    }
    return I2C_STATUS_OK;
}
/*repeaned start信号
 * @description : 发送重新开始信号
 * @param - base : 要使用的 IIC
 * @param - addrss : 设备地址
 * @param - direction : 方向
 * @return : I2C_STATUS_OK 正常 其他值 出错
 */
unsigned char i2c_master_repeated_start(I2C_Type *base,
                                        unsigned char address,
                                        enum i2c_direction direction)
{
    /* I2C忙并且工作在从模式,跳出
     * base->I2CR为0在从模式
     */
    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 I2C_STATUS_OK;
}

/*错误检查和清除函数
* @description : 检查并清除错误
* @param - base : 要使用的 IIC
* @param - status : 状态
* @return : 状态结果
*/
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;
}

/* @description : 发送数据
 * @param - base : 要使用的 IIC
 * @param - buf : 要发送的数据
 * @param - size : 要发送的数据大小
 * @param - flags : 标志
 * @return : 无
 */
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-- )
    {
        /* 将buf中的数据写入到I2DR寄存器 */
        base->I2DR = *buf++;
        /* 等待传输完成 */	
        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);
}
/*
 * @description : 读取数据
 * @param - base : 要使用的 IIC
 * @param - buf : 读取到数据
 * @param - size : 要读取的数据大小
 * @return : 无
 */
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));
    /* 如果只接收一个字节数据的话发送 NACK 信号 */
    if(size == 1)
        base->I2CR |= (1 << 3);/*nack*/
    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;
    }

}
/*
 * @description	: I2C数据传输,包括读和写
 * @param - base: 要使用的IIC
 * @param - xfer: 传输结构体
 * @return 		: 传输结果,0 成功,其他值 失败;
 */
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)); //向I2DR寄存器写入子地址
  
			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 了

4、编写ap3216c.h

#ifndef __BSP_AP3216C_H
#define __BSP_AP3216C_H

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

#define AP3216C_ADDR    	0X1E	/* AP3216C器件地址 */

/* AP3316C寄存器 */
#define AP3216C_SYSTEMCONG	0x00	/* 配置寄存器*/
#define AP3216C_INTSTATUS	0X01	/* 中断状态寄存器*/
#define AP3216C_INTCLEAR	0X02	/* 中断清除寄存器*/
#define AP3216C_IRDATALOW	0x0A	/* IR数据低字节*/
#define AP3216C_IRDATAHIGH	0x0B	/* IR数据高字节*/
#define AP3216C_ALSDATALOW	0x0C	/* ALS数据低字节*/
#define AP3216C_ALSDATAHIGH	0X0D	/* ALS数据高字节*/
#define AP3216C_PSDATALOW	0X0E	/* PS数据低字节*/
#define AP3216C_PSDATAHIGH	0X0F	/* PS数据高字节 */
/* 函数声明 */
unsigned char ap3216c_init(void);
unsigned char ap3216c_readonebyte(unsigned char addr,
                                    unsigned char reg);
unsigned char ap3216c_writeonebyte(unsigned char addr,
                                    unsigned char reg,
                                    unsigned char data);
void ap3216c_readdata(unsigned short *ir ,
                        unsigned short *ps,
                        unsigned short *als);
#endif

5、ap3216c.c

#include "bsp_ap3216c.h"

/*
 * @description	: 初始化AP3216C
 * @param		: 无
 * @return 		: 0 成功,其他值 错误代码
 */
unsigned char ap3216c_init(void)
{
	unsigned char data = 0;

	/* 1、IO初始化,配置I2C IO属性	
     * I2C1_SCL -> UART4_TXD
     * I2C1_SDA -> UART4_RXD
     */
	IOMUXC_SetPinMux(IOMUXC_UART4_TX_DATA_I2C1_SCL, 1);
	IOMUXC_SetPinMux(IOMUXC_UART4_RX_DATA_I2C1_SDA, 1);

	/* 
	 *bit 16:0 HYS关闭
	 *bit [15:14]: 1 默认47K上拉
	 *bit [13]: 1 pull功能
	 *bit [12]: 1 pull/keeper使能 
	 *bit [11]: 0 关闭开路输出
	 *bit [7:6]: 10 速度100Mhz
	 *bit [5:3]: 110 驱动能力为R0/6
	 *bit [0]: 1 高转换率
	 */
	IOMUXC_SetPinConfig(IOMUXC_UART4_TX_DATA_I2C1_SCL, 0x70B0);
	IOMUXC_SetPinConfig(IOMUXC_UART4_RX_DATA_I2C1_SDA, 0X70B0);

	i2c_init(I2C1);		/* 初始化I2C1 */

	/* 2、初始化AP3216C */
	ap3216c_writeonebyte(AP3216C_ADDR, AP3216C_SYSTEMCONG, 0X04);	/* 复位AP3216C 			*/
	delayms(50);													/* AP33216C复位至少10ms */
	ap3216c_writeonebyte(AP3216C_ADDR, AP3216C_SYSTEMCONG, 0X03);	/* 开启ALS、PS+IR 		   	*/
	data = ap3216c_readonebyte(AP3216C_ADDR, AP3216C_SYSTEMCONG);	/* 读取刚刚写进去的0X03 */
	if(data == 0X03)
		return 0;	/* AP3216C正常 	*/
	else 
		return 1;	/* AP3216C失败 	*/
}

/*
 * @description	: 向AP3216C写入数据
 * @param - addr: 设备地址
 * @param - reg : 要写入的寄存器
 * @param - data: 要写入的数据
 * @return 		: 操作结果
 */
unsigned char ap3216c_writeonebyte(unsigned char addr,unsigned char reg, unsigned char data)
{
    unsigned char status=0;
    unsigned char writedata=data;
    struct i2c_transfer masterXfer;
	
    /* 配置I2C xfer结构体 */
   	masterXfer.slaveAddress = addr; 			/* 设备地址 				*/
    masterXfer.direction = kI2C_Write;			/* 写入数据 				*/
    masterXfer.subaddress = reg;				/* 要写入的寄存器地址 			*/
    masterXfer.subaddressSize = 1;				/* 地址长度一个字节 			*/
    masterXfer.data = &writedata;				/* 要写入的数据 				*/
    masterXfer.dataSize = 1;  					/* 写入数据长度1个字节			*/

    if(i2c_master_transfer(I2C1, &masterXfer))
        status=1;
        
    return status;
}

/*
 * @description	: 从AP3216C读取一个字节的数据
 * @param - addr: 设备地址
 * @param - reg : 要读取的寄存器
 * @return 		: 读取到的数据。
 */
unsigned char ap3216c_readonebyte(unsigned char addr,unsigned char reg)
{
	unsigned char val=0;
	
	struct i2c_transfer masterXfer;	
	masterXfer.slaveAddress = addr;				/* 设备地址 				*/
    masterXfer.direction = kI2C_Read;			/* 读取数据 				*/
    masterXfer.subaddress = reg;				/* 要读取的寄存器地址 			*/
    masterXfer.subaddressSize = 1;				/* 地址长度一个字节 			*/
    masterXfer.data = &val;						/* 接收数据缓冲区 				*/
    masterXfer.dataSize = 1;					/* 读取数据长度1个字节			*/
	i2c_master_transfer(I2C1, &masterXfer);

	return val;
}

/*
 * @description	: 读取AP3216C的数据,读取原始数据,包括ALS,PS和IR, 注意!
 *				: 如果同时打开ALS,IR+PS的话两次数据读取的时间间隔要大于112.5ms
 * @param - ir	: ir数据
 * @param - ps 	: ps数据
 * @param - ps 	: als数据 
 * @return 		: 无。
 */
void ap3216c_readdata(unsigned short *ir, unsigned short *ps, unsigned short *als)
{
    unsigned char buf[6];
    unsigned char i;

	/* 循环读取所有传感器数据 */
    for(i = 0; i < 6; i++)	
    {
        buf[i] = ap3216c_readonebyte(AP3216C_ADDR, AP3216C_IRDATALOW + i);	
    }
	
    if(buf[0] & 0X80) 	/* IR_OF位为1,则数据无效 */
		*ir = 0;					
	else 				/* 读取IR传感器的数据   		*/
		*ir = ((unsigned short)buf[1] << 2) | (buf[0] & 0X03); 			
	
	*als = ((unsigned short)buf[3] << 8) | buf[2];	/* 读取ALS传感器的数据 			 */  
	
    if(buf[4] & 0x40)	/* IR_OF位为1,则数据无效 			*/
		*ps = 0;    													
	else 				/* 读取PS传感器的数据    */
		*ps = ((unsigned short)(buf[5] & 0X3F) << 4) | (buf[4] & 0X0F); 	
}

编译烧到sd卡上,sd卡和屏幕装上开发板就能看到屏幕上有关ap3216c的数据

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/366581.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【C++】哈希表

1. unordered系列关联式容器 在C98中&#xff0c;STL提供了底层为红黑树结构的一系列关联式容器&#xff0c;在查询时效率可达到 &#xff0c;即最差情况下需要比较红黑树的高度次&#xff0c;当树中的节点非常多时&#xff0c;查询效率也不理想。最好的查询是&#xff0c;进行…

TCP/IP网络协议介绍及原理分析

一.应用层协议对于应用层而言&#xff0c;协议是开发者自己进行定义的&#xff0c;开发者根据自定义的格式规范对数据进行编码和解析。但是从原理上进行分析&#xff0c;其核心主要包括两点内容&#xff1a;①确定客户端和服务端交互的内容&#xff08;协议的内容&#xff09;②…

记一次docker虚拟机横向移动渗透测试

本次渗透在几个docker虚拟机间多次横向移动&#xff0c;最终找到了一个可以进行docker逃逸的出口&#xff0c;拿下服务器。渗透过程曲折但充满了乐趣&#xff0c;入口是172.17.0.6的docker虚拟机&#xff0c;然后一路横向移动&#xff0c;最终在172.17.0.2出实现了docker逃逸&a…

【免费教程】地下水环境监测技术规范HJ/T164-2020解读使用教程

地下水环境监测技术规范依据《中华人民共和国环境保护法》第十一条“国务院环境保护行政主管部门建立监测制度、制订监测规范”和《中华人民共和国水污染防治法》的要求&#xff0c;积极开展地下水环境监测&#xff0c;掌握地下水环境质量&#xff0c;保护地下水水质&#xff0…

常青科技冲刺A股上市:研发费用率较低,关联方曾拆出资金达1亿元

近日&#xff0c;江苏常青树新材料科技股份有限公司&#xff08;下称“常青科技”或“常青树科技”&#xff09;递交招股书&#xff0c;准备在上海证券交易所主板上市。本次冲刺上市&#xff0c;常青科技计划募资8.50亿元&#xff0c;光大证券为其保荐机构。 据招股书介绍&…

我的 System Verilog 学习记录(4)

引言 本文简单介绍 System Verilog 语言的 数据类型。 前文链接&#xff1a; 我的 System Verilog 学习记录&#xff08;1&#xff09; 我的 System Verilog 学习记录&#xff08;2&#xff09; 我的 System Verilog 学习记录&#xff08;3&#xff09; 数据类型简介 Sys…

Linux:共享内存api使用

代码&#xff1a; #include <stdlib.h> #include <stdio.h> #include <unistd.h> #include <sys/types.h> #include <sys/stat.h> #include <string.h> #include <arpa/inet.h> #include <sys/un.h> #include <sys/ipc.h…

Codeforces Round #849 (Div. 4)(E~G)

A~D比较简单就不写了&#xff0c;哎嘿E. Negatives and Positives给出一个数组a&#xff0c;可以对数组进行若干次操作&#xff0c;每次操作可以将相邻的两个数换为它们的相反数&#xff0c;求进行若干次操作之后能得到数组和的最大值是多少。思路&#xff1a;最大的肯定是把负…

VSCode+Qt+MinGW开发环境搭建

VSCodeQtMinGW开发环境搭建 概述 VSCode扩展性很强&#xff0c;插件机制让其具备不断演进的潜力&#xff0c;适合作为稳定的开发工具。 VSCodeQt开发环境的搭建需要依赖于以下工具&#xff1a; VSCode、Qt&#xff0c;其中Qt需要安装MinGW编译工具&#xff1b;VSCode插件&a…

常年霸榜TK彩妆类目,看Focallure菲鹿儿如何玩转出海市场

据市场调研机构弗若斯特沙利文数据报告&#xff0c;2017年至2021年&#xff0c;中国化妆品市场规模由6305亿元增长至9468亿元&#xff0c;年均复合增长率为10.7%&#xff0c;报告预计2023年中国化妆品市场规模将达11414亿元&#xff0c;今后几年的增速将逐渐放缓。随着国内市场…

LeetCode刷题系列 -- 112. 路径总和

给你二叉树的根节点 root 和一个表示目标和的整数 targetSum 。判断该树中是否存在 根节点到叶子节点 的路径&#xff0c;这条路径上所有节点值相加等于目标和 targetSum 。如果存在&#xff0c;返回 true &#xff1b;否则&#xff0c;返回 false 。叶子节点 是指没有子节点的…

外置的媒体查询,对性能又一次的优化提升

通常情况下我们写媒体查询都是写在一个样式文件中&#xff0c;对于浏览器加载的时候&#xff0c;会解析到最后一行样式时才会渲染页面&#xff0c;这样就会造成页面的白屏时间过长。 但是通常情况下大量的媒体查询样式都是无用的&#xff0c;现在浏览器允许我们在引用样式文件…

SpringBoot优雅地处理全局异常,返回前端

笔者这边提供了两种处理全局异常的方式。这两种方式各有千秋&#xff0c;都很优雅。至于伙伴们想用哪种方式&#xff0c;那就仁者见仁&#xff0c;智者见智了。0、公共部分在介绍异常处理方式前&#xff0c;先定义一些公共的类。这些类在两种处理方式中都会用到。【自定义业务异…

jupyter的安装步骤

1.安装python文件 首先去官网python去下载python的安装包&#xff0c;点击donwload,选择合适的系统。这里我是windown系统&#xff0c;点击进去&#xff0c;如图找到有installer的去下载。不建议下载最新版本的&#xff0c;会有兼容问题。 2.安装python 点击第二个选项是自己配…

深度学习如何训练出好的模型

深度学习在近年来得到了广泛的应用&#xff0c;从图像识别、语音识别到自然语言处理等领域都有了卓越的表现。但是&#xff0c;要训练出一个高效准确的深度学习模型并不容易。不仅需要有高质量的数据、合适的模型和足够的计算资源&#xff0c;还需要根据任务和数据的特点进行合…

【2023/图对比/增强】MA-GCL: Model Augmentation Tricks for Graph Contrastive Learning

如果觉得我的分享有一定帮助&#xff0c;欢迎关注我的微信公众号 “码农的科研笔记”&#xff0c;了解更多我的算法和代码学习总结记录。或者点击链接扫码关注【2023/图对比/增强】MA-GCL: Model Augmentation Tricks for Graph Contrastive Learning 【2023/图对比/增强】MA-…

备份策略从“3-2-1”到“4-3-2-1”

在数据存储备份领域&#xff0c;说起“3-2-1”备份策略真是无人不知、如雷贯耳&#xff01;笔者也经常把“3-2-1”备份策略挂在嘴边&#xff0c;那简直就是确保数据安全的圭臬&#xff01;但是&#xff0c;最近有一位读者问我&#xff1a;“3-2-1”备份策略的出处在哪里&#x…

MySQL - 多表查询

目录1. 多表查询示例2. 多表查询分类2.1 等/非等值连接2.1.1 等值连接2.1.2非等值连接2.2 自然/非自然连接2.3 内/外连接2.3.1 内连接2.3.2 外连接3.UNION的使用3.1 合并查询结果3.1.1 UNION操作符3.1.2 UNION ALL操作符4. 7种JOIN操作多表查询&#xff0c;也称为关联查询&…

LocalDateTime使用

开发中常常需要用到时间&#xff0c;随着jdk的发展&#xff0c;对于时间的操作已经摒弃了之前的Date等方法&#xff0c;而是采用了LocalDateTime方法&#xff0c;因为LocalDateTime是线程安全的。 下面我们来介绍一下LocalDateTime的使用。 时间转换 将字符串转换为时间格式…

五分钟搞懂POM设计模式

今天&#xff0c;我们来聊聊Web UI自动化测试中的POM设计模式。 为什么要用POM设计模式 前期&#xff0c;我们学会了使用PythonSelenium编写Web UI自动化测试线性脚本 线性脚本&#xff08;以快递100网站登录举栗&#xff09;&#xff1a; import timefrom selenium import …