This repository has been archived on 2025-01-02. You can view files and clone it, but cannot push or open issues or pull requests.
torsion/User/system/bsp/i2cs.c

375 lines
11 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 "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);
}