您现在的位置是:首页 > 数码 > 

16

2025-07-27 01:53:36
16 第十六章 I2C编程 ​ I2C(Inter-Integrated Circuit BUS)是I2C BUS简称,中文为集成电路总线,是目前应用最广泛的总线之一,和IMX6ULL有些相关的是,刚好该总线是XP前身的PHILIPS设计。当前仍然是应用最广泛的总线协议之一。 16.1 I2C协议 16.

16

第十六章 I2C编程

​ I2C(Inter-Integrated Circuit BUS)是I2C BUS简称,中文为集成电路总线,是目前应用最广泛的总线之一,和IMX6ULL有些相关的是,刚好该总线是XP前身的PHILIPS设计。当前仍然是应用最广泛的总线协议之一。

16.1 I2C协议

16.1.1 概述

​ I2C是一种串行通信总线,使用多主从架构,最初设计师为了让主板、嵌入式系统或手机用以连接低速周边设备而发展而来。在小数据量场合使用,有传输距离短,任意时刻只能有一个主机等特性。严格意义上讲,I2C应该是软硬件结合体,所以我们将分物理层和协议层来介绍该总线。(总线结构如下图)

​ 对于I2C通信的过程,韦老师有个形象的说法:

​ 传输数据,我们需要发数据,从主设备发送到从设备上去,也需要把数据从从设备传送到主设备上去,数据涉及到双向传输。

​ 举个例子:

​ 体育老师:可以把球发给学生,也可以把球从学生中接过来。

​ ① 发球:

​ a. 老师说:注意了(start)

​ b. 老师对A学生说,我要球发给你(地址)。

​ c. 老师就把球发出去了(传输)。

​ d. A收到球之后,应该告诉老师一声(回应)。

​ e. 老师说下课(停止)

​ ② 接球:

​ a. 老师说注意了(start),

​ b. 老师说:B把球发给我(地址)

​ c. B就把球发给老师(传输)

​ d. 老师收到球之后,给B说一声,表示收到球了(回应)。

​ e. 老师说下课(停止)

我们就使用这个简单的例子,来解释一下IIC的传输协议。

  • 老师说注意了,表示开始信号(start)
  • 老师告诉某个学生,表示发送地址(address)
  • 老师发球/接球,表示数据的传输
  • 老师/学生收到球,回应表示:回应信号(ACK)
  • 老师说下课,表示IIC传输接受§
16.1.2 物理层

​ **特性1:**半双工(非全双工)

​ 两条总线线路:

SDA(串行数据线): 主芯片通过一根SDA线既可以把数据发给从设备,也可以从SDA上读取数据,连接SDA线的引脚里面必然有两个引脚(发送引脚/接受引脚),具体可以参考下图device端I2Cn_SDA(output/input)。

​ **SCL(**串行时钟线):**同SDA的引脚电路结构一致,引脚的输出驱动与输入缓冲连在一起。其中输出为漏极开路的场效应管、输入缓冲为一只高输入阻抗的同相器。这样结构有如下特性:

​ a. 由于 SDA、SCL 为漏极开路结构,借助于外部的上拉电阻实现了信号的“线与”逻辑;

​ b. 引脚在输出信号的同时还将引脚上的电平进行检测,检测是否与刚才输出一致。为 “时钟同步”和“总线仲裁”提供硬件基础。

​ SDA和CLK连接线上连有两个上拉电阻,当总线空闲时,两根线均为高电平。连到总线上的任一器件输出的低电平,都将使总线的信号变低。(物理层连接如下图所示)

​ **特性2:**地址和角可配置

​ 每个连接到总线的器件都可以通过唯一的地址和其它器件通信,主机/从机角和地址可配置,主机可以作为主机发送器和主机接收器。

特性:多主机

​ IIC是真正的多主机总线,( IIC可以在通讯过程中,改变主机),如果两个或更多的主机同时请求总线,可以通过冲突检测和仲裁防止总线数据被破坏。

​ **特性4:**传输速率

