本章,使用 ESP32-S3 的硬件 IIC接口去驱动 AP3216C传感器,检测环境光强度(ALS)、接近距离(PS)和红外线强度(IR)等环境参数。本章分为如下几个小节:
本章分为如下几个小节:
25.1 AP3216C 简介
25.2 硬件设计
25.3 程序设计
25.4 下载验证

25.1 AP3216C 简介

        AP3216C 是敦南科技推出的一款三合一环境传感器, 包含数字环境光传感器(ALS)、接近传感器(PS)和一个红外LED IR)。该芯片通过IIC接口和MCU连接,并支持中断(INT)输出。 广泛应用于智能手机、平板电脑、笔记本电脑等设备,实现自动亮度调节、接近检测、手势识别等功能。AP3216C 实物图如下图所示:

图 25.1.1 AP3216C 实物图

        AP3216C 的特点如下:

​特性维度​

​说明​

​​核心功能​​

环境光传感 (ALS), 接近检测 (PS), 红外发射 (IR LED)

​​通信接口​​

​I2C​​ (设备地址 ​​0x1E​​), 支持 ​​100kHz / 400kHz​​ 速率

​​供电电压​​

​2.4V ~ 3.6V​​, 可直接由 3.3V 或 3.0V MCU 供电,低功耗设计

​​环境光传感器(ALS)​​

​16位分辨率​​,动态范围广 (0.0164K Lux),支持自动增益调整和红光/红外光补偿,适应不同光照条件

​​接近传感器(PS)​​

​10位分辨率​​,利用IR LED发射红外光并通过光电二极管检测反射信号,具备环境光噪声抑制和中断模式

​​集成优势​​

三合一模块节省空间,提供多种可编程工作模式 (如ALS, PS+IR, ALS+PS+IR等),支持中断功能,低功耗

​​典型应用​​

智能手机自动亮度调节、通话时屏幕熄灭、手势识别、智能家居人体存在检测、可穿戴设备入检测检测等

表 25.1.1 AP3216C 特性说明

        内部结构与工作原理​:

        AP3216C 内部集成了光电二极管阵列、放大器电路、模数转换器(ADC)以及控制逻辑单元。环境光传感器和接近传感器分别与ADC模块连接以采集外部数据:

  • 环境光检测​​:ALS 模块检测周围光照强度,并将其转换为数字值;
  • ​​接近检测​​:IR LED 发射红外光,PS 模块通过检测反射回来的光强来判断物体与传感器的距离。物体越近,反射信号越强。

        AP3216C 的框图如下图所示:

图 25.1.2 AP3216C 框图

        AP3216C 的引脚说明如下表所示:

引脚编号 标号 说明
1 VDD 电源,接 3.3V
2 SCL IIC 时钟信号,开漏
3 GND 地线
4 LEDA LED 阳极,接 3.3V
5 LEDC LED 阴极,一般连接 LDR
6 LDR LED 驱动输出脚,一般接 LEDC
7 INT 中断输出脚
8 SDA IIC 数据信号,开漏

表 25.1.2 AP3216C 引脚说明

        AP3216C 和主控芯片只需要连接 SCL、SDA 和 INT,就可以实现驱动。其 SCL 和 SDA 同24C02、 XL9555 共用,连接在 IO41 和 IO42 上, INT 脚连接在 XL9555 的 P0_0 上。

25.1.1 AP3216C 寻址

        要进行 IIC 通信,首先得知道器件地址,AP3216C 器件地址是 7 位的,具体格式如下表。

Bit 7 6 5 4 3 2 1 0
Symbol 0 0 1 1 1 1 0 R/W
器件地址 器件地址 读写位

表 25.1.3 AP3216C 地址格式

        AP3216C 器件地址为“0011110”即 0x1E。读操作地址就为 0x3D(0x1E << 1 | 0x1),即0011 1101;写操作地址就为 0x3C(0x1E << 1 | 0x0),即 0011 1100。

25.1.2 AP3216C 寄存器介绍

        AP3216C 有一系列寄存器,由这些寄存器来控制 AP3216C 的工作模式,以及中断配置和数据输出等。这里仅介绍在本章需要用到的一些寄存器,其他寄存器的描述和说明,参考 AP3216C 的数据手册。本章需要用到 AP3216C 的寄存器如下表所示。

