正点原子 发表于 2024-8-20 15:14:35

《ESP32-S3使用指南—MicroPython版 V1.0》第十四章 IO扩展实验


1)实验平台:正点原子ESP32S3开发板
2)购买链接:https://detail.tmall.com/item.htm?id=768499342659
3)全套实验源码+手册+视频下载地址:http://www.openedv.com/thread-347618-1-1.html
4)正点原子官方B站:https://space.bilibili.com/394620890
5)正点原子手把手教你学ESP32S3快速入门视频教程:https://www.bilibili.com/video/BV1sH4y1W7Tc
6)正点原子FPGA交流群:132780729


第十四章 IO扩展实验

       在正点原子ESP32-S3开发板中,搭载了一颗16位的IO扩展芯片,该芯片主要用于控制输出和获取用户的输入,然后通过IIC协议传输至MCU内核。在本章中,我们将介绍如何通过自编写MicroPython模块来驱动该芯片,并实现按键控制蜂鸣器和LED开关状态的功能。
       14.1 IIC简介&XL9555简介
       14.2 XL9555 C模块解析
       14.3 硬件设计
       14.4 软件设计
       14.5 下载验证

       14.1 IIC简介 &XL9555简介

       14.1.1 IIC简介
       IIC(Inter-Integrated Circuit)总线是一种由PHILIPS公司开发的两线式串行总线,用于连接微控制器以及其外围设备。它是由数据线SDA和时钟线SCL构成的串行总线,可发送和接收数据,在CPU与被控IC之间、IC与IC之间进行双向传送。
       IIC总线有如下特点:

       ①总线由数据线SDA和时钟线SCL构成的串行总线,数据线用来传输数据,时钟线用来同步数据收发。

       ②总线上每一个器件都有一个唯一的地址识别,所以我们只需要知道器件的地址,根据时序就可以实现微控制器与器件之间的通信。

       ③数据线SDA和时钟线SCL都是双向线路,都通过一个电流源或上拉电阻连接到正的电压,所以当总线空闲的时候,这两条线路都是高电平。

       ④总线上数据的传输速率在标准模式下可达100kbit/s在快速模式下可达400kbit/s在高速模式下可达3.4Mbit/s。

       ⑤总线支持设备连接。在使用IIC通信总线时,可以有多个具备IIC通信能力的设备挂载在上面,同时支持多个主机和多个从机,连接到总线的接口数量只由总线电容400pF的限制决定。IIC总线挂载多个器件的示意图,如下图所示。

图14.1.1.1 IIC总线挂载多个器件
       下面来学习IIC总线协议,IIC总线时序图如下所示:

图14.1.1.2 IIC总线时序图
       为了便于大家更好的了解IIC协议,我们从起始信号、停止信号、应答信号、数据有效性、数据传输以及空闲状态等6个方面讲解,大家需要对应图17.1.1.2的标号来理解。

       ① 起始信号
       当SCL为高电平期间,SDA由高到低的跳变。起始信号是一种电平跳变时序信号,而不是一个电平信号。该信号由主机发出,在起始信号产生后,总线就处于被占用状态,准备数据传输。

       ② 停止信号
       当SCL为高电平期间,SDA由低到高的跳变。停止信号也是一种电平跳变时序信号,而不是一个电平信号。该信号由主机发出,在停止信号发出后,总线就处于空闲状态。

       ③ 应答信号
       发送器每发送一个字节,就在时钟脉冲9期间释放数据线,由接收器反馈一个应答信号。 应答信号为低电平时,规定为有效应答位(ACK简称应答位),表示接收器已经成功地接收了该字节;应答信号为高电平时,规定为非应答位(NACK),一般表示接收器接收该字节没有成功。
       观察上图标号③就可以发现,有效应答的要求是从机在第9个时钟脉冲之前的低电平期间将SDA线拉低,并且确保在该时钟的高电平期间为稳定的低电平。如果接收器是主机,则在它收到最后一个字节后,发送一个NACK信号,以通知被控发送器结束数据发送,并释放SDA线,以便主机接收器发送一个停止信号。

       ④ 数据有效性
       IIC总线进行数据传送时,时钟信号为高电平期间,数据线上的数据必须保持稳定,只有在时钟线上的信号为低电平期间,数据线上的高电平或低电平状态才允许变化。数据在SCL的上升沿到来之前就需准备好。并在下降沿到来之前必须稳定。

       ⑤ 数据传输
       在I2C总线上传送的每一位数据都有一个时钟脉冲相对应(或同步控制),即在SCL串行时钟的配合下,在SDA上逐位地串行传送每一位数据。数据位的传输是边沿触发。

       ⑥ 空闲状态
       IIC总线的SDA和SCL两条信号线同时处于高电平时,规定为总线的空闲状态。此时各个器件的输出级场效应管均处在截止状态,即释放总线,由两条信号线各自的上拉电阻把电平拉高。
       了解前面的知识后,下面介绍一下IIC的基本的读写通讯过程,包括主机写数据到从机即写操作,主机到从机读取数据即读操作。下面先看一下写操作通讯过程图,如下图所示.