​ 传输速率在标准模式下可以达到100kb/s,快速模式下可以达到400kb/s。

​ **特性5:**负载和距离

​ 节点的最大数量受限于地址空间以及总线电容决定。另外总电容也限制了实际通信距离只有几米。

16.1.2 协议层

(1)数据有效性

​ I2C协议的数据有效性是靠时钟来保证的,在时钟的高电平周期内,SDA线上的数据必须保持稳定。数据线仅可以在时钟SCL为低电平时改变。

(2)起始和结束条件

**起始条件:**当SCL为高电平的时候,SDA线上由高到低的跳变被定义为起始条件。

**结束条件:**当SCL为高电平的时候,SDA线上由低到高的跳变被定义为停止条件,要注意起始和终止信号都是由主机发出的,连接到I2C总线上的器件,若具有I2C总线的硬件接口,则很容易检测到起始和终止信号。

​ 总线在起始条件之后,视为忙状态,在停止条件之后被视为空闲状态。

()应答

​ 每当主机向从机发送完一个字节的数据,主机总是需要等待从机给出一个应答信号,以确认从机是否成功接收到了数据,从机应答主机所需要的时钟仍是主机提供的,应答出现在每一次主机完成8个数据位传输后紧跟着的时钟周期,低电平0表示应答,1表示非应答

(4)数据帧格式

​ SDA线上每个字节必须是8位长,传输几个字节每个transfer是不限制的,每个字节后面必须跟一个ACK。数据首先用最高有效位(MSB)传输。

16.2 IMX6ULL的I2C控制器操作与寄存器介绍

​ IMX6ULL的I2C提供了标准I2C从服务器和主服务器的功能,I2C是设计与标准的XP I2C总线协议兼容,所以上面的通用知识完全可以应用在IMX6ULL I2C的编程和控制。

​ 掌握IMX6ULL I2C控制器的使用,重点要熟悉IMX6ULL I2C的寄存器的操作。

​ 重点介绍寄存器之前,我们先来看一下IMX6ULL的I2C控制器的框图,对IMX6ULL I2C的结构和特性有一个初步认识。

IMX6ULL的I2C控制器有如下额外特性:

​ ① 多主机运行。

​ ② 64种不同的串行时钟频率之一的软件可编程性。

​ ③ 软件可选择的应答位。

​ ④ 中断驱动,逐字节数据传输。

​ ⑤ 仲裁丢失中断与自动模式切换从主到从。

​ ⑥ 启动和停止信号生成/检测。

​ ⑦ 重复启动信号生成。

​ ⑧ 应答位生成和检测。

​ ⑨ 总线忙检测。

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

​ 通过上面的介绍对IMX6ULL的I2C控制器有了整体认识,下面结合I2C 框图对重点寄存器进行介绍。

16.2.1 I2C Memory Map

I2C包含5个16-bit 的寄存器

注意:寄存器在偏移量0x0002/0x0006/0x000A/0x000E作为保留位。

可以看到I2C1的入口地址为21A_0000,这个我们重点关注,后面做实验,编程会使用到。

16.2.2 Register

​ 我们先把这些寄存器的用途做个介绍,后面在编程框架那里会结合位图来讲解。下面这部分也可以先跳过,回头当表来查。

16.2.2.1 I2C Address Register (I2Cx_IADR)地址寄存器,偏移量为0h。

​ a. bit15-8为保留位即只读为0。

​ b. bit7-1(ADR)位是I2C作为从机时的地址。从属模式是I2C的默认模式,这个地址是作为从机的相应地址,不能被软件复位。

16.2.2.2 I2C Frequency Divider Register (I2Cx_IFDR)分频寄存器,偏移量为4h。

​ a. bit15-6位为保留位,即只读为0。

​ b. bit5-1 位为I2C时钟频率

​ 注意:该值在传输过程中不应该改变,但是可以在之前改变。

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

寄存器IC位设置计算方法如下:

​ I2C的时钟源来源于IPG_CLK_ROOT=66Mhz

PLL2 = 528 MHz

PLL2_PFD2 = 528 MHz

