0 参考资料
PCF8563数据手册(第 11 版——2015 年 10 月 26 日).pdf
1 基于软件I2C实现读写接口
1.1 初始化软件I2C引脚
软件I2C引脚配置为开漏输出:
/**
* @brief 软件I2C初始化
*
* @param sw_i2c_cfg 软件I2C配置指针
*/
int sw_i2c_init(sw_i2c_cfg_t *sw_i2c_cfg)
{
GPIO_InitTypeDef GPIO_InitStruct = {0};
/* 使能SCL/SDA GPIO时钟 */
gpio_clk_enable(sw_i2c_cfg->scl_port);
gpio_clk_enable(sw_i2c_cfg->sda_port);
/* SCL配置为开漏输出 */
GPIO_InitStruct.Pin = sw_i2c_cfg->scl_pin;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_OD;
GPIO_InitStruct.Pull = GPIO_PULLUP;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
HAL_GPIO_Init(sw_i2c_cfg->scl_port, &GPIO_InitStruct);
/* SDA配置为开漏输出 */
GPIO_InitStruct.Pin = sw_i2c_cfg->sda_pin;
HAL_GPIO_Init(sw_i2c_cfg->sda_port, &GPIO_InitStruct);
/* SCL/SDA默认拉高 */
HAL_GPIO_WritePin(sw_i2c_cfg->scl_port, sw_i2c_cfg->scl_pin, GPIO_PIN_SET);
HAL_GPIO_WritePin(sw_i2c_cfg->sda_port, sw_i2c_cfg->sda_pin, GPIO_PIN_SET);
g_sw_i2c_cfg = *sw_i2c_cfg;
sw_i2c_delay();
if ((SCL_IN != 1) || (SDA_IN != 1))
{
return -1;
}
return 0;
}
1.2 软件I2C延时
这里使用stm32MP135的64位时间戳作为时间基准,时钟来源为HSE(24MHz)
/**
* @brief 软件I2C延时
*
*/
void sw_i2c_delay(void)
{
switch (g_sw_i2c_cfg.speed)
{
case SW_I2C_SPEED_1MHz:
hw_delay_hse(7); // 延时6个HSE为250ns正好为1MHz,这里多加1个HSE防止取在跳变边沿
break;
case SW_I2C_SPEED_400KHz:
hw_delay_hse(16); // 延时15个HSE为625ns正好为400KHz,这里多加1个HSE防止取在跳变边沿
break;
case SW_I2C_SPEED_100KHz:
hw_delay_hse(61); // 延时15个HSE为2500ns正好为100KHz,这里多加1个HSE防止取在跳变边沿
break;
default:
break;
}
}
1.3 软件I2C产生起始信号
/**
* @brief 软件I2C产生起始信号
*
* @return int 0:成功 -1:失败(总线存在竞争)
*/
int sw_i2c_START(void)
{
SCL_L;
sw_i2c_delay();
SDA_H;
sw_i2c_delay();
SCL_H;
sw_i2c_delay();
SDA_L;
sw_i2c_delay();
if (SDA_IN != 0)
{
return -1;
}
return 0;
}
1.4 软件I2C产生停止信号
/**
* @brief 软件I2C产生停止信号
*
* @return int 0:成功 -1:失败(总线存在竞争)
*/
int sw_i2c_STOP(void)
{
SCL_L;
sw_i2c_delay();
SDA_L;
sw_i2c_delay();
SCL_H;
sw_i2c_delay();
SDA_H;
sw_i2c_delay();
if (SDA_IN != 1)
{
return -1;
}
return 0;
}
1.5 软件I2C发送NOACK
/**
* @brief 软件I2C发送NOACK
*
* @return int 0:成功 -1:失败(总线存在竞争)
*/
int sw_i2c_NOACK(void)
{
SCL_L;
sw_i2c_delay();
SDA_H;
sw_i2c_delay();
if (SDA_IN != 1)
{
return -1;
}
SCL_H;
sw_i2c_delay();
sw_i2c_delay();
return 0;
}
1.6 软件I2C发送ACK
/**
* @brief 软件I2C发送ACK
*
* @return int 0:成功 -1:失败(总线存在竞争)
*/
int sw_i2c_ACK(void)
{
SCL_L;
sw_i2c_delay();
SDA_L;
sw_i2c_delay();
if (SDA_IN != 0)
{
return -1;
}
SCL_H;
sw_i2c_delay();
sw_i2c_delay();
return 0;
}
1.7 软件I2C等待ACK信号
/**
* @brief 软件I2C等待ACK信号
*
* @return int 0-成功 -1-失败
*/
int sw_i2c_wait_ACK(void)
{
SCL_L;
sw_i2c_delay();
sw_i2c_delay();
SCL_H;
sw_i2c_delay();
sw_i2c_delay();
if (SDA_IN != 0)
{
return -1;
}
return 0;
}
1.8 软件I2C发送1字节数据
/**
* @brief 软件I2C发送1字节数据
*
* @param data 数据
* @return int 0:成功 -1:失败(总线存在竞争)
*/
int sw_i2c_send_byte(u8 data)
{
int i;
for (i = 0; i < 8; i++)
{
SCL_L;
sw_i2c_delay(); // 等待SCL电平变为设置电平
if ((data << i) & 0x80)
{
SDA_H;
}
else
{
SDA_L;
}
sw_i2c_delay(); // 等待SDA电平变为设置电平
/* 检查SDA是否和目标电平一致 */
if ((data << i) & 0x80)
{
if (SDA_IN != 1)
{
return -1;
}
}
else
{
if (SDA_IN != 0)
{
return -1;
}
}
SCL_H;
// 这里使用2个延时主要是为了和前面的2个延时时间一致,便于调速
sw_i2c_delay(); // 等待SCL电平变为设置电平
sw_i2c_delay(); // 等待SCL电平变为设置电平
}
return 0;
}
1.9 软件I2C接收1字节数据
/**
* @brief 软件I2C接收1字节数据
*
* @param data 数据指针
*/
void sw_i2c_recv_byte(u8 *data)
{
int i;
*data = 0x0;
for (i = 0; i < 8; i++)
{
SCL_L;
sw_i2c_delay();
SDA_H; // 释放SDA,否则SDA读取数据全是0
sw_i2c_delay();
SCL_H;
sw_i2c_delay();
if (SDA_IN == 1)
{
*data = ((*data << 1) | 0x1);
}
else
{
*data = (*data << 1);
}
sw_i2c_delay();
}
}
1.10 软件I2C写入n字节数据
参考数据手册写入n字节数据时序图:

