【正点原子STM32】IIC-IO扩展实验(PCF8574(IO扩展芯片)I²C总线接口的8位并行I/O端口扩展器、PCF8574寻址、写/读操作时序、中断引脚、PCF8574驱动步骤)

2024-05-09 1230阅读

一、PCF8574简介

二、PCF8574读写时序

  • 2.1、PCF8574寻址
  • 2.2、写操作时序
  • 2.3、读操作时序
  • 2.4、PCF8574中断引脚

    三、PCF8574驱动步骤

    四、编程实战

    五、总结

    一、PCF8574简介

    【正点原子STM32】IIC-IO扩展实验(PCF8574(IO扩展芯片)I²C总线接口的8位并行I/O端口扩展器、PCF8574寻址、写/读操作时序、中断引脚、PCF8574驱动步骤)

    PCF8574T是由恩智浦(NXP)半导体生产的I²C总线接口的8位并行I/O端口扩展器。该芯片的主要特性与功能包括:

    • I²C总线接口:PCF8574T通过I²C总线(也称作IIC总线)与主控制器(如MCU)通信,仅需占用两根信号线(SCL和SDA),即可实现与主控制器之间的数据传输。

    • 8个准双向口:芯片内含8个准双向I/O口(P0-P7),每个端口既可以作为输出端口驱动外部负载,也可以作为输入端口读取外部设备的状态。当作为输入口使用时,为了获得正确的输入状态,需要将对应的内部输出驱动设置为高电平(上拉),这样在外部设备不驱动的情况下,端口就能通过内部上拉电阻检测到高电平输入。

    • 中断线:PCF8574T还有一个中断输出引脚(INT),当任一输入口的状态发生变化时,可以触发此中断信号,告知主控制器有输入状态的变化,方便系统做出及时响应。

    • 3个地址线:通过不同的地址线配置,可以实现多个PCF8574T芯片挂在同一个I²C总线上,并且每个芯片都有独立的地址,使得主控制器可以区别并单独访问每个芯片。

      综上所述,PCF8574T芯片非常适用于需要扩展I/O资源的嵌入式系统,特别适合在I/O口有限或者布线困难的场合,通过一根I²C总线就可轻松管理和控制大量的外围设备。

      【正点原子STM32】IIC-IO扩展实验(PCF8574(IO扩展芯片)I²C总线接口的8位并行I/O端口扩展器、PCF8574寻址、写/读操作时序、中断引脚、PCF8574驱动步骤)

      二、PCF8574读写时序

      2.1、PCF8574寻址

      【正点原子STM32】IIC-IO扩展实验(PCF8574(IO扩展芯片)I²C总线接口的8位并行I/O端口扩展器、PCF8574寻址、写/读操作时序、中断引脚、PCF8574驱动步骤)

      PCF8574的寻址方式基于I²C协议,其设备地址由固定的7位地址和可选的硬件选择位组成:

      • 固定位:PCF8574的固定部分地址是0100000,十六进制表示为0x20。

      • 硬件选择位:PCF8574提供了A0、A1和A2三个硬件地址选择引脚,这三个引脚的不同组合可以决定PCF8574的硬件地址。当A0、A1、A2都接地(接GND)时,这三个地址位均为0,因此硬件选择位为000。

      • 数据传输方向位:这是I²C协议中的约定,最低位决定了数据传输的方向,0表示写操作,1表示读操作。

        所以,当A0~2都接GND时,具体寻址方式如下:

        • 写操作地址:固定位0100000加上硬件选择位000,再加上写操作位0,最终得到的地址是01000000,即0x40。

        • 读操作地址:与写操作类似,只是最后一位变成1,所以读操作地址为010000001,即0x41。

          当A0、A1、A2引脚接法不同时,设备地址也会随之改变。例如,如果A0接VCC(高电平),A1和A2接GND,那么写操作地址将是0x42,读操作地址将是0x43。通过这种方式,同一I²C总线上可以连接多个PCF8574芯片,每个芯片具有唯一的地址,从而实现I/O口的灵活扩展。

          2.2、写操作时序

          【正点原子STM32】IIC-IO扩展实验(PCF8574(IO扩展芯片)I²C总线接口的8位并行I/O端口扩展器、PCF8574寻址、写/读操作时序、中断引脚、PCF8574驱动步骤)

          在使用I²C协议对PCF8574进行写操作时,时序如下:

          1. MCU发起写操作:

            • S(起始信号 Start): MCU首先通过I²C总线发送起始信号,即在SCL为高电平时,SDA线由高电平跳变为低电平,表明一次通信的开始。
            • 从机地址和写操作指示:MCU紧接着发送PCF8574的从机地址(考虑到A0~A2引脚的接法,确定具体的7位地址,这里是0x40),并在末尾添加一个位来指示写操作(通常是0,因为在I²C协议中,写操作地址的最低位为0)。
            • PCF8574响应:

              • 应答信号 Acknowledge:PCF8574接收到正确的从机地址和写命令后,在下一个SCL时钟周期的高电平时,将SDA线拉低,表示对MCU请求的确认(ACK)。
              • MCU发送控制端口数据:

                • 数据传输:MCU开始通过SDA线发送要写入PCF8574的8位数据(DATA),这8位数据代表了PCF8574的P0~P7八个引脚的电平状态(0代表低电平,1代表高电平)。
                • PCF8574响应并更新输出:

                  • 应答信号 Acknowledge:同样,PCF8574在接收到8位数据后,在下一个SCL时钟周期的高电平期间再次拉低SDA线,给出ACK信号,表示数据接收成功。
                  • 内部数据更新:PCF8574内部会立即将接收到的数据更新到对应的I/O引脚上,控制P0~P7的电平状态。
                  • MCU终止通信:

                    • P(停止信号 Stop): MCU在数据传输完毕后,会发送一个停止信号来结束此次通信。这表现为在SCL为高电平时,MCU将SDA线由低电平切换回高电平。

          总结一下整个写操作时序,MCU通过I²C总线向PCF8574发送指令,指定写操作和数据内容,PCF8574接收并响应,最后将数据反映在其输出引脚P0~P7上。

          2.3、读操作时序

          【正点原子STM32】IIC-IO扩展实验(PCF8574(IO扩展芯片)I²C总线接口的8位并行I/O端口扩展器、PCF8574寻址、写/读操作时序、中断引脚、PCF8574驱动步骤)

          在使用I²C协议对PCF8574进行读操作时,时序如下:

          1. MCU发起读操作:

            • S(起始信号 Start): MCU首先通过I²C总线发送起始信号。
            • 从机地址和读操作指示:MCU紧接着发送PCF8574的从机地址,但由于现在是读操作,所以在地址的最低位上,MCU需要发送1(在写操作时是0),即从机地址加1,本例中为0x41。
            • PCF8574响应并返回数据:

              • 应答信号 Acknowledge:PCF8574接收到正确的从机地址和读命令后,在下一个SCL时钟周期的高电平期间,将SDA线拉低,表示对MCU请求的确认(ACK)。
              • 发送端口状态数据:在MCU释放SDA线后,PCF8574开始通过SDA线发送当前P0~P7八个引脚的电平状态(DATA1,8位数据)。
              • MCU读取并响应数据:

                • 读取数据:MCU在接下来的8个SCL时钟周期里,逐位读取PCF8574返回的8位数据(DATA1)。
                • 应答信号 Acknowledge:在读取每个字节数据后,如果MCU希望继续读取下一个字节(例如,如果PCF8574支持连续读取),则MCU会在最后一个数据位之后的SCL高电平期间拉低SDA线,给出ACK信号。如果不需要继续读取,则MCU会保持SDA线为高电平,给出NACK信号。
                • MCU终止读操作:

                  • 停止信号 Stop:如果MCU已经在合适的时机给出了NACK信号,或者已经完成了连续读取的所有数据,它将在最后一个数据字节读取完毕后,在SCL为高电平时将SDA由低电平切换回高电平,发出停止信号(Stop),从而结束此次读操作。

          注意,原描述中的“③ MCU响应”并不准确,因为MCU在这个环节实际上是接收和读取数据,而非响应。此外,“③ MCU响应,PCF8574锁存端口状态数据返回给MCU(A + DATA4(响应时P0~P7的电平状态 ))”这部分描述中,DATA4的概念在这里并不适用,因为通常只会读取一个字节的数据(DATA1),除非PCF8574支持连续读取,并且MCU给出了连续读取的ACK信号。如果确实支持连续读取,那么DATA4指的是第二个读取的字节数据。

          2.4、PCF8574中断引脚

          【正点原子STM32】IIC-IO扩展实验(PCF8574(IO扩展芯片)I²C总线接口的8位并行I/O端口扩展器、PCF8574寻址、写/读操作时序、中断引脚、PCF8574驱动步骤)

          PCF8574T中的中断引脚(INT)提供了一种高效的通知机制,使得微控制器(MCU)无需通过耗时的I²C总线通信就可以得知I/O端口状态的变化。当使用PCF8574T作为远程I/O扩展器时,其8个端口可以配置成输入或输出。当这些端口被配置为输入时,其中任意一个端口状态发生改变(比如从低电平变为高电平,或从高电平变为低电平),INT引脚就会产生一个中断信号,即INT引脚被拉低到低电平。

          上电初始化后,所有端口默认处于输入模式,并且所有输入引脚的内部上拉电阻使这些引脚默认处于高电平状态。当任何一个输入引脚的电平发生改变(上升沿或下降沿触发,取决于芯片的具体配置),INT引脚立即产生一个中断请求。

          特别指出的是,当INT引脚产生中断后,MCU必须通过I²C总线对PCF8574T进行一次读取或写入操作,以清除中断标志,也就是所谓的中断复位。如果不这样做,INT引脚将保持在低电平状态,不会响应下一次的输入信号变化产生的中断请求。换句话说,MCU在接收到中断后,除了响应中断事件外,还需要执行必要的I²C通信操作,以确保中断系统能够正常循环工作,持续监测并报告新的输入状态变化。

          三、PCF8574驱动步骤

          【正点原子STM32】IIC-IO扩展实验(PCF8574(IO扩展芯片)I²C总线接口的8位并行I/O端口扩展器、PCF8574寻址、写/读操作时序、中断引脚、PCF8574驱动步骤)

          PCF8574驱动程序开发步骤详述如下:

          1. 初始化中断GPIO口和IIC接口

            • 中断引脚配置:首先需要配置与PCF8574 INT中断引脚相连的MCU GPIO为上拉输入模式,确保在中断未触发时,该GPIO端口处于高电平状态。

            • IIC接口初始化:调用相应的IIC驱动库函数(例如iic_init())初始化MCU的I²C接口,设置相关寄存器以匹配PCF8574的I²C通信要求,包括时钟频率、中断使能等。

            • 设备检测(可选):为了确保PCF8574设备在线,可以尝试通过I²C接口发送读写请求,并检测是否有应答信号,如果没有应答,则可能表示设备不存在或通信异常。

            • 编写读取PCF8574的8位IO值函数

              • 根据I²C读操作时序,按照(S + S_A + R + A + DR + Nack + P)的方式进行编程,其中:
                • S为发送起始信号
                • S_A为发送从机地址和读命令
                • R为从PCF8574接收数据
                • A为从机应答
                • DR为接收数据
                • Nack为主机发送非应答信号,结束数据接收
                • P为发送停止信号
                • 编写写入PCF8574的8位IO值函数

                  • 根据I²C写操作时序,按照(S + S_A + W + A + DW + A + P)的方式进行编程,其中:
                    • W为发送写命令
                    • DW为发送数据到PCF8574
                    • 其他符号含义同上一步骤
                    • 编写PCF8574读取某个IO的值函数

                      • 调用步骤2编写的读取8位IO值的函数,获取整个端口状态。
                      • 对获取到的8位数据进行位操作,提取所需的特定IO位的值。
                      • 编写PCF8574设置某个IO的值函数

                        • 调用步骤2编写的读取8位IO值的函数,读取当前所有IO的状态,保存下来。
                        • 修改读取到的数据中所关心的那一位IO状态。
                        • 调用步骤3编写的写入8位IO值的函数,将修改后的数据写回到PCF8574中,确保在修改目标IO的同时,不改变其他未涉及的IO状态。

          【正点原子STM32】IIC-IO扩展实验(PCF8574(IO扩展芯片)I²C总线接口的8位并行I/O端口扩展器、PCF8574寻址、写/读操作时序、中断引脚、PCF8574驱动步骤)

          【正点原子STM32】IIC-IO扩展实验(PCF8574(IO扩展芯片)I²C总线接口的8位并行I/O端口扩展器、PCF8574寻址、写/读操作时序、中断引脚、PCF8574驱动步骤)

          【正点原子STM32】IIC-IO扩展实验(PCF8574(IO扩展芯片)I²C总线接口的8位并行I/O端口扩展器、PCF8574寻址、写/读操作时序、中断引脚、PCF8574驱动步骤)

          四、编程实战

          【正点原子STM32】IIC-IO扩展实验(PCF8574(IO扩展芯片)I²C总线接口的8位并行I/O端口扩展器、PCF8574寻址、写/读操作时序、中断引脚、PCF8574驱动步骤)

          pcf8574.c

          #include "./BSP/PCF8574/pcf8574.h" // 包含PCF8574驱动头文件
          #include "./SYSTEM/delay/delay.h" // 包含延时函数头文件
          /**
           * @brief       初始化PCF8574芯片
           * @param       无
           * @retval      0, 初始化成功;
           *             1, 初始化失败;
           */
          uint8_t pcf8574_init(void)
          {
              uint8_t temp = 0;
              GPIO_InitTypeDef gpio_init_struct;
              // 使能PCF8574中断引脚对应的GPIO时钟
              PCF8574_GPIO_CLK_ENABLE();
              // 初始化PCF8574中断引脚为输入模式,带内部上拉,高速驱动
              gpio_init_struct.Pin = PCF8574_GPIO_PIN;                 /* PB12 */
              gpio_init_struct.Mode = GPIO_MODE_INPUT;                 /* 输入 */
              gpio_init_struct.Pull = GPIO_PULLUP;                     /* 上拉 */
              gpio_init_struct.Speed = GPIO_SPEED_HIGH;                /* 高速 */
              HAL_GPIO_Init(PCF8574_GPIO_PORT, &gpio_init_struct);     /* 初始化 */
              // 初始化IIC接口
              iic_init();
              // 检测PCF8574是否存在
              iic_start(); // 发送起始信号
              iic_send_byte(PCF8574_ADDR); // 发送PCF8574的写地址
              temp = iic_wait_ack(); // 等待ACK应答,根据应答结果判断PCF8574是否存在
              iic_stop(); // 发送停止信号
              // 初始化PCF8574所有IO口状态为高电平
              pcf8574_write_byte (0xFF);
              return temp; // 返回初始化结果
          }
          // 读取PCF8574的8位IO状态
          uint8_t pcf8574_read_byte (void)
          {
              uint8_t temp = 0;
              // 发送起始信号并设置读操作
              iic_start();
              iic_send_byte(0x41); // 发送PCF8574的读地址
              iic_wait_ack(); // 等待应答
              temp = iic_read_byte(0); // 读取并返回8位IO状态,并发送NACK信号表示读取结束
              iic_stop(); // 发送停止信号
              return temp;
          }
          // 向PCF8574写入一个字节数据,设置IO口状态
          void pcf8574_write_byte (uint8_t data)
          {
              // 发送起始信号并设置写操作
              iic_start();
              iic_send_byte(0x40); // 发送PCF8574的写地址
              iic_wait_ack(); // 等待应答
              iic_send_byte(data); // 发送要设置的8位IO状态
              iic_wait_ack(); // 等待应答
              iic_stop(); // 发送停止信号
              delay_ms(10); // 添加额外延时,确保数据稳定写入
          }
          // 读取PCF8574指定位的IO状态
          uint8_t pcf8574_read_bit(uint8_t bit)
          {
              uint8_t temp = 0;
              // 读取整个8位IO状态
              temp = pcf8574_read_byte ();
              // 判断指定位的IO状态
              if (temp & (1 
                  return 1;
              }
              else // 否则返回0
              {
                  return 0;
              }
          }
          // 设置PCF8574指定位的IO状态
          void pcf8574_write_bit(uint8_t bit, uint8_t sta)
          {
              uint8_t temp = 0;
              // 读取整个8位IO状态
              temp = pcf8574_read_byte ();
              // 根据传入的sta参数设置指定位的IO状态
              if (sta) // 如果sta为真(1),则将该位设置为1
              {
                  temp |= (1 
                  temp &= ~(1  __HAL_RCC_GPIOB_CLK_ENABLE(); }while(0)             /* PB口时钟使能 */
          /******************************************************************************************/
          #define PCF8574_INT  HAL_GPIO_ReadPin(PCF8574_GPIO_PORT, PCF8574_GPIO_PIN) /* PCF8574 INT脚 */
          #define PCF8574_ADDR  0X40      /* PCF8574地址(左移了一位) */
          /* PCF8574各个IO的功能 */
          #define BEEP_IO         0       /* 蜂鸣器控制引脚        P0 */
          #define AP_INT_IO       1       /* AP3216C中断引脚       P1 */
          #define DCMI_PWDN_IO    2       /* DCMI的电源控制引脚    P2 */
          #define USB_PWR_IO      3       /* USB电源控制引脚       P3 */
          #define EX_IO           4       /* 扩展IO,自定义使用     P4 */
          #define MPU_INT_IO      5       /* SH3001中断引脚        P5 */
          #define RS485_RE_IO     6       /* RS485_RE引脚          P6 */
          #define ETH_RESET_IO    7       /* 以太网复位引脚        P7 */
          uint8_t pcf8574_init(void); 						// 初始化PCF8574芯片
          void pcf8574_write_bit(uint8_t bit, uint8_t sta);	// 设置PCF8574指定位的IO状态
          uint8_t pcf8574_read_bit(uint8_t bit);				// 读取PCF8574指定位的IO状态
          uint8_t pcf8574_read_byte (void);					// 读取PCF8574的8位IO状态
          void pcf8574_write_byte (uint8_t data);				// 向PCF8574写入一个字节数据,设置IO口状态
          #endif
          
              uint16_t i = 0;                // 计数变量
              uint8_t key;                   // 按键状态变量
              
              // STM32 HAL库初始化
              HAL_Init();
              
              // 设置系统时钟为180MHz,配置HCLK、PCLK1、PCLK2及PLL等相关参数
              sys_stm32_clock_init(360, 25, 2, 8);
              
              // 初始化延时函数,设置延时基础频率
              delay_init(180);
              
              // 初始化USART串口通信,设置波特率为115200
              usart_init(115200);
              
              // 初始化USMART智能调试助手
              usmart_dev.init(90);
              
              // 初始化LED控制
              led_init();
              
              // 初始化按键输入
              key_init();
              
              // 初始化外部SDRAM
              sdram_init();
              
              // 初始化LCD显示屏
              lcd_init();
              
              // 初始化PCF8574 I/O扩展器
              pcf8574_init();
              
              // 主循环
              while (1)
              {
                  // 扫描按键,获取键值
                  key = key_scan(0);
                  
                  // 当按键KEY0按下时,通过PCF8574关闭蜂鸣器
                  if (key == KEY0_PRES)
                  {
                      pcf8574_write_bit(BEEP_IO, 0); // BEEP_IO为蜂鸣器对应的PCF8574 I/O端口号
                  }
                  
                  // 监听PCF8574中断引脚,当检测到输入IO电平变化时
                  if (PCF8574_INT == 0)
                  {
                      // 读取指定扩展IO端口状态
                      key = pcf8574_read_bit(EX_IO); // EX_IO为监控的PCF8574 I/O端口号
                      
                      // 如果该IO口状态为低电平
                      if (key == 0)
                      {
                          // 控制LED1状态翻转(闪烁)
                          LED1_TOGGLE();
                      }
                  }
                  
                  // 计数变量自增,用于控制LED0的闪烁频率
                  i++;
                  delay_ms(10); // 延时10ms
                  
                  // 当计数达到20次时,红灯LED0状态翻转(闪烁)
                  if (i == 20)
                  {
                      LED0_TOGGLE();
                      i = 0; // 重置计数器
                  }
              }
          }
          
VPS购买请点击我

免责声明:我们致力于保护作者版权,注重分享,被刊用文章因无法核实真实出处,未能及时与作者取得联系,或有版权异议的,请联系管理员,我们会立即处理! 部分文章是来自自研大数据AI进行生成,内容摘自(百度百科,百度知道,头条百科,中国民法典,刑法,牛津词典,新华词典,汉语词典,国家院校,科普平台)等数据,内容仅供学习参考,不准确地方联系删除处理! 图片声明:本站部分配图来自人工智能系统AI生成,觅知网授权图片,PxHere摄影无版权图库和百度,360,搜狗等多加搜索引擎自动关键词搜索配图,如有侵权的图片,请第一时间联系我们,邮箱:ciyunidc@ciyunshuju.com。本站只作为美观性配图使用,无任何非法侵犯第三方意图,一切解释权归图片著作权方,本站不承担任何责任。如有恶意碰瓷者,必当奉陪到底严惩不贷!

目录[+]