#include "i2cs.h" static inline void delay(i2c_t *handle); // 延时函数 static inline void _ack(i2c_t *handle); // 应答 static inline void _nack(i2c_t *handle); // 非应答 /** * @brief 启动I2C总线 * @param {i2c_t} *handle - I2C总线句柄 * @note: 用于启动I2C总线的操作。在发送或接收数据之前,需要先启动总线。 */ static void _start(i2c_t *handle) { DBG_ASSERT(handle != NULL __DBG_LINE); // 获取gpios指针 i2c_gpio_group_t *gpios = &handle->gpios; // 获取scl指针 gpio_t *scl = gpios->scl; // 获取sda指针 gpio_t *sda = gpios->sda; // 设置sda gpios->sda->set(*sda); // 设置scl gpios->scl->set(*scl); // 延时 delay(handle); // 重置sda gpios->sda->reset(*sda); // 延时 delay(handle); // 重置scl gpios->scl->reset(*scl); // 延时 delay(handle); } /** * @brief 停止I2C总线 * @param {i2c_t} *handle - I2C总线句柄 * @note: 用于停止I2C总线的操作。在发送或接收数据之后,需要先停止总线。 */ static void _stop(i2c_t *handle) { DBG_ASSERT(handle != NULL __DBG_LINE); i2c_gpio_group_t *gpios = &handle->gpios; gpio_t *scl = gpios->scl; gpio_t *sda = gpios->sda; gpios->scl->reset(*scl); gpios->sda->reset(*sda); delay(handle); gpios->scl->set(*scl); gpios->sda->set(*sda); delay(handle); } /** * @brief 等待应答信号 * @param {i2c_t} *handle - I2C总线句柄 * @return {BOOL} - 等待成功返回TRUE,否则返回FALSE * @note: 用于等待I2C总线上发送的应答信号。 */ BOOL _wait_ack(i2c_t *handle) { DBG_ASSERT(handle != NULL __DBG_LINE); uint8_t count = 0; i2c_gpio_group_t *gpios = &handle->gpios; gpio_t *scl = gpios->scl; gpio_t *sda = gpios->sda; gpios->sda->set(*sda); gpios->scl->set(*scl); delay(handle); while (gpios->sda->read(*sda)) { count++; if (count > 250) { _stop(handle); return FALSE; } } gpios->scl->reset(*scl); delay(handle); return TRUE; } /** * @brief 读取一个字节 * @param {i2c_t} *handle - I2C总线句柄 * @param {BOOL} ack - 应答信号标志 * @return {uint8_t} - 读取到的字节 * @note: 用于从I2C总线上读取一个字节。在读取一个字节后,需要发送应答信号。 */ uint8_t _read_byte(i2c_t *handle, BOOL ack) { DBG_ASSERT(handle != NULL __DBG_LINE); uint8_t i = 0, receive = 0; i2c_gpio_group_t *gpios = &handle->gpios; gpio_t *scl = gpios->scl; gpio_t *sda = gpios->sda; for (i = 0; i < 8; i++) { gpios->sda->set(*sda); gpios->scl->set(*scl); receive <<= 1; delay(handle); if (gpios->sda->read(*sda)) receive++; gpios->scl->reset(*scl); delay(handle); } if (TRUE == ack) { _ack(handle); } else { _nack(handle); } return receive; } /** * @brief 发送一个字节的数据到I2C总线上 * @param {i2c_t} *handle I2C总线的句柄 * @param {uint8_t} data 要发送的字节数据 * @return {*} 无 * @note: 该函数用于在I2C总线上发送一个字节的数据。它首先定义了一个循环,用于遍历要发送的字节中的每一位。在循环中,首先检查当前位是否为1,如果是,则设置SDA为1,否则设置SDA为0。然后设置SCL为1,延时1ms。接着将数据右移一位,然后设置SCL为0,延时1ms。当位遍历完后,最后设置SDA为1,以确保正确的结束传输。 */ void _write_byte(i2c_t *handle, uint8_t data) { // 定义变量i uint8_t i = 0; // 断言参数handle不为空 DBG_ASSERT(handle != NULL __DBG_LINE); // 定义变量gpios i2c_gpio_group_t *gpios = &handle->gpios; // 定义变量scl gpio_t *scl = gpios->scl; // 定义变量sda gpio_t *sda = gpios->sda; // 遍历每一位 for (i = 0; i < 8; i++) { // 如果data的最低位为1 if (data & 0x80) { // 设置sda的状态为1 gpios->sda->set(*sda); } // 否则,设置sda的状态为0 else { // 设置sda的状态为0 gpios->sda->reset(*sda); } // 设置scl的状态为1 gpios->scl->set(*scl); // 延时1ms delay(handle); // 将data右移1位 data <<= 1; // 设置scl的状态为0 gpios->scl->reset(*scl); // 延时1ms delay(handle); // 如果i等于7 if (i == 7) { // 设置sda的状态为1 gpios->sda->set(*sda); } } } /** * @brief 发送一个字节的数据到I2C总线上 * @param {i2c_t} *handle I2C总线的句柄 * @param {uint16_t} data 要发送的字节数据 * @return {*} 无 * @note: 该函数用于在I2C总线上发送一个字节的数据。它首先定义了一个循环,用于遍历要发送的字节中的每一位。在循环中,首先检查当前位是否为1,如果是,则设置SDA为1,否则设置SDA为0。然后设置SCL为1,延时1ms。接着将数据右移一位,然后设置SCL为0,延时1ms。当位遍历完后,最后设置SDA为1,以确保正确的结束传输。 */ void _write_word(i2c_t *handle, uint16_t data) { // 循环写入2个字节 for (uint8_t i = 0; i < 2; i++) { // 将data的第i个字节写入i2c接口 _write_byte(handle, (uint8_t)(data >> (8 * i))); // 等待ACK _wait_ack(handle); } } void _write_12bit(i2c_t *handle, uint16_t data) { uint8_t hi = ((data >> 8) << 4) + ((uint8_t)data >> 4); uint8_t lo = data << 4; // 定义变量i uint8_t i = 0; // 断言参数handle不为空 DBG_ASSERT(handle != NULL __DBG_LINE); // 定义变量gpios i2c_gpio_group_t *gpios = &handle->gpios; // 定义变量scl gpio_t *scl = gpios->scl; // 定义变量sda gpio_t *sda = gpios->sda; // 低位只写4bit for (i = 0; i < 8; i++) { // 如果data的最低位为1 if (lo & 0x80) { // 设置sda的状态为1 gpios->sda->set(*sda); } // 否则,设置sda的状态为0 else { // 设置sda的状态为0 gpios->sda->reset(*sda); } // 设置scl的状态为1 gpios->scl->set(*scl); // 延时1ms delay(handle); // 将data移1位 lo <<= 1; // 设置scl的状态为0 gpios->scl->reset(*scl); // 延时1ms delay(handle); } // 等待ACK _wait_ack(handle); _write_byte(handle, hi); // 等待ACK _wait_ack(handle); } /** * @brief 创建一个I2C总线设备 * @param {i2c_gpio_group_t} gpios I2C总线的GPIO配置 * @param {uint16_t} delay_ticks I2C总线的延时参数 * @return {i2c_t *} 创建的I2C总线设备句柄 * @note: 该函数用于创建一个I2C总线设备。它首先创建一个i2c_t结构体,并将gpios和delay_ticks的内存地址复制到handle结构体中。然后,它为handle结构体定义了start、stop、wait_ack、read_byte、write_byte和write_word函数。最后,它返回handle结构体。 */ i2c_t *i2c_create(i2c_gpio_group_t gpios, uint16_t delay_ticks) { // 创建一个i2c_t结构体 i2c_t *handle = (i2c_t *)osel_mem_alloc(sizeof(i2c_t)); // 将gpios的内存地址复制到handle结构体中 osel_memcpy((uint8_t *)&handle->gpios, (uint8_t *)&gpios, sizeof(i2c_gpio_group_t)); // 将delay_ticks的内存地址复制到handle结构体中 handle->delay_ticks = delay_ticks; // 创建一个start函数 handle->interface.start = _start; // 创建一个stop函数 handle->interface.stop = _stop; // 创建一个wait_ack函数 handle->interface.wait_ack = _wait_ack; // 创建一个read_byte函数 handle->interface.read_byte = _read_byte; // 创建一个write_byte函数 handle->interface.write_byte = _write_byte; // 创建一个write_word函数 handle->interface.write_word = _write_word; handle->interface.write_12bit = _write_12bit; // 返回handle结构体 return handle; } /** * @brief 释放I2C总线设备 * @param {i2c_t} *handle 需要释放的I2C总线设备句柄 * @return {*} 无 * @note: 该函数用于释放一个I2C总线设备。它首先检查handle是否为空,如果不为空,则释放所有的GPIO。最后,释放handle的内存。 */ void i2c_free(i2c_t *handle) { // 如果handle不为空,则释放所有的gpio if (NULL != handle) { gpio_free(handle->gpios.scl); gpio_free(handle->gpios.sda); osel_mem_free(handle); } } /*下面是内部实现方法*/ /** * @brief 此方法是一个简单的延迟函数,它使用循环来执行指定数量的NOP(无操作)指令。这个延迟函数的目的是在程序的执行中引入延迟,这在需要精确定时的某些应用中很有用。延迟时间由输入参数“count”的值决定,该参数指定要执行的NOP指令的数量。延迟时间通常以微秒或毫秒为单位测量,具体取决于使用延迟的环境。 * @param {uint16_t} count NOP指令的数量 * @return {*} */ static inline void delay(i2c_t *handle) { // 断言参数handle不为空 DBG_ASSERT(handle != NULL __DBG_LINE); // 定义循环计数变量count uint16_t count = 0; // 设置循环计数变量count的值为handle->delay_ticks count = handle->delay_ticks; // 循环计数变量count的值,直到count的值为0 while (count--) { // 每次循环调用__NOP()函数 __NOP(); } } /** * @brief 发送ACK信号 * @param {i2c_t} *handle I2C总线的句柄 * @return {*} 无 * @note: 该函数用于在I2C总线上发送ACK信号。它首先断言handle不为空,然后获取gpios和scl、sda的指针。接着重置sda,延时1ms,设置scl为1,延时1ms,重置scl,确保正确的结束传输。 */ static inline void _ack(i2c_t *handle) { // 断言handle不为空 DBG_ASSERT(handle != NULL __DBG_LINE); // 获取gpios指针 i2c_gpio_group_t *gpios = &handle->gpios; // 获取scl指针 gpio_t *scl = gpios->scl; // 获取sda指针 gpio_t *sda = gpios->sda; // 重置sda gpios->sda->reset(*sda); // 延时 delay(handle); // 设置scl gpios->scl->set(*scl); // 延时 delay(handle); // 重置scl gpios->scl->reset(*scl); } /** * @brief 发送NACK信号 * @param {i2c_t} *handle I2C总线的句柄 * @return {*} 无 * @note: 该函数用于在I2C总线上发送NACK信号。它首先断言handle不为空,然后获取gpios和scl、sda的指针。接着设置sda为1,延时1ms,设置scl为1,延时1ms,重置scl,确保正确的结束传输。 */ static inline void _nack(i2c_t *handle) { DBG_ASSERT(handle != NULL __DBG_LINE); // 获取gpios指针 i2c_gpio_group_t *gpios = &handle->gpios; // 获取scl指针 gpio_t *scl = gpios->scl; // 获取sda指针 gpio_t *sda = gpios->sda; // 设置sda引脚 gpios->sda->set(*sda); // 等待延时 delay(handle); // 设置scl引脚 gpios->scl->set(*scl); // 等待延时 delay(handle); // 重置scl引脚 gpios->scl->reset(*scl); }