地址 有效位 指令 说明
0X00 2:0 系统模式 000:掉电模式(默认)
001: ALS 功能激活
010: PS+IR 功能激活
011: ALS+PS+IR 功能激活
100:软复位
101: ALS 单次模式
110: PS+IR 单次模式
111: ALS+PS+IR 单次模式
0X0A 7 IR 低位数据

0: IR&PS 数据有效; 1:无效  

1:0 IR 最低 2 位数据
0X0B 7:0 IR 高位数据 IR 高 8 位数据
0X0C 7:0 ALS 低位数据 ALS 低 8 位数据 
0X0D 7:0 ALS 高位数据 ALS 高 8 位数据
0X0E 7 PS 低位数据 0:物体在远离; 1:物体在靠近   P
6 0: IR 数据有效; 1: IR 数据无效
3:0 PS 最低 4 位数据
0X0F 7 PS 高位数据 0:物体在远离; 1:物体在靠近    
6 0: IR 数据有效; 1: IR 数据无效
5:0 PS 高 6 位数据

表 25.1.2.1 AP3216C 相关寄存器及其说明

        上表中, 0X00 是一个系统模式控制寄存器,主要在初始化的时候配置,初始化的时候,先设置其值为 0x04,实行一次软复位,随后设置其值为0x03,开启 ALS+PS+IR 检测功能。
        剩下的 6 个寄存器为数据寄存器,输出 AP3216C 内部三个传感器所检测到的数据(ADC值),并且还存在数据有效位以便判断寄存器的数据是否是有效的,描述如上表所示。
        需要注意的是:读取间隔至少要大于 112.5ms,因为, AP3216C 内部完成一次ALS+PS+IR 的数据转换,需要 112.5ms 的时间。

25.1.3 AP3216C 时序介绍

        ESP32-S3是通过 IIC总线跟 AP3216C进行通信的,对 AP3216C相关寄存器进行写入配置,后面就是对相关数据寄存器进行读取。这里的时序主要就是写寄存器时序和读寄存器时序,一一介绍。
        写寄存器时序:
        AP3216C 的写寄存器时序如下图所示:

图 25.1.3.1 AP3216C 写寄存器时序

        图中,先发送 AP3216C 的写操作地址 0x3C(器件地址 0X1E << 1 | 读写位 0x0),随后发送8 位寄存器地址,最后发送 8 位寄存器值。其中: S,表示 IIC 起始信号; W,表示读/写标志位(W=0 表示写, W=1 表示读); A,表示应答信号; P,表示 IIC 停止信号。
        读寄存器时序图:
        AP3216C 的读寄存器时序如下图所示:

图 25.1.3.2 AP3216C 读寄存器时序

        图中,同样是先发送 AP3216C 的写操作地址 0x3C,然后再发送寄存器地址,随后,重新发送起始信号(Sr),发送 AP3216C 的读操作地址 0x3D(器件地址 0X1E << 1 | 读写位0x1),然后读取寄存器值。其中: Sr,表示重新发送 IIC起始信号; N,表示不对 AP3216C进行应答;其他简写同上。
        AP3216C 介绍到这里,关于该芯片的详细说明,参考其数据手册。

25.2 硬件设计

25.2.1 例程功能

        开机的时候先检测 AP3216C 是否存在,如检测不到 AP3216C,则在 LCD 屏上面显示报错信息。如果检测到 AP3216C,则显示正常,并在主循环里面,循环读取 ALS+PS+IR的传感器数据,并显示在 LCD 屏幕上面。

25.2.2 硬件资源
        1.USART0
                U0TXD-IO43
                U0RXD-IO44
        2.XL9555
                IIC_SDA-IO41
                IIC_SCL-IO42
        3.SPILCD
                CS-IO21
                SCK-IO12
                SDA-IO11
                DC-IO40(在 P5 端口,使用跳线帽将 IO_SET 和 LCD_DC 相连)
                PWR- IO1_3(XL9555)
                RST- IO1_2(XL9555)
        4.AP3216C
                IIC_SDA-IO41
                IIC_SCL-IO42
                AP_INT-IO0_0(XL9555)

