controller-pcba/User/application/src/TCA6416.c

705 lines
17 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#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, &current_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;
}