#include "mcu_io.h"
#include "mcu_timer.h"
#include "mcu_spi.h"
void mcu_spi_init(mcu_spi_t* spi, uint8_t cs_pin, uint8_t clk_pin, uint8_t mosi_pin, uint8_t miso_pin, uint8_t mode, uint8_t speed, bool msb)
{
spi->m_cs_pin = cs_pin;
spi->m_clk_pin = clk_pin;
spi->m_mosi_pin = mosi_pin;
spi->m_miso_pin = miso_pin;
spi->m_mode = mode;
spi->m_msb = msb;
switch (speed)
{
case MCU_SPI_SPEED_200K: spi->m_wait_step = 10; break;
case MCU_SPI_SPEED_400K: spi->m_wait_step = 2; break;
case MCU_SPI_SPEED_1M: spi->m_wait_step = 0; break;
default: spi->m_wait_step = 28; break;
}
mcu_io_set_mode(cs_pin, MCU_IO_MODE_OUTPUT);
mcu_io_set_mode(clk_pin, MCU_IO_MODE_OUTPUT);
mcu_io_set_mode(mosi_pin, MCU_IO_MODE_OUTPUT);
mcu_io_set_mode(miso_pin, MCU_IO_MODE_INPUT);
mcu_io_set_state(cs_pin, true);
}
void mcu_spi_transfer(mcu_spi_t* spi, uint8_t* tx, uint8_t* rx, uint16_t len)
{
uint8_t clk_pin = spi->m_clk_pin;
uint8_t mosi_pin = spi->m_mosi_pin;
uint8_t miso_pin = spi->m_miso_pin;
uint16_t wait_step = spi->m_wait_step;
static const uint8_t LSB_MASK[8] = {0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80};
static const uint8_t MSB_MASK[8] = {0x80, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x01};
const uint8_t* mask = (spi->m_msb ? MSB_MASK : LSB_MASK);
// 时钟极性(用来指示拉低片选时的状态)
uint8_t clk_init_state = (spi->m_mode&0x02) != 0;
mcu_io_set_state(clk_pin, clk_init_state);
// 片选
mcu_io_set_state(spi->m_cs_pin, false);
// 相位(用来表示以后要移动半个周期)
if ((spi->m_mode&0x01) != 0)
{
mcu_timer_wait_step(wait_step);
clk_init_state = !clk_init_state;
mcu_io_set_state(clk_pin, clk_init_state);
}
// 数据交换
for (uint16_t i = 0; i < len; i++)
{
uint8_t ch = 0xFF;
if (tx)
{
ch = tx[i];
}
if (rx)
{
rx[i] = 0;
}
for (uint8_t j = 0; j < 8; j++)
{
// 周期开始后马上写入
mcu_io_set_state(mosi_pin, (ch&mask[j]) != 0);
// 前半个周期的等待
mcu_timer_wait_step(wait_step);
// 进入下半个周期并等待对方读写
mcu_io_set_state(clk_pin, !clk_init_state);
mcu_timer_wait_step(wait_step);
// 周期快要结束时读取
if (rx && mcu_io_get_state(miso_pin))
{
rx[i] |= mask[j];
}
mcu_io_set_state(clk_pin, clk_init_state);
}
}
// 取消片选
mcu_io_set_state(spi->m_cs_pin, true);
}