当前位置: 首页 > news >正文

imx6ull-裸机学习实验16——I2C 实验

目录

前言

I2C简介

基本特性​​

I2C 协议

起始位

停止位

数据传输

应答信号

I2C 写时序

I2C 读时序

I.MX6U I2C

简介

寄存器

地址寄存器I2Cx_IADR(x=1~4)

分频寄存器I2Cx_IFDR

控制寄存器I2Cx_I2CR

状态寄存器I2Cx_I2SR

数据寄存器I2Cx_I2DR

AP3216C 简介

硬件原理图

实验程序编写

i2c.h

i2c.c

ap3216c.h

ap3216c.c

main.c


前言

I2C 是最常用的通信接口,众多的传感器都会提供 I2C 接口来和主控相连,比如陀螺仪、加速度计、触摸屏等等。

I.MX6U 有 4 个 I2C 接口,其中I2C1 接口连接了一个距离传感器 AP3216C,本讲实验我们就来学习如何使用 I.MX6U 的 I2C 接口来驱动 AP3216C,读取AP3216C 的传感器数据。

I2C简介

I²C(Inter-Integrated Circuit)是一种​​同步、串行、半双工​​的通信协议,由飞利浦(现恩智浦)在1980年代设计,主要用于短距离板级设备间的通信。以下是其核心要点:

基本特性​

  • ​两根信号线​​:
    • ​SCL​​(Serial Clock):时钟线,由主设备控制。
    • ​SDA​​(Serial Data):双向数据线。
    • 这两条数据线需要接上拉电阻一般是 4.7,总线空闲的时候 SCL 和 SDA 处于高电平
  • ​多主多从架构​​:支持多个主设备(需仲裁)和多个从设备(通过地址区分)。

  • ​地址寻址​​:每个从设备有唯一地址(7位或10位),主设备通过地址发起通信。
  • ​速度模式​​:
    • 标准模式(100 kbps)
    • 快速模式(400 kbps)
    • 高速模式(3.4 Mbps)

​关键机制​

  • ​时钟同步​​:多个主设备可通过SCL线“线与”同步时钟。
  • ​仲裁​​:若两个主设备同时发送,SDA数据冲突时,高电平设备退出。
  • ​上拉电阻​​:SCL和SDA需接上拉电阻(通常4.7kΩ),确保空闲时为高电平。

I2C 协议

起始位

I2C 通信起始标志,通过这个起始位就可以告诉 I2C 从机,主机要开始进行 I2C 通信了。

SCL为高电平时,SDA由高→低跳变,标志通信开始,如图:

停止位

停止位就是停止 I2C 通信的标志位,和起始位的功能相反。

SCL为高电平时,SDA由低→高跳变,结束通信,如图:

数据传输

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

应答信号

当 I2C 主机发送完 8 位数据以后会将 SDA 设置为输入状态,等待 I2C 从机应答,也就是等到 I2C 从机告诉主机它接收到数据了。

从机通过将 SDA 拉低来表示发出应答信号,表示通信成功,否则表示通信失败。

  • ​ACK(确认)​​:接收方(从机或主机)成功接收数据后,拉低SDA表示应答。
  • ​NACK(非确认)​​:接收方未成功接收(或无需继续通信),保持SDA高电平。

​常见场景​​:

  • ​ACK​​:从机地址匹配、数据接收成功。
  • ​NACK​​:从机地址不匹配、接收方忙、主机终止读取(最后一次字节)。

I2C 写时序

I2C 总线单字节写时序如图:

步骤​​(以 7 位地址为例):

  1. ​​START 信号​​(SDA 由高→低,SCL 高电平)
  2. ​​发送从机地址 + 写位(0)​​(7位地址 + 1位方向,共 8 位)
  3. ​​从机应答 ACK​​(SDA 被从机拉低)
  4. ​​发送数据字节​​(8 位)
  5. ​​从机应答 ACK​​
  6. ​​重复 4~5(可选,连续写入多字节)​​
  7. ​​STOP 信号​​(SDA 由低→高,SCL 高电平)