IPG_CLK_ROOT = (PLL2_PFD2 / ahb_podf )/ ipg_podf = (528 MHz/4)/2 = 66Mhz

PER_CLK_ROOT = IPG_CLK_ROOT/perclk_podf = 66 MHz/1 = 66 MHz

设置I2C的波特率为100K, 因此当分频值=66000000/100000=660.

参考Table 1-. I2C_IFDR Register Field Values 中只有640对应的0x15最接近

即寄存器IFDR的IC位设置为0X15。

16.2.2. I2C Control Register (I2Cx_I2CR)控制寄存器,偏移量为8h

​ a. bit 15-8为保留位,即只读为0。

​ b. bit 7 为I2C使能位。(0 disable,1 enable)

​ c. bit 6 为I2C中断使能位。(0 disable,1 enable)

​ d. bit 5 为主/从模式选择位(0 slave mode,1 master mode )

​ e. bit 4 为传输方向模式选择位 (0 receive mode,1 transmit mode)

​ f. bit 为应答使能位 (0 ACK , 1 O ACK)

​ g. bit 2 重复开始信号(0 no repeat start,1 Generates repeat start)

​ h. bit 0 保留位

16.2.2.4 I2C Status Register (I2Cx_I2SR)状态寄存器,偏移量为Ch

​ a. bit 15-8为保留位即只读为0。

​ b. bit 7 数据传输状态位(0 传输中,1 传输完成)

​ c. bit 6 I2C地址是否为从标识(0 不表示,1 是从机地址)

​ d. bit 5 I2C总线忙状态标识位(0 空闲,1 忙 )

​ e. bit 4 仲裁丢失位 (0 正常,1 仲裁丢失)

​ f. bit 保留位

​ g. bit 2从机读写标识位 (0 slave接收,主向从写 , 1 slave发送 主向从读)

​ h. bit 1 I2C中断(0无中断等待, 1有中断等待)

​ i. bit 0 应答信号标识位(0检测到ACK, 1检测到O ACK)

6.2.2.5 I2C Data I/O Register (I2Cx_I2DR)数据寄存器,偏移量为10h

​ a. bit 15-8为保留位即只读为0。

​ b. bit 7-1 数据字节

​ 注意:在主接收模式下,读取数据寄存器允许发生读取并初始化下一个字节被接收。在从模式下,相同功能需要编址后生效。

​ 低8位为有效数据位,发送数据时将数据写到这个寄存器中,如果要接收时直接读取该寄存器中的数据。

16. AP216C操作方法

16..1 AP216C简介

​ AP216C 模块的核心就是这个芯片本身。这颗芯片集成了光强传感器(ALS: Ambient Light Sensor),接近传感器(PS: Proximity Sensor),还有一个红外LED(IR LED)。这个芯片设计的用途是给手机之类的使用,比如:返回当前环境光强以便调整屏幕亮度;用户接听电话时,将手机放置在耳边后,自动关闭屏幕避免用户误触碰。该芯片通过I2C接口作为slave与主控制器相连,支持中断。

16..2 AP216C特性

​ ① 接口:I2C

​ ② 速率:FS mode 可达400kbit/s

​ ③ 模式:ALS/PSIR/ALSPSIR/PD/ALS once/SW Reset/PSIR once/ALSPSIR once。

​ ④ 内置温度补偿电路。

​ ⑤ 工作温度:-0℃ to 80℃

​ ⑥ ALS

​ a. (0-6556)16位有效的线性输出

​ b. 4量程动态范围可选择

​ c. 防闪烁(50/60 HZ)

​ d. 高敏感性@黑玻璃

​ e. 窗口损失补偿

​ ⑦ PS

​ a. (0-102)10位有效线性输出

​ b. 4增益

​ c. 高环境光抑制

​ d. 串扰补偿

​ ⑧ 封装大小:4.1mm x 2.4mm x 1.5mm

16.. AP216C结构图

​ 下面是AP216C的结构图以及典型电路应用图