/**
* @brief 软件I2C写入数据
* 标准操作,数据地址为单字节
* @param dev_addr 设备地址
* @param data_addr 数据起始地址
* @param data 数据指针
* @param len 数据长度
* @return int 0:成功 <0:失败
*/
int write_sw_i2c_data(u8 dev_addr, u8 data_addr, u8 *data, int len)
{
int i;
int ret;
/* 发送设备起始信号 */
ret = sw_i2c_START();
if (ret != 0)
{
return -1;
}
/* 发送设备地址,命令:写 */
ret = sw_i2c_send_byte(dev_addr);
if (ret != 0)
{
return -2;
}
ret = sw_i2c_wait_ACK();
if (ret != 0)
{
return -3;
}
/* 发送数据地址高8位 */
ret = sw_i2c_send_byte(data_addr);
if (ret != 0)
{
return -4;
}
ret = sw_i2c_wait_ACK();
if (ret != 0)
{
return -5;
}
/* 写入数据 */
for (i = 0; i < len; i++)
{
sw_i2c_send_byte(data[i]);
ret = sw_i2c_wait_ACK();
if (ret != 0)
{
return -6;
}
}
ret = sw_i2c_STOP();
if (ret != 0)
{
return -7;
}
return 0;
}
1.11 软件I2C读取n字节数据
参考数据手册读取n字节数据时序图:

/**
* @brief 软件I2C读取数据
* 标准操作,数据地址为单字节
* @param dev_addr 设备地址
* @param data_addr 数据起始地址
* @param data 数据指针
* @param len 数据长度
* @return int 0:成功 <0:失败
*/
int read_sw_i2c_data(u8 dev_addr, u8 data_addr, u8 *data, int len)
{
int i;
int ret;
/* 发送设备起始信号 */
ret = sw_i2c_START();
if (ret != 0)
{
return -1;
}
/* 发送设备地址,命令:写 */
ret = sw_i2c_send_byte(dev_addr);
if (ret != 0)
{
return -2;
}
ret = sw_i2c_wait_ACK();
if (ret != 0)
{
return -3;
}
/* 发送数据地址高8位 */
ret = sw_i2c_send_byte(data_addr);
if (ret != 0)
{
return -4;
}
ret = sw_i2c_wait_ACK();
if (ret != 0)
{
return -5;
}
/* 发送设备起始信号 */
ret = sw_i2c_START();
if (ret != 0)
{
return -6;
}
/* 发送设备地址,命令:读 */
ret = sw_i2c_send_byte(dev_addr + 1);
if (ret != 0)
{
return -7;
}
ret = sw_i2c_wait_ACK();
if (ret != 0)
{
return -8;
}
/* 读取数据 */
for (i = 0; i < len; i++)
{
sw_i2c_recv_byte(&data[i]);
if (i != (len - 1))
{
ret = sw_i2c_ACK();
if (ret != 0)
{
return -9;
}
}
else
{
ret = sw_i2c_NOACK();
if (ret != 0)
{
return -10;
}
}
}
ret = sw_i2c_STOP();
if (ret != 0)
{
return -11;
}
return 0;
}

65

被折叠的 条评论
为什么被折叠?



