#include "main.h" #include "TCA6416.h" #include "stm32f4xx_hal.h" // 更新HAL库头文件路径 /** * @brief 初始化TCA6416的GPIO */ //第一条IIC总线初始化 void TCA6416_GPIO_Init(void) { GPIO_InitTypeDef GPIO_InitStruct = {0}; // 使能GPIOE时钟 __HAL_RCC_GPIOE_CLK_ENABLE(); //两个芯片挂一条总线上SDA PE3 SCL PE2 GPIO_InitStruct.Pin = TCA6416_SCL_PIN | TCA6416_SDA_PIN; GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_OD; // 开漏输出 GPIO_InitStruct.Pull = GPIO_NOPULL; // 无内部上拉,依赖外部上拉 GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH; HAL_GPIO_Init(TCA6416_SDA_PORT, &GPIO_InitStruct); // 初始状态设为高电平(实际由外部上拉电阻拉高) SCL_HIGH(); SDA_HIGH(); } //第二条IIC总线初始化 void TCA6416_GPIO_Init2(void) { GPIO_InitTypeDef GPIO_InitStruct = {0}; // 使能GPIOE时钟 __HAL_RCC_GPIOE_CLK_ENABLE(); //两个芯片挂一条总线上SDA PE4 SCL PE5 GPIO_InitStruct.Pin = TCA6416_SCL_PIN2 | TCA6416_SDA_PIN2; GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_OD; // 开漏输出 GPIO_InitStruct.Pull = GPIO_NOPULL; // 无内部上拉,依赖外部上拉 GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH; HAL_GPIO_Init(TCA6416_SDA_PORT2, &GPIO_InitStruct); // 初始状态设为高电平(实际由外部上拉电阻拉高) SCL_HIGH2(); SDA_HIGH2(); } void U74HC245_GPIO_Init(void) { GPIO_InitTypeDef GPIO_InitStruct = {0}; // 使能GPIOE时钟 __HAL_RCC_GPIOD_CLK_ENABLE(); //74HC245 DIR端口设置 GPIO_InitStruct.Pin = GPIO_PIN_4 | GPIO_PIN_7; GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP; // 开漏输出 GPIO_InitStruct.Pull = GPIO_PULLDOWN; // 下拉 GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH; HAL_GPIO_Init(GPIOD, &GPIO_InitStruct); HAL_GPIO_WritePin(GPIOD, GPIO_PIN_4 | GPIO_PIN_7, GPIO_PIN_RESET); } /** * @brief 软件I2C起始信号 */ void I2C_Start(void) { SDA_OUTPUT_MODE(); // 确保SDA为输出模式 SDA_HIGH(); SCL_HIGH(); I2C_DELAY(); SDA_LOW(); // SDA下降沿(SCL为高) I2C_DELAY(); SCL_LOW(); // 拉低SCL } /** * @brief 软件I2C停止信号 */ void I2C_Stop(void) { SDA_OUTPUT_MODE(); // 确保SDA为输出模式 SCL_LOW(); SDA_LOW(); I2C_DELAY(); SCL_HIGH(); I2C_DELAY(); SDA_HIGH(); // SDA上升沿(SCL为高) I2C_DELAY(); } /** * @brief 软件I2C发送应答 */ void I2C_Ack(void) { SCL_LOW(); SDA_LOW(); I2C_DELAY(); SCL_HIGH(); I2C_DELAY(); SCL_LOW(); } /** * @brief 软件I2C发送非应答 */ void I2C_NAck(void) { SCL_LOW(); SDA_HIGH(); I2C_DELAY(); SCL_HIGH(); I2C_DELAY(); SCL_LOW(); } /** * @brief 软件I2C等待应答 * @retval 0:有应答 1:无应答 */ uint8_t I2C_WaitAck(void) { uint8_t ack; SCL_LOW(); SDA_HIGH(); SDA_INPUT_MODE(); I2C_DELAY(); SCL_HIGH(); I2C_DELAY(); ack = SDA_READ() ? 1 : 0; SCL_LOW(); SDA_OUTPUT_MODE(); return ack; } /** * @brief 软件I2C发送一个字节 * @param byte: 要发送的字节 */ uint8_t I2C_SendByte(uint8_t byte) { uint8_t i; for(i = 0; i < 8; i++) { SCL_LOW(); I2C_DELAY(); if(byte & 0x80) SDA_HIGH(); else SDA_LOW(); byte <<= 1; I2C_DELAY(); SCL_HIGH(); I2C_DELAY(); } SCL_LOW(); // 释放SDA,准备接收ACK SDA_HIGH(); SDA_INPUT_MODE(); I2C_DELAY(); SCL_HIGH(); I2C_DELAY(); uint8_t ack = SDA_READ() ? 1 : 0; // 0=ACK, 1=NACK SCL_LOW(); SDA_OUTPUT_MODE(); return ack; } /** * @brief 软件I2C读取一个字节 * @retval 读取到的字节 */ uint8_t I2C_ReadByte(void) { uint8_t i, byte = 0; SDA_HIGH(); for(i = 0; i < 8; i++) { SCL_LOW(); I2C_DELAY(); SCL_HIGH(); byte <<= 1; if(SDA_READ()) byte |= 0x01; I2C_DELAY(); } SCL_LOW(); return byte; } /** * @brief 初始化TCA6416 * @retval 0:成功 1:失败 */ uint8_t TCA6416_Init(void) { // 初始化GPIO TCA6416_GPIO_Init(); HAL_Delay(50); // 默认将所有引脚设置为输入 if(TCA6416_SetPortDirection(0, 0xFF) != 0) return 1; if(TCA6416_SetPortDirection(1, 0xFF) != 0) return 1; // 设置默认输出值为0 if(TCA6416_WritePort(0, 0x00) != 0) return 1; if(TCA6416_WritePort(1, 0x00) != 0) return 1; // 设置默认极性(非反转) if(TCA6416_SetPortPolarity(0, 0x00) != 0) return 1; if(TCA6416_SetPortPolarity(1, 0x00) != 0) return 1; return 0; } /** * @brief 向端口写入数据 * @param port: 端口号(0或1) * @param data: 要写入的数据 * @retval 0:成功 1:失败 */ uint8_t TCA6416_WritePort(uint8_t port, uint8_t data) { uint8_t reg_addr = (port == 0) ? TCA6416_OUTPUT_PORT0 : TCA6416_OUTPUT_PORT1; I2C_Start(); if(I2C_SendByte(TCA6416_ADDR << 1)) { I2C_Stop(); return 1; } // 直接使用返回值 if(I2C_SendByte(reg_addr)) { I2C_Stop(); return 1; } // 直接使用返回值 if(I2C_SendByte(data)) { I2C_Stop(); return 1; } // 直接使用返回值 I2C_Stop(); return 0; } /** * @brief 从端口读取数据 * @param port: 端口号(0或1) * @param data: 存储读取数据的指针 * @retval 0:成功 1:失败 */ uint8_t TCA6416_ReadPort(uint8_t port, uint8_t *data) { uint8_t reg_addr = (port == 0) ? TCA6416_INPUT_PORT0 : TCA6416_INPUT_PORT1; I2C_Start(); if(I2C_SendByte(TCA6416_ADDR << 1)) { I2C_Stop(); return 1; } // 直接使用返回值 if(I2C_SendByte(reg_addr)) { I2C_Stop(); return 1; } // 直接使用返回值 I2C_Start(); if(I2C_SendByte((TCA6416_ADDR << 1) | 0x01)) { I2C_Stop(); return 1; } // 直接使用返回值 *data = I2C_ReadByte(); // 读取数据 I2C_NAck(); I2C_Stop(); return 0; } /** * @brief 设置端口方向(输入/输出) * @param port: 端口号(0或1) * @param direction: 0 = 输出, 1 = 输入 * @retval 0:成功 1:失败 */ uint8_t TCA6416_SetPortDirection(uint8_t port, uint8_t direction) { uint8_t reg_addr = (port == 0) ? TCA6416_CONFIG_PORT0 : TCA6416_CONFIG_PORT1; I2C_Start(); if(I2C_SendByte(TCA6416_ADDR << 1)) { I2C_Stop(); return 1; } // 直接使用返回值 if(I2C_SendByte(reg_addr)) { I2C_Stop(); return 1; } // 直接使用返回值 if(I2C_SendByte(direction)) { I2C_Stop(); return 1; } // 直接使用返回值 I2C_Stop(); return 0; } /** * @brief 设置端口极性 * @param port: 端口号(0或1) * @param polarity: 0 = 非反转, 1 = 反转 * @retval 0:成功 1:失败 */ uint8_t TCA6416_SetPortPolarity(uint8_t port, uint8_t polarity) { uint8_t reg_addr = (port == 0) ? TCA6416_POL_INV_PORT0 : TCA6416_POL_INV_PORT1; I2C_Start(); if(I2C_SendByte(TCA6416_ADDR << 1)) { I2C_Stop(); return 1; } // 直接使用返回值 if(I2C_SendByte(reg_addr)) { I2C_Stop(); return 1; } // 直接使用返回值 if(I2C_SendByte(polarity)) { I2C_Stop(); return 1; } // 直接使用返回值 I2C_Stop(); return 0; } /** * @brief 写入单个引脚 * @param port: 端口号(0或1) * @param pin: 引脚号(0-7) * @param state: 引脚状态(0或1) * @retval 0:成功 1:失败 */ uint8_t TCA6416_WritePin(uint8_t port, uint8_t pin, uint8_t state) { uint8_t current_data; uint8_t reg_addr = (port == 0) ? TCA6416_OUTPUT_PORT0 : TCA6416_OUTPUT_PORT1; // 读取当前端口值 if(TCA6416_ReadPort(port, ¤t_data) != 0) return 1; // 修改特定引脚 if(state) current_data |= (1 << pin); else current_data &= ~(1 << pin); // 写回修改后的值 return TCA6416_WritePort(port, current_data); } /** * @brief 读取单个引脚 * @param port: 端口号(0或1) * @param pin: 引脚号(0-7) * @param state: 存储引脚状态的指针 * @retval 0:成功 1:失败 */ uint8_t TCA6416_ReadPin(uint8_t port, uint8_t pin, uint8_t *state) { uint8_t port_data; if(TCA6416_ReadPort(port, &port_data) != 0) return 1; *state = (port_data >> pin) & 0x01; return 0; } /* 第二个芯片的I2C操作函数 */ void I2C_Start2(void) { SDA_HIGH2(); SCL_HIGH2(); I2C_DELAY(); SDA_LOW2(); I2C_DELAY(); SCL_LOW2(); } void I2C_Stop2(void) { SCL_LOW2(); SDA_LOW2(); I2C_DELAY(); SCL_HIGH2(); I2C_DELAY(); SDA_HIGH2(); I2C_DELAY(); } void I2C_Ack2(void) { SCL_LOW2(); SDA_LOW2(); I2C_DELAY(); SCL_HIGH2(); I2C_DELAY(); SCL_LOW2(); } void I2C_NAck2(void) { SCL_LOW2(); SDA_HIGH2(); I2C_DELAY(); SCL_HIGH2(); I2C_DELAY(); SCL_LOW2(); } uint8_t I2C_WaitAck2(void) { uint8_t ack; SCL_LOW2(); SDA_HIGH2(); I2C_DELAY(); SCL_HIGH2(); I2C_DELAY(); ack = SDA_READ2() ? 1 : 0; SCL_LOW2(); return ack; } void I2C_SendByte2(uint8_t byte) { uint8_t i; for(i = 0; i < 8; i++) { SCL_LOW2(); if(byte & 0x80) SDA_HIGH2(); else SDA_LOW2(); byte <<= 1; I2C_DELAY(); SCL_HIGH2(); I2C_DELAY(); } SCL_LOW2(); } uint8_t I2C_ReadByte2(void) { uint8_t i, byte = 0; SDA_HIGH2(); for(i = 0; i < 8; i++) { SCL_LOW2(); I2C_DELAY(); SCL_HIGH2(); byte <<= 1; if(SDA_READ2()) byte |= 0x01; I2C_DELAY(); } SCL_LOW2(); return byte; } /** * @brief 初始化第二个TCA6416 * @retval 0:成功 1:失败 */ uint8_t TCA6416_Init2(void) { // 初始化GPIO TCA6416_GPIO_Init2(); // 增加延时以确保设备稳定 HAL_Delay(10); // 默认将所有引脚设置为输入 if(TCA6416_SetPortDirection2(0, 0xFF) != 0) return 1; if(TCA6416_SetPortDirection2(1, 0xFF) != 0) return 1; // 设置默认输出值为0 if(TCA6416_WritePort2(0, 0x00) != 0) return 1; if(TCA6416_WritePort2(1, 0x00) != 0) return 1; // 设置默认极性(非反转) if(TCA6416_SetPortPolarity2(0, 0x00) != 0) return 1; if(TCA6416_SetPortPolarity2(1, 0x00) != 0) return 1; return 0; } /** * @brief 向第二个TCA6416的端口写入数据 * @param port: 端口号(0或1) * @param data: 要写入的数据 * @retval 0:成功 1:失败 */ uint8_t TCA6416_WritePort2(uint8_t port, uint8_t data) { uint8_t reg_addr = (port == 0) ? TCA6416_OUTPUT_PORT0 : TCA6416_OUTPUT_PORT1; I2C_Start2(); I2C_SendByte2(TCA6416_ADDR2 << 1); // 写地址 if(I2C_WaitAck2()) { I2C_Stop2(); return 1; } I2C_SendByte2(reg_addr); // 寄存器地址 if(I2C_WaitAck2()) { I2C_Stop2(); return 1; } I2C_SendByte2(data); // 数据 if(I2C_WaitAck2()) { I2C_Stop2(); return 1; } I2C_Stop2(); return 0; } /** * @brief 从第二个TCA6416的端口读取数据 * @param port: 端口号(0或1) * @param data: 存储读取数据的指针 * @retval 0:成功 1:失败 */ uint8_t TCA6416_ReadPort2(uint8_t port, uint8_t *data) { uint8_t reg_addr = (port == 0) ? TCA6416_INPUT_PORT0 : TCA6416_INPUT_PORT1; I2C_Start2(); I2C_SendByte2(TCA6416_ADDR2 << 1); // 写地址 if(I2C_WaitAck2()) { I2C_Stop2(); return 1; } I2C_SendByte2(reg_addr); // 寄存器地址 if(I2C_WaitAck2()) { I2C_Stop2(); return 1; } I2C_Start2(); I2C_SendByte2((TCA6416_ADDR2 << 1) | 0x01); // 读地址 if(I2C_WaitAck2()) { I2C_Stop2(); return 1; } *data = I2C_ReadByte2(); // 读取数据 I2C_NAck2(); I2C_Stop2(); return 0; } /** * @brief 设置第二个TCA6416的端口方向 * @param port: 端口号(0或1) * @param direction: 0 = 输出, 1 = 输入 * @retval 0:成功 1:失败 */ uint8_t TCA6416_SetPortDirection2(uint8_t port, uint8_t direction) { uint8_t reg_addr = (port == 0) ? TCA6416_CONFIG_PORT0 : TCA6416_CONFIG_PORT1; I2C_Start2(); I2C_SendByte2(TCA6416_ADDR2 << 1); // 写地址 if(I2C_WaitAck2()) { I2C_Stop2(); return 1; } I2C_SendByte2(reg_addr); // 寄存器地址 if(I2C_WaitAck2()) { I2C_Stop2(); return 1; } I2C_SendByte2(direction); // 方向设置 if(I2C_WaitAck2()) { I2C_Stop2(); return 1; } I2C_Stop2(); return 0; } /** * @brief 设置第二个TCA6416的端口极性 * @param port: 端口号(0或1) * @param polarity: 0 = 非反转, 1 = 反转 * @retval 0:成功 1:失败 */ uint8_t TCA6416_SetPortPolarity2(uint8_t port, uint8_t polarity) { uint8_t reg_addr = (port == 0) ? TCA6416_POL_INV_PORT0 : TCA6416_POL_INV_PORT1; I2C_Start2(); I2C_SendByte2(TCA6416_ADDR2 << 1); // 写地址 if(I2C_WaitAck2()) { I2C_Stop2(); return 1; } I2C_SendByte2(reg_addr); // 寄存器地址 if(I2C_WaitAck2()) { I2C_Stop2(); return 1; } I2C_SendByte2(polarity); // 极性设置 if(I2C_WaitAck2()) { I2C_Stop2(); return 1; } I2C_Stop2(); return 0; } /* 第三个芯片的操作函数(使用第二个芯片的I2C总线,但地址为TCA6416_ADDR3) */ /** * @brief 初始化第三个TCA6416 * @retval 0:成功 1:失败 */ uint8_t TCA6416_Init3(void) { // GPIO已在TCA6416_GPIO_Init()中初始化 U74HC245_GPIO_Init(); // 增加延时以确保设备稳定 HAL_Delay(10); // 设置默认极性(非反转) if(TCA6416_SetPortPolarity3(0, 0x00) != 0) return 1; if(TCA6416_SetPortPolarity3(1, 0x00) != 0) return 1; // 默认将所有引脚设置为输入 if(TCA6416_SetPortDirection3(0, 0xFF) != 0) return 1; if(TCA6416_SetPortDirection3(1, 0xFF) != 0) return 1; // 设置默认输出值为0 if(TCA6416_WritePort3(0, 0x00) != 0) return 1; if(TCA6416_WritePort3(1, 0x00) != 0) return 1; return 0; } /** * @brief 向第三个TCA6416的端口写入数据 * @param port: 端口号(0或1) * @param data: 要写入的数据 * @retval 0:成功 1:失败 */ uint8_t TCA6416_WritePort3(uint8_t port, uint8_t data) { uint8_t reg_addr = (port == 0) ? TCA6416_OUTPUT_PORT0 : TCA6416_OUTPUT_PORT1; I2C_Start2(); I2C_SendByte2(TCA6416_ADDR3 << 1); // 写地址 if(I2C_WaitAck2()) { I2C_Stop2(); return 1; } I2C_SendByte2(reg_addr); // 寄存器地址 if(I2C_WaitAck2()) { I2C_Stop2(); return 1; } I2C_SendByte2(data); // 数据 if(I2C_WaitAck2()) { I2C_Stop2(); return 1; } I2C_Stop2(); return 0; } /** * @brief 从第三个TCA6416的端口读取数据 * @param port: 端口号(0或1) * @param data: 存储读取数据的指针 * @retval 0:成功 1:失败 */ uint8_t TCA6416_ReadPort3(uint8_t port, uint8_t *data) { uint8_t reg_addr = (port == 0) ? TCA6416_INPUT_PORT0 : TCA6416_INPUT_PORT1; I2C_Start2(); I2C_SendByte2(TCA6416_ADDR3 << 1); // 写地址 if(I2C_WaitAck2()) { I2C_Stop2(); return 1; } I2C_SendByte2(reg_addr); // 寄存器地址 if(I2C_WaitAck2()) { I2C_Stop2(); return 1; } I2C_Start2(); I2C_SendByte2((TCA6416_ADDR3 << 1) | 0x01); // 读地址 if(I2C_WaitAck2()) { I2C_Stop2(); return 1; } *data = I2C_ReadByte2(); // 读取数据 I2C_NAck2(); I2C_Stop2(); return 0; } /** * @brief 设置第三个TCA6416的端口方向 * @param port: 端口号(0或1) * @param direction: 0 = 输出, 1 = 输入 * @retval 0:成功 1:失败 */ uint8_t TCA6416_SetPortDirection3(uint8_t port, uint8_t direction) { uint8_t reg_addr = (port == 0) ? TCA6416_CONFIG_PORT0 : TCA6416_CONFIG_PORT1; I2C_Start2(); I2C_SendByte2(TCA6416_ADDR3 << 1); // 写地址 if(I2C_WaitAck2()) { I2C_Stop2(); return 1; } I2C_SendByte2(reg_addr); // 寄存器地址 if(I2C_WaitAck2()) { I2C_Stop2(); return 1; } I2C_SendByte2(direction); // 方向设置 if(I2C_WaitAck2()) { I2C_Stop2(); return 1; } I2C_Stop2(); return 0; } /** * @brief 设置第三个TCA6416的端口极性 * @param port: 端口号(0或1) * @param polarity: 0 = 非反转, 1 = 反转 * @retval 0:成功 1:失败 */ uint8_t TCA6416_SetPortPolarity3(uint8_t port, uint8_t polarity) { uint8_t reg_addr = (port == 0) ? TCA6416_POL_INV_PORT0 : TCA6416_POL_INV_PORT1; I2C_Start2(); I2C_SendByte2(TCA6416_ADDR3 << 1); // 写地址 if(I2C_WaitAck2()) { I2C_Stop2(); return 1; } I2C_SendByte2(reg_addr); // 寄存器地址 if(I2C_WaitAck2()) { I2C_Stop2(); return 1; } I2C_SendByte2(polarity); // 极性设置 if(I2C_WaitAck2()) { I2C_Stop2(); return 1; } I2C_Stop2(); return 0; }