​ 从图上看AP216C结构简单,应用方便。本来模块我们其实也不用太深究原理,所以重点我们放在对典型I2C从设备的应用上。

16..4 AP216C寄存器及使用

​ a. I2C从设备地址

​ 从设备地址有7bit ,一个读/写位应该由主设备附加到从设备地址以正确地与设备通信。

​ AP216的地址是0X1E。

​ b. 系统寄存器表

​ 同其他所有从设备一样,AP216C有它的内部寄存器让我们来设置和操作。表格如下所示。可以知道。次级地址0x00为系统配置寄存器,并且提供了后面7bit组合的工作模式供选择。

​ 后面会参考这张表格对AP216C进行编程并读取数据。

16.4 I2C控制器编程_框架

​ 之前的所有铺垫都是为了实现I2C通讯,所以怎么用代码实现也是尤为关键的一个问题。本节源码目录在裸机Git仓库 oosProgramProject/(16_I2C编程\001_example_i2c_ap216c_led_show\和

裸机Git仓库 oosProgramProject/(16_I2C编程\001_example_i2c_ap216c_led_show\i2c.h

​ 首先,需要定义I2C寄存器的入口地址,各个寄存器绝对地址,我们代码仅使用I2C1,故仅先定义I2C1相关寄存器。

 /**The I2C contains five 16-bit registers.*//*  绝对地址  寄存器名    位宽(bit)权限   复位值   章节/页* 21A_0000  (I2C1_IADR)  16       R/W  0000h   1.7.1/146* 21A_0004  (I2C1_IFDR)  16       R/W  0000h   1.7.2/146* 21A_0008  (I2C1_I2CR)  16       R/W  0000h   1.7./1465* 21A_000C  (I2C1_I2SR)  16       R/W  0081h  1.7.4/1466* 21A_0010  (I2C1_I2DR)  16       R/W  0000h  1.7.5/1468*/#define I2C1_BASE_ADDR            (0x21A0000u)/* I2C1 Base address */#define I2C1                       ((I2C_REGISTERS *)I2C1_BASE_ADDR)

​ 有了上面的地址,定义如下寄存器结构体,这样在使用时,我们仅需要把入口地址定义成如下结构,那么就可以通用I2C1-IC25,在使用时传入相应的入口地址即可。

 /* 寄存器地址的宏结构体定义,此种方式仅定义入口地址即可 *//* all registers address is Base address  xh offset*/typedef struct tagRegisters{volatile uint16_t IADR;                /*I2C Address Register, offset: 0x0 */uint8_t ReservedIADR[2];volatile uint16_t IFDR;                /*I2C Frequency Divider Register, offset: 0x4 */uint8_t ReservedIFDR[2];volatile uint16_t I2CR;                /*I2C Control Register, offset: 0x8 */uint8_t ReservedI2CR[2];volatile uint16_t I2SR;                /*I2C Status Register, offset: 0xC */uint8_t ReservedI2SR[2];volatile uint16_t I2DR;                /*I2C Data I/O Register, offset: 0x10 */} I2C_REGISTERS;

​ 有了基本的信息,我们也要有相应的操作,主要有读和写两个操作码。

 typedef enum enI2C_OPCODE{I2C_WRITE = 0,            /* 主机向从机写数据 */I2C_READ  = 1,  		    /* 主机从从机读数据 */I2C_DOOTHIG_BULL} I2C_OPCODE;

​ 然后需要有个分配和传输承载信息角的载体,我们仅以做master为例,如果做通用的结构体,还可以加上角等信息。而master的主传输结构体包括目标以下信息:

 typedef struct tagI2cTransfer{uint8_t  ucSlaveAddress;      	 /* 7位从机地址 */uint2_t ulOpcode  ; 		     /* 操作码*/uint2_t ulSubAddress;          /* 目标寄存器地址 */uint8_t  ulSubAddressLen;    	 /* 寄存器地址长度 */volatile uint2_t ulLenth;  	     /* 数据长度 */uint8_t *volatile pbuf;    	     /* 数据*/
} I2C_TRASFER;

​ 有了这些寄存器的基本信息、操作方式、传输结构,下面来实现具体的函数和功能。

16.4.1 i2c_init
void i2c_init(I2C_REGISTERS *I2C_BASE);81 void i2c_init(I2C_REGISTERS *I2C_BASE);

​ 通过传入I2C1的入口地址,完成初始化。

​ 因为不使用中断服务函数模式,初始化代码非常简单,我们仅需要将I2CR(bit7)(位图如下)

​ 置0后写入IFDR为0x15(位图如下),设置波特率为100k。(具体计算方法参考16.2 IMX6ULL的I2C控制器操作与寄存器介绍中关于分频寄存器),然后再使能I2C,将I2CR(bit7)置1 。

代码如下:

     I2C_BASE->I2CR &= ~(1 << 7);I2C_BASE->IFDR = 0x15;I2C_BASE->I2CR |= (1<<7);
16.4.2 i2c_transfer

函数原型如下:

uint8_t i2c_transfer(I2C_REGISTERS *I2C_BASE, I2C_TRASFER *transfer);

参数是I2C控制器入口地址以及传输结构体,作为通用公共传输函数,负责公共流程

下面是函数流程图。

清除标识位是bit-4 IAL 仲裁位,bit-1 IIF 中断标志位都置位,位图如图所示:

代码如下:

I2C_BASE->I2SR &= ~((1 << 1) | (1 << 4));

等待传输完成需要判断上面的ICF位:

while(!((I2C_BASE->I2SR >> 7) & 0X1)){};
16.4. i2c_start

​ start需要先判断I2C是否忙,然后设置发送模式,最后将slave地址通过数据寄存器发出去,函数声明如下:

uint8_t i2c_start(I2C_REGISTERS *I2C_BASE, uint8_t ucSlaveAddr, uint2_t ulOpcode);

​ 首先,判断忙是上面的IBB位

if(I2C_BASE->I2SR & (1 << 5))

​ 设置发送模式MSTA设置为1即master,MTX设置为1即发送模式,位图如下:

I2C_BASE->I2CR |= (1 << 5) | (1 << 4);

​ 接下来将slave地址放到数据寄存器中发送,最后一位是操作码,位图如下:

6  I2C_BASE->I2DR = ((uint2_t)ucSlaveAddr << 1) | ((I2C_READ == ulOpcode)? 1 : 0);
16.4.4 i2c_check

​ check中传入的是I2SR寄存器中的值,代码如下:

uint8_t i2c_check(I2C_REGISTERS *I2C_BASE, uint2_t status);

​ 判断仲裁位即SR寄存器bit4,如果置位,需要清除仲裁位,并重启I2C。如果没有收到从机的应答信号即SR寄存器bit0是否为1,如果没有返回状态码AK。代码如下:

uint8_t i2c_check(I2C_REGISTERS *I2C_BASE, uint2_t status);/* 检查是否发生仲裁丢失错误 */if(status & (1<<4)){I2C_BASE->I2SR &= ~(1<<4);	   /* 清除仲裁丢失错误位 */I2C_BASE->I2CR &= ~(1 << 7);	   /* 先关闭I2C */I2C_BASE->I2CR |= (1 << 7);	   /* 重新打开I2C */return I2C_ARBITRATIOLOST;} else if(status & (1 << 0))     	       /* 没有接收到从机的应答信号 */{return I2C_AK;		           /* 返回AK(o acknowledge) */}return I2C_OK;
16.4.5 i2c_write

​ 写函数完成清除标志位,设置模式为发送,把buf中的数据写入I2DR数据寄存器中,等待传输完成(判断的是I2SR bit1 IIF),然后清除该位。然后检查ACK,最后再清除标识位。代码如下:

void i2c_write(I2C_REGISTERS *I2C_BASE, ct uint8_t *pbuf, uint2_t len){/* 等待传输完成 */while(!(I2C_BASE->I2SR & (1 << 7))); I2C_BASE->I2SR &= ~(1 << 1); 	        /* 清除标志位 */I2C_BASE->I2CR |= 1 << 4;		       /* 发送数据 */while(len--){I2C_BASE->I2DR = *pbuf; 	   /* 将buf中的数据写入到I2DR寄存器 */while(!(I2C_BASE->I2SR & (1 << 1))); 	            /* 等待传输完成 */	I2C_BASE->I2SR &= ~(1 << 1);                         /* 清除标志位 *//* 检查ACK */if(i2c_check(I2C_BASE, I2C_BASE->I2SR))break;}I2C_BASE->I2SR &= ~(1 << 1);i2c_stop(I2C_BASE); 	                                 /* 发送停止信号 */}
16.4.6 i2c_read

​ 读函数具体流程如下,值得注意两个点,一个是假读是为了启动用的,有兴趣的小伙伴可以移步 查看官方解释。另外如果仅需要读一个字节的话,需要发送一个ACK信号。即将I2CR bit TXAK置1。其他寄存器和操作我们在上面已经都讲解过。代码如下:

void i2c_read(I2C_REGISTERS *I2C_BASE, uint8_t *pbuf, uint2_t len)
{volatile uint8_t dummy = 0;dummy; 	/* 防止编译报错 *//* 等待传输完成 */while(!(I2C_BASE->I2SR & (1 << 7))); I2C_BASE->I2SR &= ~(1 << 1); 				/* 清除中断挂起位 */I2C_BASE->I2CR &= ~((1 << 4) | (1 << ));	/* 接收数据 *//* 如果只接收一个字节数据的话发送ACK信号 */if(len == 1)I2C_BASE->I2CR |= (1 << );dummy = I2C_BASE->I2DR; /* 假读 */while(len--){while(!(I2C_BASE->I2SR & (1 << 1))); 	/* 等待传输完成 */	I2C_BASE->I2SR &= ~(1 << 1);			/* 清除标志位 */if(len == 0){i2c_stop(I2C_BASE); 			/* 发送停止信号 */}if(len == 1){I2C_BASE->I2CR |= (1 << );}*pbuf = I2C_BASE->I2DR;}
}
16.4.7 I2C读写标准流程

下面是是I2C的读写标准流程,可能实际使用时根据需求略有异同。

写寄存器的标准流程如下图:

① 1.Master发起START

② 2.Master发送I2C addr(7bit)和w操作0(1bit),等待ACK

③ .Slave发送ACK

④ 4.Master发送reg addr(8bit),等待ACK

⑤ Slave发送ACK

⑥ Master发送data(8bit),即要写入寄存器中的数据,等待ACK

⑦ Slave发送ACK

⑧ 第6步和第7步可以重复多次,即顺序写多个寄存器

⑨ Master发起STOP

读寄存器的标准流程如下图:

① Master发送I2C addr(7bit)和w操作1(1bit),等待ACK

② Slave发送ACK

③ Master发送reg addr(8bit),等待ACK

④ Slave发送ACK

⑤ Master发起RESTART

⑥ Master发送I2C addr(7bit)和r操作1(1bit),等待ACK

⑦ Slave发送ACK

⑧ Slave发送data(8bit),即寄存器里的值

⑨ Master发送ACK

⑩ 第8步和第9步可以重复多次,即顺序读多个寄存器

⑪ Master发送O ACK表示读取完成,从机也不用发送ACK

⑫ Master发送STOP

1.5 I2C控制器编程_中断

I2C控制器中和中断有关的寄存器如下:

**IIE:**I2C_I2CR(bit6)(0 disable I2C interrupt ,1 enable I2C interrupt)

**IIF:**I2C_I2SR (bit1) (0 o I2C interrupt pending ,1 An interrupt is pending)

上面分别是中断使能位,中断状态位。

编码中在传送完1byte后可以通过判断IIF状态来确认ACK和传输完成。当然作为从机时,收到自己的地址后也可以进入中断。

16.6 AP216C编程

AP216C的编程基于I2C初始化后,write/read功能之上。

代码路径在裸机Git仓库 oosProgramProject/(16_I2C编程\001_example_i2c_ap216c_led_show\)裸机Git仓库 oosProgramProject/(16_I2C编程\001_example_i2c_ap216c_led_show\ap216c.h)

16.6.1 AP216C初始化IO

参考100ASK_IMX6ULL原理图如下:

AP216C接口简单,仅将PI2和PI8分别连接到I2C1_SCL和I2C1_SDA。而另外的一端从下图可知,I2C1_SCL使用的UART4_RXD,I2C1_SDA使用的UART4_TXD这两个IO。

16.6.2 初始化AP216C

通过《1.-1..4 AP216C寄存器及使用》 的系统寄存器表格可知,0X00这个寄存器是模式控制寄存器,用来设置AP216C的工作模式。

初始化流程如下:

① 复位(设置0X00寄存器为0X04)

② 设置工作模式(如0X0,开启ALSPSIR,其他模式请参考16.中系统寄存器表格)

③ 设置中断(可选)

代码如下:

 	ret = i2c_write_one_byte(AP216C_ADDR, AP216C_SYSTEMCOG, 0X4);mdelay(10);						/* AP216C复位至少10ms */ret = i2c_write_one_byte(AP216C_ADDR, AP216C_SYSTEMCOG, 0X);

​ 判断是否初始化成功,就读一下刚写入的data是否为0X,代码如下:

data = i2c_read_one_byte(AP216C_ADDR, AP216C_SYSTEMCOG);	

​ i2c_write_one_byte和i2c_read_one_byte就是刚才 i2c_transfer函数入参的填写过程,这里不多介绍。详细可以参考中具体实现。

16.6. AP216C数据读取

​ 重点介绍一下下面的读数据操作,函数原型如下:

	void ap216c_read_data(uint16_t *ir, uint16_t *ps, uint16_t *als)

​ 入参和出参很简单,分别是这三个寄存器的值。

​ 寄存器AP216C_IRDATALOW到AP216C_PSDATAHIGH一共6个寄存器,将这个6个寄存器的值顺序读取,分别放到ir,ps,als变量中。

void ap216c_read_data(uint16_t *ir, uint16_t *ps, uint16_t *als)
{uint8_t buf[6];uint8_t i;for(i = 0; i < 6; i)	/* 循环读取所有传感器数据 */{buf[i] = i2c_read_one_byte(AP216C_ADDR, AP216C_IRDATALOW  i);	}if(buf[0] & 0X80)      /* IR_OF位为1,则数据无效 */{*ir = 0;			}else 				    /* 读取IR传感器的数据   */{*ir = ((uint16_t)buf[1] << 2) | (buf[0] & 0X0); 	}/* 读取ALS传感器的数据 */  *als = ((uint16_t)buf[] << 8) | buf[2];	if(buf[4] & 0x40)	    /* IR_OF位为1,则数据无效*/{*ps = 0;    	}										else 			       /* 读取PS传感器的数据    */	{*ps = ((uint16_t)(buf[5] & 0XF) << 4) | (buf[4] & 0X0F); }return;
}

​ 从0X0A~0X0F这6个寄存器就是数据寄存器,保存着ALS、PS和IR这三个传感器获取到的数据值。如果同时打开ALS、PS和IR的读取间隔最少要112.5ms,其他情况时间间隔,如下图所示:

16.7 AP216C上机实验

​ 通过下面的两个例程总结本章对于AP216C通过I2C总线将数据发送给IMX6ULL并使用不同输出方法显示。实验程序结构如下:

​ ① 实验中的显示或输出设备

​ ② 通信方式

​ ③ 设备

​ ④ 应用

16.7.1 AP216C实验一

​ 代码在裸机Git仓库 oosProgramProject/(16_I2C编程\001_example_i2c_ap216c_led_show)

​ 通过上述实验程序结构图中1-步骤后,应用通过如下方式获取数据,并显示结果。

 while(!ret){   /*环境光强度(ALS)、接近距离(PS)和红外线强度(IR)*/ap216c_read_data(&ir,&ps,&als);/*调整光强和接近距离可以控制LED亮灭*/if (als>100 || ps >1000){led_ctl(1);}else{led_ctl(0);}}
16.7.2 参考章节《4-1.4编译程序》编译程序

​ 进入 裸机Git仓库 oosProgramProject/(16_I2C编程\001_example_i2c_ap216c_led_show) 源码目录进行编译。

16.7. 参考章节《-1.4映像文件烧写、运行》烧写、运行程序

​ 初始化成功后会绿灯闪烁一下后关闭,LED用来判断ALS或者PS寄存器值的是否超过阈值,如果超过绿灯亮起,否则常暗。

​ 下图常暗:

​ 下图调整板子入光或者接近传感器后绿灯亮起:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-8bHFu5S5-1642060404761)(@main/IMX6ULL-BareMetal/I2C_Program_image0026.png)]