I2C 读时序

读时序分为 4 大步:

  • 发送设备地址,
  • 发送要读取的寄存器地址,
  • 重新发送设备地址,
  • I2C 从器件输出要读取的寄存器值

I2C 多字节读写时序:

多字节读写时序和单字节的基本一致,只是在读写数据的时候可以连续发送多个自己的数据,

I.MX6U I2C

简介

I.MX6U 提供了 4 个 I2C 外设,通过这四个 I2C 外设即可完成与 I2C 从器件进行通信。

I.MX6U 的 I2C 支持两种模式:标准模式和快速模式,标准模式下 I2C 数据传输速率最高是 100Kbits/s,在快速模式下数据传输速率最高为 400Kbits/s。

寄存器

我们接下来看一下 I2C 的几个重要的寄存器:

  • 地址寄存器I2Cx_IADR(x=1~4)
  • 分频寄存器I2Cx_IFDR
  • 控制寄存器I2Cx_I2CR
  • 状态寄存器I2Cx_I2SR
  • 数据寄存器I2Cx_I2DR

地址寄存器I2Cx_IADR(x=1~4)

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

分频寄存器I2Cx_IFDR

I2C_IFDR提供了一个可编程的预分频器,用于配置时钟以选择比特率。寄存器不会通过软件重置而重置。

I2C时钟来自PERCLK_ROOT,后者从IPG_CLK_ROOT路由。I2C时钟频率可以通过使用以下公式轻松获得:

寄存器 I2Cx_IFDR 也只有 IC(bit5:0)这个位,用来设置 I2C 的波特率:

I2C 的时钟源可以选择 IPG_CLK_ROOT=66MHz,通过设置 IC 位来设置波特率。 IC 位可选的设置如图:

这就是IC 的所有可选值。如果I2C的时钟源为66MHz,我们要设置I2C的波特率为100KHz,那么IC就可以设置为0X15,也就是 640 分频。 66000000/640=103.125KHz≈100KHz。

控制寄存器I2Cx_I2CR

I2C_I2CR用于启用I2C和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

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

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

AP3216C 简介

AP3216C是一个三合一环境传感器,支持环境光强度(ALS)、接近距离(PS)和红外线强度(IR)这三个环境参数检测。

I.MX6U-ALPHA 开发板上通过 I2C1 连接的该传感器。

AP3216C 常被用于手机、平板、导航设备等,其内置的接近传感器可以用于检测是否有物体接近,比如手机上用来检测耳朵是否接触听筒,如果检测到的话就表示正在打电话,手机就会关闭手机屏幕以省电。也可以使用环境光传感器检测光照强度,可以实现自动背光亮度调节。

AP3216C 结构如图:

AP3216 的设备地址为 0X1E,我们等会写代码发送的从机地址就填这个。

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

0X00 这个寄存器是模式控制寄存器,用来设置 AP3216C 的工作模式,先设置为 0X04,也就是先软件复位一次 AP3216C,再设置为 0X03,也就是开启 ALS+PS+IR。

0X0A~0X0F 这 6 个寄存器就是数据寄存器,保存着 ALS、 PS 和 IR 这三个传感器获取到的数据值。如果同时打开 ALS、PS 和 IR 则读取间隔最少要 112.5ms(数据转换间隔)。

硬件原理图

本讲实验我们通过 I.MX6U 的 I2C1 来读取 AP3216C 内部的 ALS、 PS 和 IR 这三个传感器的值,然后使用串口打印出来。

配置步骤如下:

  1. 初始化相应的 IO:初始化 I2C1 相应的 IO,设置其复用功能,如果要使用 AP3216C 中断功能的话,还需要设置 AP3216C 的中断 IO。
  2. 初始化 I2C1:初始化 I2C1 接口,设置波特率。
  3. 初始化 AP3216C:初始化 AP3216C,读取 AP3216C 的数据。