图14.1.1.3 写操作通讯过程图
       主机首先在IIC总线上发送起始信号,那么这时总线上的从机都会等待接收由主机发出的数据。主机接着发送从机地址+0(写操作)组成的8bit数据,所有从机接收到该8bit数据后,自行检验是否是自己的设备的地址,假如是自己的设备地址,那么从机就会发出应答信号。主机在总线上接收到有应答信号后,才能继续向从机发送数据。注意:IIC总线上传送的数据信号是广义的,既包括地址信号,又包括真正的数据信号。
       接着讲解一下IIC总线的读操作过程,先看一下读操作通讯过程图,如下图所示。

图14.1.1.4 读操作通讯过程图
       主机向从机读取数据的操作,一开始的操作与写操作有点相似,观察两个图也可以发现,都是由主机发出起始信号,接着发送从机地址+1(读操作)组成的8bit数据,从机接收到数据验证是否是自身的地址。 那么在验证是自己的设备地址后,从机就会发出应答信号,并向主机返回8bit数据,发送完之后从机就会等待主机的应答信号。假如主机一直返回应答信号,那么从机可以一直发送数据,也就是图中的(n byte + 应答信号)情况,直到主机发出非应答信号,从机才会停止发送数据。
       24C02的数据传输时序是基于IIC总线传输时序,下面讲解一下24C02的数据传输时序。

       14.1.2 XL9555简介
       XL9555是信路达推出的一款24引脚CMOS器件,通过I²C总线/SMBus接口(串行时钟线(SCL)、串行数据线(SDA))为大多数微控制器系列提供16位通用并行输入/输出(GPIO)扩展。这些设备设计用于2.3-V至5.5-V VCC操作。这些改进包括更高的驱动能力、5V I/O容差、更低的电源电流、单独的I/O配置和更小的封装。当ACPI电源开关、传感器、按钮、LED、风扇等需要额外的I/O时,I/O扩展器提供了一个简单的解决方案。当任何输入状态与其对应的输入端口寄存器状态不同时,这些设备开漏中断(/INT)输出被激活,并用于向系统主机指示输入状态已改变。在超时或其他不当操作的情况下,系统主设备可以利用上电重置功能重置这些设备。通电重置将寄存器设置为默认值,并初始化I²C/SMBus状态机。三个硬件引脚(A0、A1和A2)改变固定的I²C总线地址,并允许多达八个设备共享同一I²C总线/SMBus。
       XL9555有如下特性:

       I²C总线至16位GPIO扩展器

       工作电源电压范围为2.3 V至5.5 V

       低待机电流消耗

       5 V容错I/O端口

       400 kHz快速模式I²C总线时钟频率

       SCL/SDA输入上的噪声滤波器

       内部通电复位

       通电时无故障

       极性反转寄存器

       开漏有源低中断输出

       16个I/O引脚,默认为16个输入

       1,引脚说明
       XL9555的引脚说明如下表所示。