25.2.3 原理图

        AP3216C 相关原理图,如下图所示:

图 25.2.3.1 AP3216C 原理图

        AP3216C的 AP_INT脚是连接在 XL9555器件的 IO0_0脚上,如果要使用 AP3216C 的中断输出功能,必须先初始化 XL9555 器件并配置 IO0_0 为输入功能,监测XL9555 中断引脚是否有中断产生。若发现有中断产生,则判断是否是 IO0_0 导致的,从而检测到 AP3216C 的中断。在本章中,并没有用到 AP3216C 中断功能,所以没有对 XL9555 器件的IO0_0 做设置。

25.3 程序设计

25.3.1 程序流程图
        本实验的程序流程图:

图 25.3.1.1 IIC_AP3216C 实验程序流程图

25.3.2 IIC_AP3216C 函数解析

        ESP-IDF 提供了一套 API 来配置 IIC。要使用此功能,需要导入必要的头文件:

#include "driver/i2c.h"

        关于配置 IIC 的函数在前面的章节已经详细讲明,回顾之前第十九章节的相关内容

(1)写数据

        该函数用于复位 AP3216C,并封装在 ap3216c_comfig()函数中,其函数原型如下所示:

esp_err_t ap3216c_write_one_byte(uint8_t reg, uint8_t data)

        该函数的形参描述,如下表所示:

形参 描述
reg 要写入数据的寄存器地址
data 要写入的数据

表 25.3.2.1 函数 ap3216c_write_one_byte ()形参描述

        返回值: ESP_OK 表示配置成功。其他表示配置失败。

(2)读取开启 ALS 和 PS+IR 指令

        该函数用于复位 AP3216C,并封装在 ap3216c_comfig()函数中,其函数原型如下所示:

esp_err_t ap3216c_read_one_byte(uint8_t* data, uint8_t reg)

        该函数的形参描述,如下表所示:

形参 描述
data 指向数据存储区的指针类型
reg 寄存器地址

表 25.3.2.2 函数 ap3216c_read_one_byte ()形参描述

        返回值: ESP_OK 表示复位成功。其他表示复位失败。

        该函数通过一个指向数据存储区的指针再配合一寄存器地址的方式来读取之前写入系统配置寄存器的值0x03,通过判断寄存器是否写入了0x03来决定AP3216C的初始化是否真正完成。

25.3.3 IIC_AP3216C 驱动解析

        在 IDF版 15_ap3216c 例程中,在 15_ap3216c \components\BSP 路径下新增了一个AP3216C 文件夹,分别用于存放 ap3216c.c、 ap3216c.h 这两个文件。其中, ap3216c.h 文件负责声明 AP3216C 相关的函数和变量,而 ap3216c.c 文件则实现了 AP3216C 的驱动代码。下面解析这两个文件的实现内容。

(1)ap3216c.h 文件

/* AP3216C信息 */
typedef struct _ap3216c_value_t
{
    uint16_t ir;
    uint16_t als;
    uint16_t ps;
} ap3216c_value_t;

/* 相关参数定义 */
#define AP3216C_INT     xl9555_pin_read(AP_INT_IO)
#define AP3216C_ADDR    0X1E                                        /* AP3216C地址 */

/* 函数声明 */
void ap3216c_init(i2c_obj_t self);                                  /* 初始化AP3216C */
uint8_t ap3216c_comfig(void);                                       /* 检查AP3216C */
void ap3216c_read_data(uint16_t *ir, uint16_t *ps, uint16_t *als);  /* 读取AP3216C的数据 */

        使用 IO42 作为 IIC 时钟线, IO41作为 IIC数据线, AP3216C的器件地址为 0x1E。

(2)ap3216c.c 文件

i2c_obj_t ap3216c_master;

/**
 * @brief       读取ap3216c的16位IO值
 * @param       data:存储区
 * @param       reg :寄存器
 * @retval      ESP_OK:读取成功;其他:读取失败
 */