本试验用到的资源如下:指示灯 LED0、AP3216C、串口。

AP3216C 是在 I.MX6U-ALPHA 开发板底板上,原理图如图:

AP3216C 使用的是 I2C1,其中 I2C1_SCL 使用的 UART4_TXD 这个IO、 I2C1_SDA 使用的是 UART4_R XD 这个 IO。

实验程序编写

在 bsp 文件夹下创建名为“i2c”和“ap3216c”的文件夹。

在 bsp/i2c 中新建 bsp_i2c.c 和 bsp_i2c.h 这两个文件,是I.MX6U 的 I2C 文件.

在 bsp/ap3216c 中新建 bsp_ap3216c.c 和 bsp_ap3216c.h 这两个文件,是 AP3216C 的驱动文件。

i2c.h

定义了一些I2C状态相关的宏。

定义了一个枚举类型i2c_direction,此枚举类型用来表示 I2C 主机对从机的操作,也就是读数据还是写数据。

定义了一个结构体 i2c_transfer,此结构体用于 I2C 的数据传输。

#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)/** I2C方向枚举类型*/
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_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);
unsigned char i2c_master_stop(I2C_Type *base);
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

i2c.c

i2c.c中一共有8个函数:

  • 函数i2c_init
  • 函数i2c_master_repeated_start
  • 函数 i2c_master_start
  • 函数 i2c_check_and_clear_error
  • 函数 i2c_master_stop
  • 函数i2c_master_write 
  • 函数 i2c_master_read
  • 函数 i2c_master_transfer

函数i2c_init,此函数用来初始化 I2C,重点是设置 I2C 的波特率,初始化完成以后开启 I2C。

/** @description		: 初始化I2C,波特率100KHZ* @param - base 	: 要初始化的IIC设置* @return 			: 无*/
void i2c_init(I2C_Type *base)
{/* 1、配置I2C */base->I2CR &= ~(1 << 7); /* 要访问I2C的寄存器,首先需要先关闭I2C *//* 设置波特率为100K* I2C的时钟源来源于IPG_CLK_ROOT=66Mhz* IC2 时钟 = PERCLK_ROOT/dividison(IFDR寄存器)* 设置寄存器IFDR,IFDR寄存器参考IMX6UL参考手册P1260页,表29-3,* 根据表29-3里面的值,挑选出一个还是的分频数,比如本例程我们* 设置I2C的波特率为100K, 因此当分频值=66000000/100000=660.* 在表29-3里面查找,没有660这个值,但是有640,因此就用640,* 即寄存器IFDR的IC位设置为0X15*/base->IFDR = 0X15 << 0;/** 设置寄存器I2CR,开启I2C* bit[7] : 1 使能I2C,I2CR寄存器其他位其作用之前,此位必须最先置1*/base->I2CR |= (1<<7);
}

函数i2c_master_repeated_start,发送一个重复开始信号,发送开始信号的时候也会顺带发送从设备地址。

/** @description			: 发送重新开始信号* @param - base 		: 要使用的IIC* @param - addrss		: 设备地址* @param - 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;/** 设置寄存器I2CR* bit[4]: 1 发送* bit[2]: 1 产生重新开始信号*/base->I2CR |=  (1 << 4) | (1 << 2);/** 设置寄存器I2DR* bit[7:0] : 要发送的数据,这里写入从设备地址*            参考资料:IMX6UL参考手册P1249*/ base->I2DR = ((unsigned int)address << 1) | ((direction == kI2C_Read)? 1 : 0);return 0;
}

函数 i2c_master_start,发送一个开始信号,发送开始信号的时候也顺带发送从设备地址。