表14.1.2.1 XL9555引脚说明
       我们使用的XL9555采用QFN24封装,总共24个脚,其中包括:16个准双向IO口(P00~P07,P10~P17)、3个地址线(A0~A2)、SCL、SDA、INT、GND和Vcc。每个XL9555只需要最少2个IO口,就可以扩展16路IO,在MCU的IO不够用的时候,XL9555是一个非常不错的IO扩展方案。

       2,寻址
       一个IIC总线上,可以挂多个XL9555(通过A0~A2寻址),XL9555的从机地址格式如下图所示。

图14.1.2.1 XL9555从机地址格式
       图中的A0~A2为XL9555的寻址信息,我们开发板上A0~A2都是接GND的,所以,XL9555的地址为:0X20(左移了一位);R/W为读/写控制位,R/W=0的时候,表示写数据到XL9555,用来使IO输出电平;R/W=1的时候,表示读取XL9555的数据,获取IO口的状态。相关内容,请参考《XL9555.pdf》规格书。

       3,芯片寄存器地址
       在成功确认地址字节之后,总线主机发送一个命令,该命令指向存储在设备的控制XL9555寄存器中的字节。这个数据的低三位表示操作不同的寄存器,例如,操作读和写和内部寄存器(输入、输出、极性反转和配置)。如下图所示。

图14.1.2.2 芯片寄存器地址
       从上图可以看出,“Input Port0”和“Input Port1”寄存器用于获取端口0和端口1的IO输入状态;“Output Port0”和“Output Port1”寄存器用于配置端口0和端口1的IO输出电平;“Polarity Inversion Port0”和“Polarity Inversion Port1”用于对端口0和端口1进行极性翻转,例如,如果P00输入的电平为低电平,那么XL9555将对它进行极性翻转,因此MCU获得的是高电平状态,同理,输出一样的原理;“Configuration Port1”和“Configuration Port0”寄存器则用于配置端口0和端口1的IO输入/输出模式。
       值得注意的是,“Configuration Port1”和“Configuration Port0”寄存器配置需结合实际的原理图来设置,如下图所示。

图14.1.2.3 XL9555硬件原理图
       从上图可以看到,IO0_0~IO0_1和IO1_4~IO1_7被用作输入IO,其他管脚被用作输出IO。根据这些信息,我们使用寄存器6和寄存器7来配置端口0和端口1的IO输入/输出状态。

       3,配置IO输入输出模式
       XL9555的配置输入输出模式的写数据时序如下图所示。

图14.1.2.4 寄存器6和寄存器7写数据流程
       由图可知,XL9555操作寄存器6和寄存器7的数据写入非常简单,首先发送XL9555的从机地址+写信号(R/W=0),然后等待XL9555的应答信号,在应答成功后,发送数据(DATA0:用来配置寄存器6的IO输入输出模式)给XL9555就可以了,发送完数据,会收到XL9555的应答信号,接着,再一次发送数据(DATA1:用来配置寄存器7的IO输入输出模式)并且接收到XL9555的应答信号,最后就是IIC结束信号。

       4,写数据(输出状态)
       XL9555的配置输出写数据时序如下图所示。

