492 lines
16 KiB
C
492 lines
16 KiB
C
#include "spis.h"
|
||
static inline void spi_delay(spi_t *handle); // 延时函数
|
||
|
||
static inline void spi_rdy_high(spi_t *handle); // RDY高电平
|
||
static inline void spi_rdy_low(spi_t *handle); // RDY低电平
|
||
static inline void spi_cs_high(spi_t *handle); // CS高电平
|
||
static inline void spi_cs_low(spi_t *handle); // CS低电平
|
||
static inline void spi_mosi_high(spi_t *handle); // MOSI高电平
|
||
static inline void spi_mosi_low(spi_t *handle); // MOSI低电平
|
||
static inline void spi_sck_high(spi_t *handle); // SCK高电平
|
||
static inline void spi_sck_low(spi_t *handle); // SCK低电平
|
||
static inline uint8_t spi_miso_read(spi_t *handle); // 读取MISO电平
|
||
|
||
static uint8_t spi_read_write_byte(spi_t *handle, uint8_t tx_data); // 读写一个字节
|
||
|
||
static void _hardware_enable(spi_t *handle, SPI_TypeDef *spi); // 硬件SPI
|
||
static void _dma_enable(spi_t *handle, DMA_TypeDef *dma, uint32_t dma_rx_channel, spis_dma_callback *dma_rx_cb,
|
||
uint32_t dma_tx_channel, spis_dma_callback *dma_tx_cb); // DMA SPI
|
||
static void _spi_dma_callback(spi_t *handle); // DMA发送完成回调
|
||
static void _spi_dma_send(spi_t *handle, uint8_t *data, uint16_t length); // DMA发送数据
|
||
static uint8_t _read_drdy(spi_t *handle); // 读取DRDY电平
|
||
static uint8_t _write_regs(spi_t *handle, uint8_t reg, uint8_t *data, uint8_t len); // 写多个寄存器
|
||
static uint8_t _read_regs(spi_t *handle, uint8_t reg, uint8_t *data, uint8_t len); // 读多个寄存器
|
||
static uint8_t _spi_write_reg(spi_t *handle, uint8_t reg, uint8_t data); // 写单个寄存器
|
||
static uint8_t _spi_read_reg(spi_t *handle, uint8_t reg); // 读单个寄存器
|
||
static uint8_t _spi_write_cmd(spi_t *handle, uint8_t cmd); // 写命令
|
||
static uint8_t _spi_write_data(spi_t *handle, uint8_t *data, uint16_t len); // 写数据
|
||
|
||
/**
|
||
* @brief 创建一个SPI总线设备
|
||
* @param {spi_type_e} spi_type SPI总线的类型
|
||
* @param {spi_gpio_group_t} gpios SPI总线的GPIO配置
|
||
* @param {uint16_t} delay_ticks SPI总线的延时参数
|
||
* @return {*} 创建的SPI总线设备句柄
|
||
* @note: 该函数用于创建一个SPI总线设备。它首先断言spi_type在有效的范围内,然后创建一个spi_t结构体,并将gpios和delay_ticks的内存地址复制到handle结构体中。接着,根据spi_type的值,设置不同的接口函数。最后,返回handle结构体。
|
||
*/
|
||
spi_t *spi_create(spi_type_e spi_type, spi_gpio_group_t gpios, uint16_t delay_ticks)
|
||
{
|
||
DBG_ASSERT(spi_type < SPI_TYPE_MAX __DBG_LINE);
|
||
spi_t *handle = (spi_t *)osel_mem_alloc(sizeof(spi_t));
|
||
osel_memcpy((uint8_t *)&handle->gpios, (uint8_t *)&gpios, sizeof(spi_gpio_group_t));
|
||
handle->delay_ticks = delay_ticks;
|
||
handle->spi_type = spi_type;
|
||
handle->simualte_gpio = TRUE;
|
||
handle->interface.hardware_enable = _hardware_enable;
|
||
handle->interface.dma_enable = _dma_enable;
|
||
handle->interface.spi_dma_send = _spi_dma_send;
|
||
handle->interface.spi_dma_callback = _spi_dma_callback;
|
||
if (spi_type == SPI_TYPE_NORMAL)
|
||
{
|
||
handle->interface.u.normal.write_reg = _spi_write_reg;
|
||
handle->interface.u.normal.read_reg = _spi_read_reg;
|
||
handle->interface.u.normal.write_regs = _write_regs;
|
||
handle->interface.u.normal.read_regs = _read_regs;
|
||
handle->interface.u.normal.read_drdy = _read_drdy;
|
||
|
||
handle->interface.u.normal.spi_send = spi_read_write_byte;
|
||
}
|
||
else if (spi_type == SPI_TYPE_LCD)
|
||
{
|
||
handle->interface.u.lcd.write_cmd = _spi_write_cmd;
|
||
handle->interface.u.lcd.write_data = _spi_write_data;
|
||
}
|
||
else
|
||
{
|
||
DBG_ASSERT(FALSE __DBG_LINE);
|
||
}
|
||
return handle;
|
||
}
|
||
|
||
void spi_free(spi_t *handle)
|
||
{
|
||
if (handle != NULL)
|
||
{
|
||
gpio_free(handle->gpios.cs);
|
||
gpio_free(handle->gpios.rdy);
|
||
gpio_free(handle->gpios.rst);
|
||
gpio_free(handle->gpios.mosi);
|
||
gpio_free(handle->gpios.miso);
|
||
gpio_free(handle->gpios.sck);
|
||
|
||
osel_mem_free(handle);
|
||
}
|
||
}
|
||
|
||
/**
|
||
* @brief 硬件SPI模式
|
||
* @param {spi_t} *handle SPI总线设备句柄
|
||
* @param {SPI_TypeDef} *spi SPI总线的寄存器结构体指针
|
||
* @return {*} 无
|
||
* @note: 该函数用于设置SPI总线的硬件模式。它首先断言handle不为空,然后断言spix不为空。接着,将handle的simulate_gpio设置为FALSE,并将spix的地址赋值给handle->spix。最后,调用SPI_ENABLE函数启用SPI总线。
|
||
*/
|
||
static void _hardware_enable(spi_t *handle, SPI_TypeDef *spi)
|
||
{
|
||
DBG_ASSERT(handle != NULL __DBG_LINE);
|
||
DBG_ASSERT(spi != NULL __DBG_LINE);
|
||
handle->simualte_gpio = FALSE;
|
||
handle->spi = spi;
|
||
SPI_ENABLE(spi);
|
||
}
|
||
|
||
static void _dma_enable(spi_t *handle, DMA_TypeDef *dma, uint32_t dma_rx_channel, spis_dma_callback *dma_rx_cb,
|
||
uint32_t dma_tx_channel, spis_dma_callback *dma_tx_cb)
|
||
{
|
||
DBG_ASSERT(handle != NULL __DBG_LINE);
|
||
DBG_ASSERT(dma != NULL __DBG_LINE);
|
||
DBG_ASSERT(handle->spi != NULL __DBG_LINE);
|
||
handle->dma = dma;
|
||
handle->dma_rx_channel = dma_rx_channel;
|
||
handle->dma_tx_channel = dma_tx_channel;
|
||
handle->rx_dma_ok = TRUE;
|
||
handle->tx_dma_ok = TRUE;
|
||
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_SetPeriphAddress(dma, dma_tx_channel, LL_SPI_DMA_GetRegAddr(handle->spi));
|
||
LL_DMA_ClearFlag_GI1(dma);
|
||
LL_DMA_ClearFlag_TC1(dma);
|
||
LL_DMA_EnableIT_TC(dma, dma_tx_channel);
|
||
LL_SPI_EnableDMAReq_TX(handle->spi);
|
||
LL_SPI_Enable(handle->spi);
|
||
}
|
||
|
||
static void _spi_dma_callback(spi_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);
|
||
}
|
||
}
|
||
|
||
static void _spi_dma_send(spi_t *handle, uint8_t *data, uint16_t length)
|
||
{
|
||
handle->tx_dma_ok = FALSE;
|
||
LL_DMA_DisableChannel(handle->dma, handle->dma_tx_channel);
|
||
|
||
LL_DMA_SetMemoryAddress(handle->dma, handle->dma_tx_channel, (uint32_t)data);
|
||
LL_DMA_SetDataLength(handle->dma, handle->dma_tx_channel, length);
|
||
|
||
LL_DMA_EnableChannel(handle->dma, handle->dma_tx_channel);
|
||
|
||
uint16_t i = 0;
|
||
while (handle->tx_dma_ok == FALSE)
|
||
{
|
||
i++;
|
||
if (i > 60000)
|
||
{
|
||
__NOP();
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
|
||
/**
|
||
* @brief 读取数据准备好信号
|
||
* @param {spi_t} *handle SPI总线设备句柄
|
||
* @return {uint8_t} 数据准备好信号的值
|
||
* @note: 该函数用于读取SPI总线的数据准备好信号。它首先断言handle不为空,然后返回handle的gpios.rdy的读取结果。
|
||
*/
|
||
static uint8_t _read_drdy(spi_t *handle)
|
||
{
|
||
DBG_ASSERT(handle != NULL __DBG_LINE);
|
||
return handle->gpios.rdy->read(*handle->gpios.rdy);
|
||
}
|
||
|
||
/**
|
||
* @brief 写入单个寄存器
|
||
* @param {spi_t} *handle SPI总线设备句柄
|
||
* @param {uint8_t} reg 寄存器号
|
||
* @param {uint8_t} data 寄存器值
|
||
* @return {*} 操作结果
|
||
* @note: 该函数用于写入SPI总线的单个寄存器。它首先断言handle不为空,然后断言data不为空。接着,将SPI总线的CS引脚设置为低电平,发送寄存器号和寄存器值,最后将SPI总线的CS引脚设置为高电平,返回操作结果。
|
||
*/
|
||
static uint8_t _write_regs(spi_t *handle, uint8_t reg, uint8_t *data, uint8_t len)
|
||
{
|
||
uint8_t status = 0;
|
||
DBG_ASSERT(handle != NULL __DBG_LINE);
|
||
DBG_ASSERT(data != NULL __DBG_LINE);
|
||
DBG_ASSERT(len != 0 __DBG_LINE);
|
||
spi_cs_low(handle);
|
||
status = spi_read_write_byte(handle, reg); // 发送寄存器号
|
||
while (len--)
|
||
{
|
||
spi_read_write_byte(handle, *data); // 写入寄存器的值
|
||
data++;
|
||
}
|
||
spi_cs_high(handle);
|
||
|
||
return status;
|
||
}
|
||
|
||
/**
|
||
* @brief 读取多个寄存器
|
||
* @param {spi_t} *handle SPI总线设备句柄
|
||
* @param {uint8_t} reg 寄存器号
|
||
* @param {uint8_t} *data 寄存器值的指针
|
||
* @param {uint8_t} len 寄存器值的个数
|
||
* @return {uint8_t} 操作结果
|
||
* @note: 该函数用于读取SPI总线的多个寄存器。它首先断言handle不为空,然后断言data不为空,接着断言len大于0。接着,将SPI总线的CS引脚设置为低电平,发送寄存器号,然后循环读取多个寄存器的值,最后将SPI总线的CS引脚设置为高电平,返回操作结果。
|
||
*/
|
||
static uint8_t _read_regs(spi_t *handle, uint8_t reg, uint8_t *data, uint8_t len)
|
||
{
|
||
uint8_t status = 0;
|
||
|
||
DBG_ASSERT(handle != NULL __DBG_LINE);
|
||
DBG_ASSERT(data != NULL __DBG_LINE);
|
||
DBG_ASSERT(len != 0 __DBG_LINE);
|
||
|
||
spi_cs_low(handle);
|
||
status = spi_read_write_byte(handle, reg);
|
||
for (uint8_t i = 0; i < len; i++)
|
||
{
|
||
data[i] = spi_read_write_byte(handle, 0xff); // 读出数据
|
||
}
|
||
spi_cs_high(handle);
|
||
|
||
return status;
|
||
}
|
||
|
||
/**
|
||
* @brief 写入单个寄存器
|
||
* @param {spi_t} *handle SPI总线设备句柄
|
||
* @param {uint8_t} reg 寄存器号
|
||
* @param {uint8_t} data 寄存器值
|
||
* @return {*} 操作结果
|
||
* @note: 该函数用于写入SPI总线的单个寄存器。它首先断言handle不为空,然后将SPI总线的CS引脚设置为低电平,发送寄存器号和寄存器值,最后将SPI总线的CS引脚设置为高电平,返回操作结果。
|
||
*/
|
||
static uint8_t _spi_write_reg(spi_t *handle, uint8_t reg, uint8_t data)
|
||
{
|
||
uint8_t status = 0;
|
||
DBG_ASSERT(handle != NULL __DBG_LINE);
|
||
|
||
spi_cs_low(handle);
|
||
status = spi_read_write_byte(handle, reg); // 发送寄存器号
|
||
spi_read_write_byte(handle, data); // 写入寄存器的值
|
||
|
||
spi_cs_high(handle);
|
||
|
||
return status;
|
||
}
|
||
|
||
/**
|
||
* @brief 读取单个寄存器
|
||
* @param {spi_t} *handle SPI总线设备句柄
|
||
* @param {uint8_t} reg 寄存器号
|
||
* @return {*} 寄存器值
|
||
* @note: 该函数用于读取SPI总线的单个寄存器。它首先断言handle不为空,然后将SPI总线的CS引脚设置为低电平,发送寄存器号,接着读取寄存器的值,最后将SPI总线的CS引脚设置为高电平,返回寄存器值。
|
||
*/
|
||
static uint8_t _spi_read_reg(spi_t *handle, uint8_t reg)
|
||
{
|
||
uint8_t reg_val = 0;
|
||
DBG_ASSERT(handle != NULL __DBG_LINE);
|
||
|
||
spi_cs_low(handle);
|
||
spi_read_write_byte(handle, reg); // 发送寄存器号
|
||
reg_val = spi_read_write_byte(handle, 0);
|
||
spi_cs_high(handle);
|
||
|
||
return reg_val;
|
||
}
|
||
|
||
/**
|
||
* @brief 写入命令
|
||
* @param {spi_t} *handle SPI总线设备句柄
|
||
* @param {uint8_t} cmd 命令
|
||
* @return {*} 操作结果
|
||
* @note: 该函数用于写入SPI总线的命令。它首先断言handle不为空,然后将SPI总线的RDY引脚设置为低电平,发送命令,最后将SPI总线的CS引脚设置为高电平,返回操作结果。
|
||
*/
|
||
static uint8_t _spi_write_cmd(spi_t *handle, uint8_t cmd)
|
||
{
|
||
uint8_t status = 0;
|
||
|
||
DBG_ASSERT(handle != NULL __DBG_LINE);
|
||
spi_rdy_low(handle);
|
||
spi_cs_low(handle);
|
||
status = spi_read_write_byte(handle, cmd); // 发送命令
|
||
spi_cs_high(handle);
|
||
return status;
|
||
}
|
||
|
||
/**
|
||
* @brief 写入数据
|
||
* @param {spi_t} *handle SPI总线设备句柄
|
||
* @param {uint8_t} *data 要写入的数据的指针
|
||
* @param {uint16_t} len 要写入的数据的长度
|
||
* @return {*} 操作结果
|
||
* @note: 该函数用于写入SPI总线的数据。它首先断言handle不为空,然后将SPI总线的RDY引脚设置为高电平,发送数据,最后将SPI总线的CS引脚设置为高电平,返回操作结果。
|
||
*/
|
||
static uint8_t _spi_write_data(spi_t *handle, uint8_t *data, uint16_t len)
|
||
{
|
||
uint8_t status = 0;
|
||
DBG_ASSERT(handle != NULL __DBG_LINE);
|
||
spi_rdy_high(handle);
|
||
spi_cs_low(handle);
|
||
for (uint16_t i = 0; i < len; i++)
|
||
{
|
||
status = spi_read_write_byte(handle, *data);
|
||
data++;
|
||
}
|
||
spi_cs_high(handle);
|
||
return status;
|
||
}
|
||
|
||
/**
|
||
* @brief SPI延时函数
|
||
* @param {spi_t} *handle SPI总线设备句柄
|
||
* @return {*} 无
|
||
* @note: 该函数用于SPI总线的延时。它首先断言handle不为空,然后根据handle的delay_ticks的值,循环执行NOP指令。
|
||
*/
|
||
static inline void spi_delay(spi_t *handle)
|
||
{
|
||
uint16_t count = 0;
|
||
DBG_ASSERT(handle != NULL __DBG_LINE);
|
||
count = handle->delay_ticks;
|
||
while (count--)
|
||
{
|
||
__NOP();
|
||
}
|
||
}
|
||
|
||
/**
|
||
* @brief 读写一个字节
|
||
* @param {spi_t} *handle SPI总线设备句柄
|
||
* @param {uint8_t} tx_data 要写入的数据
|
||
* @return {*} 读取到的数据
|
||
* @note: 该函数用于SPI总线的读写一个字节。它首先断言handle不为空,然后根据handle的simulate_gpio的值,选择模拟SPI或硬件SPI。最后,返回读取到的数据。
|
||
*/
|
||
static uint8_t spi_read_write_byte(spi_t *handle, uint8_t tx_data)
|
||
{
|
||
uint8_t bit_ctr;
|
||
uint8_t rdata = 0xff;
|
||
uint16_t i = 0;
|
||
DBG_ASSERT(handle != NULL __DBG_LINE);
|
||
if (handle->simualte_gpio == TRUE)
|
||
{
|
||
// 模拟SPI
|
||
for (bit_ctr = 0; bit_ctr < 8; bit_ctr++)
|
||
{
|
||
spi_sck_low(handle);
|
||
spi_delay(handle);
|
||
|
||
if (tx_data & 0x80)
|
||
spi_mosi_high(handle);
|
||
else
|
||
spi_mosi_low(handle);
|
||
|
||
spi_delay(handle);
|
||
spi_sck_high(handle);
|
||
spi_delay(handle);
|
||
tx_data = (tx_data << 1);
|
||
rdata = rdata << 1;
|
||
|
||
if (NULL != handle->gpios.miso->port)
|
||
{
|
||
if (spi_miso_read(handle) == 1)
|
||
rdata += 0x01;
|
||
}
|
||
}
|
||
return (rdata);
|
||
}
|
||
else
|
||
{
|
||
// 硬件SPI
|
||
LL_SPI_TransmitData8(handle->spi, tx_data);
|
||
while (LL_SPI_IsActiveFlag_TXE(handle->spi) == 0)
|
||
{
|
||
i++;
|
||
if (i > 2000)
|
||
break;
|
||
}
|
||
i = 0;
|
||
while (LL_SPI_IsActiveFlag_RXNE(handle->spi) == 0)
|
||
{
|
||
i++;
|
||
if (i > 2000)
|
||
break;
|
||
}
|
||
return LL_SPI_ReceiveData8(handle->spi);
|
||
}
|
||
}
|
||
|
||
/**
|
||
* @brief 用于将SPI(串行外围接口)设备的复位(RST)引脚设置为高
|
||
* @param {spi_id_e} id
|
||
* @return {*}
|
||
*/
|
||
static inline void spi_rdy_high(spi_t *handle)
|
||
{
|
||
DBG_ASSERT(handle != NULL __DBG_LINE);
|
||
handle->gpios.rdy->set(*handle->gpios.rdy);
|
||
}
|
||
|
||
/**
|
||
* @brief 用于将SPI(串行外围接口)设备的复位(RST)引脚设置为低
|
||
* @param {spi_id_e} id
|
||
* @return {*}
|
||
*/
|
||
static inline void spi_rdy_low(spi_t *handle)
|
||
{
|
||
DBG_ASSERT(handle != NULL __DBG_LINE);
|
||
handle->gpios.rdy->reset(*handle->gpios.rdy);
|
||
}
|
||
|
||
/**
|
||
* @brief 用于将SPI(串行外围接口)设备的芯片选择(CS)引脚设置为高
|
||
* @param {spi_id_e} id
|
||
* @return {*}
|
||
*/
|
||
static inline void spi_cs_high(spi_t *handle)
|
||
{
|
||
DBG_ASSERT(handle != NULL __DBG_LINE);
|
||
handle->gpios.cs->set(*handle->gpios.cs);
|
||
}
|
||
|
||
/**
|
||
* @brief 用于将SPI(串行外围接口)设备的芯片选择(CS)引脚设置为低
|
||
* @param {spi_id_e} id
|
||
* @return {*}
|
||
*/
|
||
static inline void spi_cs_low(spi_t *handle)
|
||
{
|
||
DBG_ASSERT(handle != NULL __DBG_LINE);
|
||
handle->gpios.cs->reset(*handle->gpios.cs);
|
||
}
|
||
|
||
/**
|
||
* @brief 用于将SPI(串行外围接口)设备的时钟(SCK)引脚设置为高
|
||
* @param {spi_id_e} id
|
||
* @return {*}
|
||
*/
|
||
static inline void spi_mosi_high(spi_t *handle)
|
||
{
|
||
DBG_ASSERT(handle != NULL __DBG_LINE);
|
||
handle->gpios.mosi->set(*handle->gpios.mosi);
|
||
}
|
||
|
||
/**
|
||
* @brief 用于将SPI(串行外围接口)设备的输出(MOSI)引脚设置为低
|
||
* @param {spi_id_e} id
|
||
* @return {*}
|
||
*/
|
||
static inline void spi_mosi_low(spi_t *handle)
|
||
{
|
||
DBG_ASSERT(handle != NULL __DBG_LINE);
|
||
handle->gpios.mosi->reset(*handle->gpios.mosi);
|
||
}
|
||
|
||
/**
|
||
* @brief 用于将SPI(串行外围接口)设备的时钟(SCK)引脚设置为高
|
||
* @param {spi_id_e} id
|
||
* @return {*}
|
||
*/
|
||
static inline void spi_sck_high(spi_t *handle)
|
||
{
|
||
DBG_ASSERT(handle != NULL __DBG_LINE);
|
||
handle->gpios.sck->set(*handle->gpios.sck);
|
||
}
|
||
|
||
/**
|
||
* @brief 用于将SPI(串行外围接口)设备的时钟(SCK)引脚设置为低
|
||
* @param {spi_id_e} id
|
||
* @return {*}
|
||
*/
|
||
static inline void spi_sck_low(spi_t *handle)
|
||
{
|
||
DBG_ASSERT(handle != NULL __DBG_LINE);
|
||
handle->gpios.sck->reset(*handle->gpios.sck);
|
||
}
|
||
|
||
/**
|
||
* @brief 用于读取SPI(串行外围接口)设备的输入(MISO)引脚的电平
|
||
* @param {spi_id_e} id
|
||
* @return {*}
|
||
*/
|
||
static inline uint8_t spi_miso_read(spi_t *handle)
|
||
{
|
||
DBG_ASSERT(handle != NULL __DBG_LINE);
|
||
return handle->gpios.miso->read(*handle->gpios.miso);
|
||
}
|