662 lines
21 KiB
C
662 lines
21 KiB
C
#include "i2cs.h"
|
||
#include "main.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总线上发送的应答信号。
|
||
*/
|
||
static 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总线上读取一个字节。在读取一个字节后,需要发送应答信号。
|
||
*/
|
||
static 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,以确保正确的结束传输。
|
||
*/
|
||
static 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,以确保正确的结束传输。
|
||
*/
|
||
static 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);
|
||
}
|
||
}
|
||
|
||
/**
|
||
* @brief I2C重置
|
||
* @param {I2C_TypeDef} *I2Cx
|
||
* @return {*}
|
||
* @note
|
||
*/
|
||
static void i2c_reset(I2C_TypeDef *I2Cx)
|
||
{
|
||
// Disable the I2C peripheral
|
||
LL_I2C_Disable(I2Cx);
|
||
|
||
// Re-enable the I2C peripheral
|
||
LL_I2C_Enable(I2Cx);
|
||
}
|
||
|
||
/**
|
||
* @brief 非阻塞模式下使用DMA将数据写入特定内存地址
|
||
* @param {i2c_t} *handle 指向一个i2c_t结构体,其中包含指定I2C的配置信息
|
||
* @param {uint16_t} dev_address 目标设备地址:在数据手册中,设备7位地址值需要向左移动以调用接口
|
||
* @param {uint16_t} mem_address 内部内存地址
|
||
* @param {uint16_t} mem_addsize 内部内存地址大小
|
||
* @param {uint8_t} *data 数据缓冲区的指针
|
||
* @param {uint16_t} size 要发送的数据量
|
||
* @return {*}
|
||
* @note
|
||
*/
|
||
static BOOL _write_mem_dma(i2c_t *handle, uint16_t mem_address, uint16_t mem_addsize, uint8_t *data, uint16_t size)
|
||
{
|
||
DBG_ASSERT(handle != NULL __DBG_LINE);
|
||
DBG_ASSERT(data != NULL __DBG_LINE);
|
||
if (LL_I2C_IsActiveFlag_BUSY(handle->i2c) == 1)
|
||
{
|
||
i2c_reset(handle->i2c); // xsh:重置I2C,修复一段时间后无法读写的问题
|
||
}
|
||
uint16_t count = 2000;
|
||
handle->txsize = 0;
|
||
handle->tx_dma_ok = FALSE;
|
||
for (uint8_t i = mem_addsize; i > 0; i--)
|
||
{
|
||
handle->txbuf[handle->txsize++] = (uint8_t)(mem_address >> (8 * (i - 1)));
|
||
}
|
||
osel_memcpy(&handle->txbuf[handle->txsize], data, size);
|
||
handle->txsize += size;
|
||
LL_DMA_SetDataLength(handle->dma, handle->dma_tx_channel, handle->txsize);
|
||
LL_DMA_EnableChannel(handle->dma, handle->dma_tx_channel);
|
||
|
||
LL_I2C_HandleTransfer(handle->i2c, handle->w_address, LL_I2C_ADDRSLAVE_7BIT, handle->txsize, LL_I2C_MODE_AUTOEND, LL_I2C_GENERATE_START_WRITE);
|
||
|
||
count = 2000;
|
||
while (!handle->tx_dma_ok)
|
||
{
|
||
if (count-- == 0)
|
||
{
|
||
handle->tx_dma_ok = TRUE;
|
||
return FALSE;
|
||
}
|
||
}
|
||
count = 2000;
|
||
while (!LL_I2C_IsActiveFlag_STOP(handle->i2c))
|
||
{
|
||
if (count-- == 0)
|
||
{
|
||
handle->tx_dma_ok = TRUE;
|
||
return FALSE;
|
||
}
|
||
}
|
||
|
||
LL_DMA_DisableChannel(handle->dma, handle->dma_tx_channel);
|
||
LL_I2C_ClearFlag_STOP(handle->i2c);
|
||
|
||
return TRUE;
|
||
}
|
||
|
||
/**
|
||
* @brief 非阻塞模式下使用DMA从特定内存地址读取数据
|
||
* @param {i2c_t} *handle 指向一个i2c_t结构体,其中包含指定I2C的配置信息
|
||
* @param {uint16_t} dev_address 目标设备地址:在数据手册中,设备7位地址值需要向左移动以调用接口
|
||
* @param {uint16_t} mem_address 内部内存地址
|
||
* @param {uint16_t} mem_addsize 内部内存地址大小
|
||
* @param {uint8_t} *data 数据缓冲区的指针
|
||
* @param {uint16_t} size 要发送的数据量
|
||
* @return {*}
|
||
* @note
|
||
*/
|
||
static BOOL _read_mem_dma(i2c_t *handle, uint16_t mem_address, uint16_t mem_addsize, uint8_t *data, uint16_t size)
|
||
{
|
||
DBG_ASSERT(handle != NULL __DBG_LINE);
|
||
DBG_ASSERT(data != NULL __DBG_LINE);
|
||
if (LL_I2C_IsActiveFlag_BUSY(handle->i2c) == 1)
|
||
{
|
||
i2c_reset(handle->i2c); // xsh:重置I2C,修复一段时间后无法读写的问题
|
||
}
|
||
uint16_t count = 5000;
|
||
handle->txsize = 0;
|
||
handle->tx_dma_ok = FALSE;
|
||
handle->rx_dma_ok = FALSE;
|
||
|
||
for (uint8_t i = mem_addsize; i > 0; i--)
|
||
{
|
||
handle->txbuf[handle->txsize++] = (uint8_t)(mem_address >> (8 * (i - 1)));
|
||
}
|
||
|
||
LL_DMA_SetDataLength(handle->dma, handle->dma_tx_channel, handle->txsize);
|
||
LL_DMA_EnableChannel(handle->dma, handle->dma_tx_channel);
|
||
|
||
LL_I2C_HandleTransfer(handle->i2c, handle->w_address, LL_I2C_ADDRSLAVE_7BIT, handle->txsize, LL_I2C_MODE_SOFTEND, LL_I2C_GENERATE_START_WRITE);
|
||
count = 5000;
|
||
while (!handle->tx_dma_ok)
|
||
{
|
||
if (count-- == 0)
|
||
{
|
||
handle->tx_dma_ok = TRUE;
|
||
return FALSE;
|
||
}
|
||
}
|
||
|
||
count = 5000;
|
||
while (LL_I2C_IsActiveFlag_TC(handle->i2c) != 1)
|
||
{
|
||
if (count-- == 0)
|
||
{
|
||
handle->tx_dma_ok = TRUE;
|
||
return FALSE;
|
||
}
|
||
}
|
||
|
||
handle->tx_dma_ok = FALSE;
|
||
handle->rx_dma_ok = FALSE;
|
||
handle->rxsize = size;
|
||
osel_memset(handle->rxbuf, 0, handle->rxsize);
|
||
LL_DMA_DisableChannel(handle->dma, handle->dma_tx_channel);
|
||
LL_DMA_SetDataLength(handle->dma, handle->dma_rx_channel, handle->rxsize);
|
||
LL_DMA_EnableChannel(handle->dma, handle->dma_rx_channel);
|
||
LL_I2C_HandleTransfer(handle->i2c, handle->r_address, LL_I2C_ADDRSLAVE_7BIT, handle->rxsize, LL_I2C_MODE_AUTOEND, LL_I2C_GENERATE_RESTART_7BIT_READ);
|
||
|
||
count = 10000;
|
||
while (!LL_I2C_IsActiveFlag_STOP(handle->i2c))
|
||
{
|
||
if (count-- == 0)
|
||
{
|
||
handle->tx_dma_ok = TRUE;
|
||
return FALSE;
|
||
}
|
||
}
|
||
|
||
LL_DMA_DisableChannel(handle->dma, handle->dma_rx_channel);
|
||
LL_I2C_ClearFlag_STOP(handle->i2c);
|
||
osel_memcpy(data, handle->rxbuf, handle->rxsize);
|
||
return TRUE;
|
||
}
|
||
/**
|
||
* @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结构体
|
||
return handle;
|
||
}
|
||
|
||
/**
|
||
* @brief 创建一个I2C总线设备 DMA
|
||
* @param {I2C_TypeDef} *i2c
|
||
* @param {DMA_TypeDef} *dma
|
||
* @param {uint16_t} rxsize
|
||
* @param {uint32_t} dma_rx_channel
|
||
* @param {uint16_t} txsize
|
||
* @param {uint32_t} dma_tx_channel
|
||
* @return {*}
|
||
* @note
|
||
*/
|
||
i2c_t *i2c_create_dma(I2C_TypeDef *i2c, DMA_TypeDef *dma, uint16_t rxsize, uint32_t dma_rx_channel,
|
||
i2cs_dma_callback *dma_rx_cb, uint16_t txsize, uint32_t dma_tx_channel, i2cs_dma_callback *dma_tx_cb)
|
||
{
|
||
i2c_t *handle = (i2c_t *)osel_mem_alloc(sizeof(i2c_t));
|
||
handle->i2c = i2c;
|
||
handle->dma = dma;
|
||
handle->dma_rx_channel = dma_rx_channel;
|
||
handle->dma_tx_channel = dma_tx_channel;
|
||
handle->rxbuf = (uint8_t *)osel_mem_alloc(rxsize);
|
||
handle->txbuf = (uint8_t *)osel_mem_alloc(txsize);
|
||
handle->rxsize = rxsize;
|
||
handle->txsize = txsize;
|
||
handle->tx_dma_ok = TRUE;
|
||
handle->interface.write_mem_dma = _write_mem_dma;
|
||
handle->interface.read_mem_dma = _read_mem_dma;
|
||
if (dma_rx_cb != NULL)
|
||
{
|
||
handle->dma_rx_cb = dma_rx_cb;
|
||
}
|
||
|
||
if (dma_tx_cb != NULL)
|
||
{
|
||
handle->dma_tx_cb = dma_tx_cb;
|
||
}
|
||
|
||
LL_DMA_DisableChannel(dma, dma_tx_channel);
|
||
LL_DMA_DisableChannel(dma, dma_rx_channel);
|
||
|
||
// TX
|
||
uint8_t *pTransmitBuffer = handle->txbuf;
|
||
LL_DMA_ConfigTransfer(dma, dma_tx_channel, LL_DMA_DIRECTION_MEMORY_TO_PERIPH | LL_DMA_PRIORITY_HIGH | LL_DMA_MODE_NORMAL | LL_DMA_PERIPH_NOINCREMENT | LL_DMA_MEMORY_INCREMENT | LL_DMA_PDATAALIGN_BYTE | LL_DMA_MDATAALIGN_BYTE);
|
||
LL_DMA_ConfigAddresses(dma, dma_tx_channel, (uint32_t)pTransmitBuffer, (uint32_t)LL_I2C_DMA_GetRegAddr(i2c, LL_I2C_DMA_REG_DATA_TRANSMIT), LL_DMA_GetDataTransferDirection(dma, dma_tx_channel));
|
||
LL_DMA_SetPeriphRequest(dma, dma_tx_channel, LL_DMA_REQUEST_3);
|
||
|
||
// RX
|
||
uint8_t *pReceiveBuffer = handle->rxbuf;
|
||
LL_DMA_ConfigTransfer(dma, dma_rx_channel, LL_DMA_DIRECTION_PERIPH_TO_MEMORY | LL_DMA_PRIORITY_HIGH | LL_DMA_MODE_NORMAL | LL_DMA_PERIPH_NOINCREMENT | LL_DMA_MEMORY_INCREMENT | LL_DMA_PDATAALIGN_BYTE | LL_DMA_MDATAALIGN_BYTE);
|
||
LL_DMA_ConfigAddresses(dma, dma_rx_channel, (uint32_t)LL_I2C_DMA_GetRegAddr(i2c, LL_I2C_DMA_REG_DATA_RECEIVE), (uint32_t)pReceiveBuffer, LL_DMA_GetDataTransferDirection(dma, dma_rx_channel));
|
||
LL_DMA_SetPeriphRequest(dma, dma_rx_channel, LL_DMA_REQUEST_3);
|
||
|
||
LL_DMA_EnableIT_TC(dma, dma_tx_channel);
|
||
LL_DMA_EnableIT_TE(dma, dma_tx_channel);
|
||
LL_DMA_EnableIT_TC(dma, dma_rx_channel);
|
||
LL_DMA_EnableIT_TE(dma, dma_rx_channel);
|
||
|
||
LL_I2C_EnableDMAReq_TX(i2c);
|
||
LL_I2C_EnableDMAReq_RX(i2c);
|
||
LL_I2C_Enable(i2c);
|
||
|
||
return handle;
|
||
}
|
||
|
||
/**
|
||
* @brief 设置I2C器件地址
|
||
* @param {i2c_t} *handle
|
||
* @param {uint8_t} w_address 写地址
|
||
* @param {uint8_t} r_address 读地址
|
||
* @return {*}
|
||
* @note
|
||
*/
|
||
void i2c_dma_set_address(i2c_t *handle, uint8_t w_address, uint8_t r_address)
|
||
{
|
||
DBG_ASSERT(handle != NULL __DBG_LINE);
|
||
handle->w_address = w_address;
|
||
handle->r_address = r_address;
|
||
}
|
||
|
||
/**
|
||
* @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 I2C DMA TX回调函数
|
||
* @param {i2c_t} handle
|
||
* @return {*}
|
||
* @note
|
||
*/
|
||
void i2c_dma_callback(i2c_t *handle)
|
||
{
|
||
// 检查输入参数是否为空
|
||
DBG_ASSERT(handle != NULL __DBG_LINE);
|
||
// 清除传输完成标志位
|
||
if (handle->dma_tx_cb != NULL)
|
||
{
|
||
handle->dma_tx_cb(handle);
|
||
}
|
||
if (handle->dma_rx_cb != NULL)
|
||
{
|
||
handle->dma_rx_cb(handle);
|
||
}
|
||
}
|
||
|
||
/**
|
||
* @brief I2C 中断回调函数
|
||
* @param {i2c_t} *handle
|
||
* @return {*}
|
||
* @note
|
||
*/
|
||
void i2c_ev_callback(i2c_t *handle)
|
||
{
|
||
DBG_ASSERT(handle != NULL __DBG_LINE);
|
||
if (LL_I2C_IsActiveFlag_ADDR(handle->i2c))
|
||
{
|
||
/* Verify the Address Match with the OWN Slave address */
|
||
if (LL_I2C_GetAddressMatchCode(handle->i2c) == handle->w_address || LL_I2C_GetAddressMatchCode(handle->i2c) == handle->r_address)
|
||
{
|
||
/* Verify the transfer direction, a write direction, Slave enters receiver mode */
|
||
if (LL_I2C_GetTransferDirection(handle->i2c) == LL_I2C_DIRECTION_WRITE)
|
||
{
|
||
/* Clear ADDR flag value in ISR register */
|
||
LL_I2C_ClearFlag_ADDR(handle->i2c);
|
||
|
||
/* Enable Receive Interrupt */
|
||
LL_I2C_EnableIT_RX(handle->i2c);
|
||
}
|
||
else if (LL_I2C_GetTransferDirection(handle->i2c) == LL_I2C_DIRECTION_READ)
|
||
{
|
||
/* Clear ADDR flag value in ISR register */
|
||
LL_I2C_ClearFlag_ADDR(handle->i2c);
|
||
|
||
/* Enable Transmit Interrupt */
|
||
LL_I2C_EnableIT_TX(handle->i2c);
|
||
}
|
||
}
|
||
else
|
||
{
|
||
/* Clear ADDR flag value in ISR register */
|
||
LL_I2C_ClearFlag_ADDR(handle->i2c);
|
||
|
||
/* Call Error function */
|
||
DBG_ASSERT(FALSE __DBG_LINE);
|
||
}
|
||
}
|
||
/* Check NACK flag value in ISR register */
|
||
else if (LL_I2C_IsActiveFlag_NACK(handle->i2c))
|
||
{
|
||
/* End of Transfer */
|
||
LL_I2C_ClearFlag_NACK(handle->i2c);
|
||
}
|
||
/* Check RXNE flag value in ISR register */
|
||
else if (LL_I2C_IsActiveFlag_RXNE(handle->i2c))
|
||
{
|
||
/* Call function Slave Reception Callback */
|
||
}
|
||
/* Check TXIS flag value in ISR register */
|
||
else if (LL_I2C_IsActiveFlag_TXIS(handle->i2c))
|
||
{
|
||
/* Call function Slave Ready to Transmit Callback */
|
||
}
|
||
/* Check STOP flag value in ISR register */
|
||
else if (LL_I2C_IsActiveFlag_STOP(handle->i2c))
|
||
{
|
||
/* End of Transfer */
|
||
LL_I2C_ClearFlag_STOP(handle->i2c);
|
||
|
||
/* Check TXE flag value in ISR register */
|
||
if (!LL_I2C_IsActiveFlag_TXE(handle->i2c))
|
||
{
|
||
/* Flush TX buffer */
|
||
LL_I2C_ClearFlag_TXE(handle->i2c);
|
||
}
|
||
|
||
/* Call function Slave Complete Callback */
|
||
}
|
||
/* Check TXE flag value in ISR register */
|
||
else if (!LL_I2C_IsActiveFlag_TXE(handle->i2c))
|
||
{
|
||
/* Do nothing */
|
||
/* This Flag will be set by hardware when the TXDR register is empty */
|
||
/* If needed, use LL_I2C_ClearFlag_TXE() interface to flush the TXDR register */
|
||
}
|
||
else
|
||
{
|
||
/* Call Error function */
|
||
DBG_ASSERT(FALSE __DBG_LINE);
|
||
}
|
||
}
|
||
|
||
/*下面是内部实现方法*/
|
||
|
||
/**
|
||
* @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);
|
||
}
|