图14.1.2.5 寄存器2和寄存器3写数据流程
       由图可知,首先发送XL9555的从机地址+写信号(R/W=0),然后等待XL9555的应答信号,在应答成功后,发送数据(DATA0:用来配置寄存器2的IO输出状态)给XL9555就可以了,发送完数据,会收到XL9555的应答信号,接着,再一次发送数据(DATA1:用来配置寄存器3的IO输出状态)并且接收到XL9555的应答信号,最后就是IIC结束信号。

       5,读数据(输入状态)
       XL9555的读数据时序如下图所示:




       XL9555的读数据流程:首先发送XL9555的从机地址+读信号(R/W=1),然后等待XL9555应答(注意:XL9555在发送应答的同时,会锁存P00~P07和P10~P17的数据),然后读取P00~P07的数据,再读取P10~P17的数据。数据读取支持连续读取,在最后的时候发送STOP信号,即可完成读数据操作。

       6,中断
       XL9555带有中断输出脚,它可以连接到MCU的中断输入引脚上。在输入模式中(IO口输出高电平,即可做输入使用),输入的上升沿或下降沿都可以产生中断,在tiv时间之后INT有效。特别注意:初始化XL9555以及一旦中断有效后,必须对XL9555进行一次读取/写入操作,复位中断,才可以输出下一次中断,否则中断将一直保持(无法输出下一次输入信号变化所产生的中断)。

       14.2 XL9555 C模块解析

       14.2.1 C模块解析
       在先前的章节里,作者已经详述了C模块的添加流程以及整体架构,接下来作者将简要介绍正点原子XL9555 C模块驱动。这个讲解内容会分为几个部分:XL9555构造函数、写入数据、读取数据。XL9555 C模块驱动可在A盘6,软件资料1,软件2,MicroPython开发工具01-Windows2,正点原子MicroPython驱动CModules_LibIIC路径下找到。

       1,XL9555构造函数
/* xl9555结构体 */
typedef struct _xl9555_obj_t
{
    mp_obj_base_t   base;       /* 基地址 */
    mp_obj_base_t   *iic_obj;   /* 指向IIC控制块 */
} xl9555_obj_t;

mp_obj_t xl9555_make_new(const mp_obj_type_t *type,size_t n_args,
size_t n_kw,const mp_obj_t *all_args )
{
    /* 创建对象的参数 */
    enum
    {
      ARG_iic,
    };

    static const mp_arg_t allowed_args[] = {
      { MP_QSTR_iic, MP_ARG_OBJ | MP_ARG_REQUIRED, {.u_obj = MP_OBJ_NULL} },
    };
    mp_arg_val_t args;
mp_arg_parse_all_kw_array(n_args, n_kw, all_args,
MP_ARRAY_SIZE(allowed_args), allowed_args, args);

    /* 创建对象 */
    xl9555_self = m_new_obj(xl9555_obj_t);
    xl9555_self->base.type = &xl9555_type;
    /* 设置对象参数 */
mp_obj_base_t *xl9555_obj=
(mp_obj_base_t*)MP_OBJ_TO_PTR(args.u_obj);

    if (xl9555_obj == MP_OBJ_NULL)
    {
      mp_raise_ValueError(MP_ERROR_TEXT("I2C init ???"));
    }

    xl9555_self->iic_obj      = xl9555_obj;

    if (xl9555_obj == MP_OBJ_NULL)
    {
      mp_raise_ValueError(MP_ERROR_TEXT("I2C init ???"));
    }
      /* 初始化XL9555 */
    xl9555_init();
   
    return MP_OBJ_FROM_PTR(xl9555_self);
}       从上述源代码中可以得知,该构造函数只有一个参数,即传入IIC驱动的控制块。我们可以通过这个控制块调用IIC驱动下的收发函数。然后,我们还创建了一个XL9555对象,用于实例化对象并引用类的方法。最后,调用了xl9555_init函数来初始化XL9555 IO扩展芯片。以下是该初始化代码的示例:
/**
* @brief       XL9555初始化
* @param       无
* @retval      无
*/
void xl9555_init(void)
{
    /* 上电先读取一次清除中断标志 */
    uint8_t r_data;
    xl9555_read_byte(r_data, 2);
   
    /* 配置XL9555端口,即写配置寄存器,数据格式为:地址 + CMD + 6 input + 12 output */
    xl9555_ioconfig(0xF003);
    xl9555_pin_write(BEEP_IO,1);
    xl9555_pin_write(SPK_EN_IO,1);
}