static esp_err_t ap3216c_read_one_byte(uint8_t* data, uint8_t reg)
{
    uint8_t reg_buf[1];

    reg_buf[0] = reg;

    i2c_buf_t buf[2] = {
        {.len = 1, .buf = reg_buf},
        {.len = 1, .buf = data},
    };

    i2c_transfer(&ap3216c_master, AP3216C_ADDR, 2, buf, I2C_FLAG_WRITE | I2C_FLAG_READ | I2C_FLAG_STOP);
    return ESP_OK;
}

/**
 * @brief       向ap3216c写入16位IO值
 * @param       data:要写入的数据
 * @retval      ESP_OK:读取成功;其他:读取失败
 */
static esp_err_t ap3216c_write_one_byte(uint8_t reg, uint8_t data)
{
    uint8_t reg_buf[1];

    reg_buf[0] = reg;
    
    i2c_buf_t buf[2] = {
        {.len = 1, .buf = reg_buf},
        {.len = 1, .buf = &data},
    };

    i2c_transfer(&ap3216c_master, AP3216C_ADDR, 2, buf, I2C_FLAG_STOP); /* 传输传感器地址 */
    return ESP_OK;
}

/**
 * @brief       初始化AP3216C
 * @param       无
 * @retval      0, 成功;
                1, 失败;
*/
uint8_t ap3216c_comfig(void)
{
    uint8_t temp;
    
    ap3216c_write_one_byte(0x00, 0X04); /* 复位AP3216C */
    vTaskDelay(50);                     /* AP33216C复位至少10ms */
    ap3216c_write_one_byte(0x00, 0X03); /* 开启ALS、PS+IR */
    ap3216c_read_one_byte(&temp,0X00);  /* 读取刚刚写进去的0X03 */

    if (temp == 0X03)
    {
        ESP_LOGE("ap3216c", "AP3216C success!!!");
        return 0;                       /* AP3216C正常 */
    }
    else
    {
        ESP_LOGE("ap3216c", "AP3216C fail!!!");
        return 1;                       /* AP3216C失败 */
    }
} 

/**
 * @brief       读取AP3216C的数据
 * @note        读取原始数据,包括ALS,PS和IR
 *              如果同时打开ALS,IR+PS的话两次数据读取的时间间隔要大于112.5ms
 * @param       ir  :IR传感器值指针
 * @param       ps  :PS传感器值指针
 * @param       als :ALS传感器值指针
 * @retval      无
 */
void ap3216c_read_data(uint16_t *ir, uint16_t *ps, uint16_t *als)
{
    uint8_t buf[6] = {0};
    uint8_t i;

    for (i = 0; i < 6; i++)
    {
        ap3216c_read_one_byte(&buf[i],0X0A + i);                    /* 循环读取所有传感器数据 */
    }

    if (buf[0] & 0X80)
    {
        *ir = 0;                                                    /* IR_OF位为1,则数据无效 */
    }
    else 
    {
        *ir = ((uint16_t)buf[1] << 2) | (buf[0] & 0X03);            /* 读取IR传感器的数据   */
    }

    *als = ((uint16_t)buf[3] << 8) | buf[2];                        /* 读取ALS传感器的数据   */ 

    if (buf[4] & 0x40) 
    {
        *ps = 0;                                                    /* IR_OF位为1,则数据无效 */
    }
    else
    {
        *ps = ((uint16_t)(buf[5] & 0X3F) << 4) | (buf[4] & 0X0F);   /* 读取PS传感器的数据 */
    }
}

/**
 * @brief       初始化AP3216C
 * @param       无
 * @retval      无
 */
void ap3216c_init(i2c_obj_t self)
{
    if (self.init_flag == ESP_FAIL)
    {
        iic_init(I2C_NUM_0);    /* 初始化IIC */
    }

    ap3216c_master = self;

    while (ap3216c_comfig())    /* 检测不到AP3216C */
    {
        ESP_LOGE("ap3216c", "ap3216c init fail!!!");
        vTaskDelay(500);
    }
}

        在 ap3216c_init()函数中,通过判断 IIC 初始化标志位,确认 IIC 是否已经初始化,如果没有则进行 IIC 初始化,已经初始化了则跳过。然后把 IIC_SDA 引脚和 IIC_SCL 引脚作为I2C_NUM_0 的数据线和时钟线使用。由于 AP3216C 手册中描述到 400kHz 通信频率,所以这里直接把 I2C_NUM_0 的通信速率设为 400kHz。
        初始化好 AP3216C 传感器之后,就可以对其采集到的数据进行获取,实现这个读取过程的函数为 ap3216c_read_data,该函数用于读取 AP3216C 传感器的 ALS+PS+IR 数据。通过调用ap3216c_read_data 函数读取相关寄存器的值,然后判断数据有效位,再整合得到 IR 值、 ALS 值、 PS 值。