/** @description			: 发送开始信号* @param - base 		: 要使用的IIC* @param - addrss		: 设备地址* @param - direction	: 方向* @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] : 要发送的数据,这里写入从设备地址*            参考资料:IMX6UL参考手册P1249*/ base->I2DR = ((unsigned int)address << 1) | ((direction == kI2C_Read)? 1 : 0);return 0;
}

函数 i2c_check_and_clear_error,检查并清除错误。

/** @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;
}

函数 i2c_master_stop,产生一个停止信号。

/** @description		: 停止信号* @param - base	: 要使用的IIC* @param			: 无* @return 			: 状态结果*/
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;
}

函数i2c_master_write ,向 I2C 从设备写数据。

/** @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--){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); 	/* 发送停止信号 */
}

函数 i2c_master_read,从 I2C 从设备读数据。

/** @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);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;}
}

函数 i2c_master_transfer,此函数就是用户最终调用的,用于完成 I2C通信的函数,此函数会使用前面的函数拼凑出 I2C 读/写时序。

/** @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;	
}

ap3216c.h

我们使用前面编写的 I2C 操作函数来配置 AP3216C ,配置完成以后就可以读取 AP3216C 里面的传感器数据。

ap3216c.h文件里定义了一些宏,分别为 AP3216C 的设备地址和寄存器地址。

#ifndef _BSP_AP3216C_H
#define _BSP_AP3216C_H#include "imx6ul.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

ap3216c.c

文件 bsp_ap3216c.c 里面共有 4 个函数,

  • 函数 ap3216c_init
  • 函数ap3216c_writeonebyte
  • 函数ap3216c_readonebyte
  • 函数 ap3216c_readdata

函数ap3216c_init,用于初始化 AP3216C。

此函数先初始化所使用到的 IO,比如初始化 I2C1 的相关 IO,并设置其复用为 I2C1。然后此函数会调用 i2c_init来初始化 I2C1,最后初始化 AP3216C。

/** @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失败 	*/
}

函数 ap3216c_writeonebyte ,向 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;
}

函数ap3216c_readonebyte,从 AP3216C 读取数据。

/** @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;
}

函数 ap3216c_readdata,读取 AP3216C 中的 ALS、 PS 和 IR 传感器数据。

/** @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); 	
}

main.c

首先调用 ap3216c_init 来初始化 AP3216C,如果 AP3216C 初始化失败的话就会进入循环,会在 LCD 上不断的闪烁字符串“AP3216C Check Failed!”和“Please Check!”,直到 AP3216C初始化成功。

在while(1)里循环调用函数 ap3216c_readdata 来获取 AP3216C 的 ALS、 PS 和 IR 传感器数据值,获取完成以后就会在 LCD 上显示出来。

#include "bsp_clk.h"
#include "bsp_delay.h"
#include "bsp_led.h"
#include "bsp_beep.h"
#include "bsp_key.h"
#include "bsp_int.h"
#include "bsp_uart.h"
#include "bsp_lcd.h"
#include "bsp_lcdapi.h"
#include "bsp_rtc.h"
#include "bsp_ap3216c.h"
#include "stdio.h"/** @description	: main函数* @param 		: 无* @return 		: 无*/
int main(void)
{unsigned short ir, als, ps;unsigned char state = OFF;int_init(); 				/* 初始化中断(一定要最先调用!) */imx6u_clkinit();			/* 初始化系统时钟 			*/delay_init();				/* 初始化延时 			*/clk_enable();				/* 使能所有的时钟 			*/led_init();					/* 初始化led 			*/beep_init();				/* 初始化beep	 		*/uart_init();				/* 初始化串口,波特率115200 */lcd_init();					/* 初始化LCD 			*/tftlcd_dev.forecolor = LCD_RED;	lcd_show_string(30, 50, 200, 16, 16, (char*)"ALPHA-IMX6U IIC TEST");  lcd_show_string(30, 70, 200, 16, 16, (char*)"AP3216C TEST");  lcd_show_string(30, 90, 200, 16, 16, (char*)"ATOM@ALIENTEK");  lcd_show_string(30, 110, 200, 16, 16, (char*)"2019/3/26");  while(ap3216c_init())		/* 检测不到AP3216C */{lcd_show_string(30, 130, 200, 16, 16, (char*)"AP3216C Check Failed!");delayms(500);lcd_show_string(30, 130, 200, 16, 16, (char*)"Please Check!        ");delayms(500);}	lcd_show_string(30, 130, 200, 16, 16, (char*)"AP3216C Ready!");  lcd_show_string(30, 160, 200, 16, 16, (char*)" IR:");	 lcd_show_string(30, 180, 200, 16, 16, (char*)" PS:");	lcd_show_string(30, 200, 200, 16, 16, (char*)"ALS:");	tftlcd_dev.forecolor = LCD_BLUE;	while(1)					{ap3216c_readdata(&ir, &ps, &als);		/* 读取数据		  	*/lcd_shownum(30 + 32, 160, ir, 5, 16);	/* 显示IR数据 		*/lcd_shownum(30 + 32, 180, ps, 5, 16);	/* 显示PS数据 		*/lcd_shownum(30 + 32, 200, als, 5, 16);	/* 显示ALS数据 	*/ delayms(120);state = !state;led_switch(LED0,state);	}return 0;
}