/**
* @brief       XL9555的IO配置
* @param       config_value:配置数值
* @retval      返回配置数值
*/
static uint16_t xl9555_ioconfig(uint16_t config_value)
{
    /* 从机地址 + CMD + data1(P0) + data2(P1) */
/* P00、P01、P14、P15、P16、P17为输入,其他引脚为输出
-->1111 0000 0000 0011 .注意:0为输出,1为输入*/
    uint8_t data;
    esp_err_t err;
    int retry = 3;

    data = (uint8_t)(0xFF & config_value);
    data = (uint8_t)(0xFF & (config_value >> 8));

    do
    {
      err = xl9555_write_byte(XL9555_CONFIG_PORT0_REG, data, 2);
      
      if (err != ESP_OK)
      {
            retry--;
            mp_hal_delay_ms(100);
            ESP_LOGE("IIC", "%s configure %X failed, ret: %d",
__func__, config_value, err);
            xl9555_failed = 1;
            
            if ((retry <= 0) && xl9555_failed)
            {
                mp_hal_delay_ms(5000);
                esp_restart();
            }
      }
      else
      {
            xl9555_failed = 0;
            break;
      }
      
    } while (retry);
   
    return config_value;
}       我们可以看到,首先,作者上电先读取一次数据清除中断标志,然后调用xl9555_ioconfig函数配置P0端和P1端的管脚输入输出模式,最后,调用xl9555_pin_write函数关闭蜂鸣器和喇叭功能。

       2,XL9555写时序
/**
* @brief       向XL9555写入16位IO值
* @param       data:存储区
* @param       len :读取数据大小
* @retval      ESP_OK:读取成功;其他:读取失败
*/
esp_err_t xl9555_write_byte(uint8_t reg,uint8_t* data, size_t len)
{
    int data_len = 0;
    mp_obj_base_t *self = (mp_obj_base_t *)MP_OBJ_TO_PTR(xl9555_self->iic_obj);
mp_machine_i2c_p_t *i2c_p =
(mp_machine_i2c_p_t *)MP_OBJ_TYPE_GET_SLOT(self->type, protocol);

    mp_machine_i2c_buf_t bufs = {
      {.len = 1, .buf = ®},
      {.len = len, .buf = data},
    };

data_len = i2c_p->transfer(self, XL9555_ADDR,2,
bufs, MP_MACHINE_I2C_FLAG_STOP);
   
    if (data_len != 0)
    {
      return ESP_OK;
    }
    else
    {
      return ESP_FAIL;
    }
}       在上述源代码中,作者根据传入的IIC控制块,调用了IIC收发函数来发送XL9555的命令和数据。发送完成后,函数返回了ESP_OK状态。

       3,XL9555读时序
/**
* @brief       读取XL9555的16位IO值
* @param       data:存储区
* @param       len :读取数据大小
* @retval      ESP_OK:读取成功;其他:读取失败
*/
esp_err_t xl9555_read_byte(uint8_t* data, size_t len)
{
    int data_len = 0;
    uint8_t memaddr_buf;
    memaddr_buf= XL9555_INPUT_PORT0_REG;
    mp_obj_base_t *self = (mp_obj_base_t *)MP_OBJ_TO_PTR(xl9555_self->iic_obj);
mp_machine_i2c_p_t *i2c_p =
(mp_machine_i2c_p_t *)MP_OBJ_TYPE_GET_SLOT(self->type, protocol);

    mp_machine_i2c_buf_t bufs = {
      {.len = 1, .buf = memaddr_buf},
      {.len = len, .buf = data},
    };

data_len = i2c_p->transfer(self, XL9555_ADDR, 2, bufs,
MP_MACHINE_I2C_FLAG_WRITE1
| MP_MACHINE_I2C_FLAG_READ
| MP_MACHINE_I2C_FLAG_STOP);
   
    if (data_len != 0)
    {
      return ESP_OK;
    }
    else
    {
      return ESP_FAIL;
    }
}       同样地,XL9555的读时序也是利用IIC收发函数来实现的。写时序和读时序的唯一区别在于最后的flag标志位不同,从而导致发送流程有所不同。如果读者想了解i2c_p->transfer函数的收发流程,可以在MicroPython源代码中找到machine_i2c.c文件(位于micropython\ports\esp32路径下)。

       4,控制某个IO的状态