16.7.4 AP216C实验二

​ 代码在裸机Git仓库 oosProgramProject/(16_I2C编程\002_example_i2c_ap216c_printf_show)

​ 通过上述实验程序结构图中1-步骤后,应用通过如下方式获取数据,并显示结果。

    while(1){   delay(100000);/*环境光强度(ALS)、接近距离(PS)和红外线强度(IR)*/ap216c_read_data(&ir,&ps,&als);printf(ir=%d ps=%d als=%d\n\r,ir,ps,als);}
16.7.5 参考章节《4-1.4编译程序》编译程序

进入 **裸机Git仓库 oosProgramProject/(16_I2C编程\002_example_i2c_ap216c_printf_show)**源码目录进行编译。

16.7.6 参考章节《-1.4映像文件烧写、运行》烧写、运行程序

实验结果如图所示:

#感谢您对电脑配置推荐网 - 最新i3 i5 i7组装电脑配置单推荐报价格的认可,转载请说明来源于"电脑配置推荐网 - 最新i3 i5 i7组装电脑配置单推荐报价格

本文地址:http://www.dnpztj.cn/shuma/707521.html

相关标签:无
上传时间: 2023-11-21 22:07:58
留言与评论(共有 8 条评论)
本站网友 ab型
20分钟前 发表
​ 寄存器AP216C_IRDATALOW到AP216C_PSDATAHIGH一共6个寄存器,将这个6个寄存器的值顺序读取,分别放到ir,ps,als变量中
本站网友 生命人寿保险公司
12分钟前 发表
uint2_t len){/* 等待传输完成 */while(!(I2C_BASE->I2SR & (1 << 7))); I2C_BASE->I2SR &= ~(1 << 1); /* 清除标志位 */I2C_BASE->I2CR |= 1 << 4; /* 发送数据 */while(len--){I2C_BASE->I2DR = *pbuf; /* 将buf中的数据写入到I2DR寄存器 */while(!(I2C_BASE->I2SR & (1 << 1))); /* 等待传输完成 */ I2C_BASE->I2SR &= ~(1 << 1); /* 清除标志位 *//* 检查ACK */if(i2c_check(I2C_BASE
本站网友 中国太平人寿保险股份有限公司
9分钟前 发表
代码如下: I2C_BASE->I2CR &= ~(1 << 7);I2C_BASE->IFDR = 0x15;I2C_BASE->I2CR |= (1<<7); 16.4.2 i2c_transfer 函数原型如下: uint8_t i2c_transfer(I2C_REGISTERS *I2C_BASE
本站网友 最年轻的博士
11分钟前 发表
​ ④ 中断驱动,逐字节数据传输
本站网友 牛满
21分钟前 发表
​ 另外的IMX6ULL 的I2C也支持两种模式:标准模式和快速模式,标准模式下I2C数据传输速率最高是100Kbits/s,在快速模式下数据传输速率最高为400Kbits/s
本站网友 盐城二手房网
24分钟前 发表
AP216C_IRDATALOW  i); }if(buf[0] & 0X80) /* IR_OF位为1
本站网友 北京吸脂价格
9分钟前 发表
传输结构,下面来实现具体的函数和功能