25.3.4 CMakeLists.txt 文件

        打开本实验 BSP 下的 CMakeLists.txt 文件,其内容如下所示:

set(src_dirs
            AP3216C
            IIC
            LCD
            LED
            SPI
            XL9555)

set(include_dirs
            AP3216C
            IIC
            LCD
            LED
            SPI
            XL9555)

set(requires
            driver)

idf_component_register(SRC_DIRS ${src_dirs} INCLUDE_DIRS ${include_dirs} REQUIRES ${requires})

component_compile_options(-ffast-math -O3 -Wno-error=format=-Wno-format)

25.3.5 实验应用代码

        打开 main/main.c 文件,该文件定义了工程入口函数,名为 app_main。该函数代码如下。

i2c_obj_t i2c0_master;

/**
 * @brief       程序入口
 * @param       无
 * @retval      无
 */
void app_main(void)
{
    uint16_t ir, als, ps;
    esp_err_t ret;
    
    ret = nvs_flash_init();                             /* 初始化NVS */

    if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND)
    {
        ESP_ERROR_CHECK(nvs_flash_erase());
        ret = nvs_flash_init();
    }

    led_init();                                         /* 初始化LED */
    i2c0_master = iic_init(I2C_NUM_0);                  /* 初始化IIC0 */
    spi2_init();                                        /* 初始化SPI2 */
    xl9555_init(i2c0_master);                           /* 初始化XL9555 */
    lcd_init();                                         /* 初始化LCD */
    ap3216c_init(i2c0_master);                          /* 初始化AP3216C */

    lcd_show_string(10, 50, 200, 16, 16, "ESP32", RED);
    lcd_show_string(10, 70, 200, 16, 16, "AP3216C TEST", RED);
    lcd_show_string(10, 90, 200, 16, 16, "ATOM@ALIENTEK", RED);

    lcd_show_string(10, 130, 200, 16, 16, "AP3216C Ready!", RED);
    lcd_show_string(10, 150, 200, 16, 16, "IR :", RED);
    lcd_show_string(10, 170, 200, 16, 16, "PS :", RED);
    lcd_show_string(10, 190, 200, 16, 16, "ALS:", RED);

    while(1)
    {
        ap3216c_read_data(&ir, &ps, &als);              /* 读取数据  */
        lcd_show_num(10 + 40, 150, ir, 5, 16, BLUE);    /* 显示IR数据 */
        lcd_show_num(10 + 40, 170, ps, 5, 16, BLUE);    /* 显示PS数据 */
        lcd_show_num(10 + 40, 190, als, 5, 16, BLUE);   /* 显示ALS数据  */

        LED_TOGGLE();                                   /* 提示系统正在运行 */
        vTaskDelay(200);
    }
}

        在 app_main 函数中,完成一系列外设初始化后,调用 ap3216c_init 函数完成 AP3216C 初始化, LCD 显示 IR、 PS、 ALS 信息。在 while 循环中,间隔 200 毫秒调用 ap3216c_read_data 函数获取 AP3216C 传感器的 IR、 PS 和 ALS 数据,然后在 LCD 进行显示。

25.4 下载验证

        程序下载成功后, LCD 会显示光环境传感器的 ALS+PS+IR 数据,同时, LED 闪烁,提示系统正在运行,并且串口打印实验现象。
        用手遮挡/靠近 AP3216C传感器,可以看到三个传感器的数据变化,说明代码正常。

         用手靠近传感器:



    图 25.4.1 用手靠近传感器显示效果图

         用手遮挡传感器:

图 25.4.2 用手遮挡传感器显示效果图

Logo

码道开发者社区,聚焦华为云码道 CodeArts 代码智能体,沉淀 Agent、Skill、鸿蒙开发实战内容,供开发者查阅资料、交流技术、分享工程实践

更多推荐