/**
* @brief       控制所有IO的电平
* @param       reg_value:寄存器的数值
* @retval      返回16位IO状态
*/
uint16_t xl9555_multi_write_ex(uint16_t reg_value)
{
    uint8_t w_data;
    esp_err_t err;
    int retry = 3;

    w_data = (uint8_t)(0xFF & reg_value);
    w_data = (uint8_t)(0xFF & (reg_value >> 8));

    do
    {
      /* 控制某个IO的电平 */
      err = xl9555_write_byte(XL9555_OUTPUT_PORT0_REG, w_data, 2);
      
      if (err != ESP_OK)
      {
            retry--;
            mp_hal_delay_ms(100);
            ESP_LOGE("IIC", "%s failed, prev:%X, target: %X",
__func__, xl9555_value, reg_value);
            xl9555_failed = 1;
      }
      else
      {
            xl9555_failed = 0;
            break;
      }
      
    } while (retry);

    if (xl9555_failed == 0)
    {
      xl9555_value = reg_value;
    }
   
    return xl9555_value;
}

/**
* @brief       控制某个IO的电平
* @param       pin:控制的IO
* @param       val:电平
* @retval      返回16位IO状态
*/
uint16_t xl9555_pin_write(uint16_t pin, int val)
{
    uint16_t i;
    uint16_t wrt_data = xl9555_value;
    uint16_t bitval = (val > 0) ? 1 : 0;
    uint16_t temp;

    for (i = 0; i < 16; i++)
    {
      temp = 0x1 << i;

      if (pin & temp)
      {
            if (val) wrt_data |= temp;
            else wrt_data &= ~temp;
      }
    }

    return xl9555_multi_write_ex(wrt_data);
}       上述源码中,作者先判断控制哪个IO,然后调用xl9555_multi_write_ex函数控制这个IO的电平输出。

       5,读取IO状态
/**
* @brief       读取XL9555的所有IO状态
* @param       无
* @retval      返回16位IO状态
*/
uint16_t xl9555_multi_read_ex(void)
{
    uint16_t ret;
    esp_err_t err;
    uint8_t r_data;
    int retry = 3;

    do
    {
      err = xl9555_read_byte(r_data, 2);
      
      if (err != ESP_OK)
      {
            retry--;
            mp_hal_delay_ms(100);
            ESP_LOGE("IIC", "%s failed, prev:%X, target: %X",
__func__, xl9555_value, xl9555_readonly_value);
            xl9555_value = 1;
      }
      else
      {
            xl9555_value = 0;
            break;
      }
      
    } while (retry);

    ret = r_data << 8 | r_data;

    xl9555_readonly_value = ret;
    return ret;
}

/**
* @brief       获取某个IO状态
* @param       pin:要获取状态的IO
* @retval      返回IO口的状态(0/1)
*/
int xl9555_pin_read(uint16_t pin)
{
    return (xl9555_multi_read_ex() & pin) ? 1 : 0;
}       从上述源代码中可以得知,作者首先调用了xl9555_multi_read_ex函数来读取所有IO的状态。然后,将这十六位的数值与读取的IO数值进行比较。如果数值为0,则输出为低电平;反之,则为高电平。

       14.2.2 C模块构造与类的方法

       1,atk_xl9555类与IIC构造函数
       xl9555和IIC的构造对象方法如下:
class machine.I2C(id, scl, sda, freq=400000)
class atk_xl9555.init(iic)
使用示例:
i2c0 = I2C(0, scl = Pin(42), sda = Pin(41), freq = 400000)
xl9555 = atk_xl9555.init(i2c0)
       该构造方法的参数描述,如下表所示。