使用 Make 命令编译代码,编译成功以后使用软件 imxdownload 将编译完成的 ap3216c.bin文件下载到 SD 卡中,烧写成功以后将 SD 卡插到开发板的 SD 卡槽中,然后复位开发板。

还记得我们说过,AP3216C是一个三合一环境传感器,支持环境光强度(ALS)、接近距离(PS)和红外线强度(IR)这三个环境参数检测。实验现象大概就是,光线改变,ALS的值随之改变;用手靠近或者远离AP3216C,PS的值随之改变。

http://www.dtcms.com/a/271779.html

相关文章:

  • 解锁localtime:使用技巧与避坑指南
  • shell 字符串常用操作
  • 网安系列【16】之Weblogic和jboss漏洞
  • 深入剖析 ADL:C++ 中的依赖查找机制及其编译错误案例分析
  • 短剧分销系统开发指南:从0到1构建高效变现平台
  • 基于双向cuk斩波均衡电路的串联锂离子均衡系统设计
  • 文心一言4.5开源部署指南及文学领域测评
  • frp内网穿透下创建FTP(解决FTP“服务器回应不可路由的地址。使用服务器地址替代”错误)
  • 【macos用镜像站体验】Claude Code入门使用教程和常用命令
  • JS实现页面实时时间显示/倒计时
  • SMTPman,smtp的端口号是多少全面解析配置
  • 【数据结构】时间复杂度和空间复杂度
  • 杰赛S65_中星微ZX296716免拆刷机教程解决网络错误和时钟问题
  • Java线程池原理概述
  • 浏览器 实时监听音量 实时语音识别 vue js
  • 解析LLM层裁剪:Qwen实战指南
  • 搭建自动化工作流:探寻解放双手的有效方案(1)
  • Spring Boot项目中大文件上传的高级实践与性能优化
  • Spring for Apache Pulsar->Reactive Support->Message Consumption
  • Socket服务器代理工具及服务端网络转发中枢
  • 【Action帧简要分析】
  • iOS APP混合开发性能测试怎么做?页面卡顿、通信异常的工具组合实战
  • iOS Widget 开发-7:TimelineProvider 机制全解析:构建未来时间线
  • 在 MacOS 上安装和配置 Kafka
  • 深入理解 Linux 中的 stat 函数与文件属性操作
  • 每天一个前端小知识 Day 29 - WebGL / WebGPU 数据可视化引擎设计与实践
  • 在Linux上使用libasan开发QT程序定位内存问题
  • Spring AI 系列之七 - MCP Client
  • 限流式保护器如何筑牢无人驾驶汽车充电站的安全防线
  • linxu内核的signal fault和arm内核的flault