#include "spis.h" #include "delay.h" #define SPI_TIMEOUT 2000 /** * @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; if (count > 0) { while (count--) { __NOP(); } } } // static void spi_reset(spi_t *handle) // { // DBG_ASSERT(handle != NULL __DBG_LINE); // handle->gpios.cs->reset(*handle->gpios.cs); // delay_tick(10); // handle->gpios.cs->set(*handle->gpios.cs); // delay_tick(10); // } /** * @brief 用于将SPI(串行外围接口)设备的复位(RST)引脚设置为高 * @param {spi_id_e} id * @return {*} */ static inline void spi_rdy_high(spi_t *handle) { DBG_ASSERT(handle != NULL __DBG_LINE); if (handle->gpios.rdy != NULL) { 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); if (handle->gpios.rdy != NULL) { 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); if (handle->gpios.cs != NULL) { handle->gpios.cs->set(*handle->gpios.cs); spi_delay(handle); } } /** * @brief 用于将SPI(串行外围接口)设备的芯片选择(CS)引脚设置为低 * @param {spi_id_e} id * @return {*} */ static inline void spi_cs_low(spi_t *handle) { DBG_ASSERT(handle != NULL __DBG_LINE); if (handle->gpios.cs != NULL) { handle->gpios.cs->reset(*handle->gpios.cs); spi_delay(handle); } } /** * @brief 用于将SPI(串行外围接口)设备的时钟(SCK)引脚设置为高 * @param {spi_id_e} id * @return {*} */ static inline void spi_mosi_high(spi_t *handle) { DBG_ASSERT(handle != NULL __DBG_LINE); if (handle->gpios.mosi != NULL) { 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); if (handle->gpios.mosi != NULL) { 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); if (handle->gpios.sck != NULL) { 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); if (handle->gpios.sck != NULL) { 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); } /** * @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; 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 { uint8_t rx_data = 0; if (HAL_SPI_TransmitReceive(handle->spi, &tx_data, &rx_data, 1, SPI_TIMEOUT) != HAL_OK) { // 处理错误 return 0xff; } return rx_data; } } /** * @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总线设备句柄 * @param {SPI_HandleTypeDef} *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_HandleTypeDef *spi) { DBG_ASSERT(handle != NULL __DBG_LINE); DBG_ASSERT(spi != NULL __DBG_LINE); handle->simualte_gpio = FALSE; handle->spi = spi; __HAL_SPI_ENABLE(handle->spi); } 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->interface.hardware_enable = _hardware_enable; if (spi_type == SPI_TYPE_NORMAL) { } 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; }