表14.2.2.1 IIC和XL9555构造函数参数描述
       返回值:IIC和XL9555对象。

       2,xl9555类的方法

       ①:控制某个扩展IO输出电平
       其方法原型如下:
xl9555.write_bit(pin, value)       该函数的参数描述,如下表所示。

表14.2.2.2 xl9555.write_bit方法参数描述
       返回值:无。

       ②:读取某个扩展IO电平。
       其方法原型如下:
xl9555.read_bit(pin)       该方法的参数描述,如下表所示。

表14.2.3.3 xl9555.write_bit方法参数描述
       返回值:读取IO的电平值

       ③:按键扫描。
       其方法原型如下:
xl9555.key_scan()       返回值:按下的按键值。

       14.3 硬件设计

       1. 例程功能
       本章实验功能简介:通过按下KEY0~4按键来控制蜂鸣器和LED灯开关状态,KEY0和KEY1控制蜂鸣器开与关;KEY2和KEY3控制LED灯开与关。

       2. 硬件资源
       1)LED灯
              LED-IO1

       2)XL9555
              IIC_INT-IO0(需在P5连接IO0)
              IIC_SDA-IO41
              IIC_SCL-IO42
              IO_17-KEY0
              IO_16-KEY1
              IO_15-KEY2
              IO_14-KEY3

       3. 原理图
       XL9555、独立按键和蜂鸣器硬件部分的原理图,如下图所示。


图14.3.1 XL9555、独立按键和蜂鸣器原理图
       这里需要注意的是:独立按键设计为采样到按键另一端的低电平为有效电平。

       14.4 软件设计

       14.4.1 程序流程图
       程序流程图能帮助我们更好的理解一个工程的功能和实现的过程,对学习和设计工程有很好的主导作用。下面看看本实验的程序流程图:

图14.4.1.1 程序流程图
       14.4.2 程序解析
       本书籍的代码都在main.py脚本下编写的,读者可在光盘资料下找到对应的源码。IO扩展实验main.py源码如下:
from machine import Pin,I2C
import atk_xl9555 as io_ex
import time


"""
* @brief       程序入口
* @param       无
* @retval      无
"""
if __name__ == '__main__':
   
    # 初始化LED并输出高电平
    led = Pin(1,Pin.OUT,value = 1)
    # IIC初始化
    i2c0 = I2C(0, scl = Pin(42), sda = Pin(41), freq = 400000)
    # XL9555初始化
    xl9555 = io_ex.init(i2c0)
   
    while True:
      
      # 获取按键值
      key = int(xl9555.key_scan())
      
      if key == io_ex.KEY0:
            xl9555.write_bit(io_ex.BEEP,0)      # 打开蜂鸣器
      elif key == io_ex.KEY1:
            xl9555.write_bit(io_ex.BEEP,1)      # 关闭蜂鸣器
      elif key == io_ex.KEY2:
            led.value(0)                        # 打开LED
      elif key == io_ex.KEY3:
            led.value(1)

      time.sleep_ms(10)                     # 延时10ms       这示例代码的功能是:使用I2C通信协议,通过操作XL9555芯片来控制LED灯和蜂鸣器的状态。主程序进入无限循环,不断地扫描XL9555芯片的按键输入。如果KEY0被按下,则打开蜂鸣器;如果KEY1被按下,则关闭蜂鸣器;如果KEY2被按下,则关闭LED灯;如果KEY3被按下,则打开LED灯。每次按键操作后,程序会暂停10毫秒,以等待下一次按键输入。

       14.5 下载验证
       下载代码完成后,按键被按下时,程序会执行特定的操作。例如,如果按下KEY0,程序会启动蜂鸣器;如果按下KEY1,程序会关闭蜂鸣器;如果按下KEY2,程序会关闭LED灯;如果按下KEY3,程序会打开LED灯。
页: [1]
查看完整版本: 《ESP32-S3使用指南—MicroPython版 V1.0》第十四章 IO扩展实验