rtthread的软件模拟SPI用的上层PIN驱动写,由于经过层层封装,时钟频率并不会太高,200MHz的MCU跑不到1MHz的时钟频率。所以最好是在底层就模拟好,给上层用。
头文件
cpp
struct io_poSOFT
{
gpio_type *port;
uint16_t pin;
};
typedef struct soft_spi_ios
{
struct io_poSOFT cs;
struct io_poSOFT clk;
struct io_poSOFT mosi;
struct io_poSOFT miso;
} soft_spi_ios_t;
struct soft_spi_bus_ops;
typedef struct soft_spi_bus
{
struct soft_spi_ios *ios;
uint8_t mode;
uint32_t clktk;
void (*delay_us)(uint8_t us);
struct soft_spi_bus_ops *ops; // NOTE 先引用后声明的只能用指针
} soft_spi_bus_t;
struct soft_spi_bus_ops
{
uint8_t (*transfer)(soft_spi_bus_t *bus, uint8_t data);
void (*send_then_send)(soft_spi_bus_t *bus, uint8_t *sbuf1, uint8_t slen1, uint8_t *sbuf2, uint8_t slen2);
void (*send_then_receive)(soft_spi_bus_t *bus, uint8_t *sbuf, uint8_t slen, uint8_t *rbuf, uint8_t rlen);
};
#define SOFT_SPI_CPHA (1 << 0) /* bit[0]:CPHA, clock phase */
#define SOFT_SPI_CPOL (1 << 1) /* bit[1]:CPOL, clock polarity */
#define SOFT_SPI_LSB (0 << 2) /* bit[2]: 0-LSB */
#define SOFT_SPI_MSB (1 << 2) /* bit[2]: 1-MSB */
#define SOFT_SPI_MODE_0 (0 | 0) /* CPOL = 0, CPHA = 0 */
#define SOFT_SPI_MODE_1 (0 | SOFT_SPI_CPHA) /* CPOL = 0, CPHA = 1 */
#define SOFT_SPI_MODE_2 (SOFT_SPI_CPOL | 0) /* CPOL = 1, CPHA = 0 */
#define SOFT_SPI_MODE_3 (SOFT_SPI_CPOL | SOFT_SPI_CPHA) /* CPOL = 1, CPHA = 1 */
void soft_spi_bus_init(struct soft_spi_bus *bus, struct soft_spi_ios *ios);
void soft_spi_bus_send_then_send(struct soft_spi_bus *bus, uint8_t *sbuf1, uint8_t slen1, uint8_t *sbuf2, uint8_t slen2);
void soft_spi_bus_send_then_receive(struct soft_spi_bus *bus, uint8_t *sbuf, uint8_t slen, uint8_t *rbuf, uint8_t rlen);
源文件
cpp
#define __io_set(x) gpio_bits_set(bus->ios->x.port, bus->ios->x.pin)
#define __io_reset(x) gpio_bits_reset(bus->ios->x.port, bus->ios->x.pin)
// #define __io_read(x) ((bus->ios->x.port->idt & bus->ios->x.pin) != 0)
#define __io_read(x) gpio_input_data_bit_read(bus->ios->x.port, bus->ios->x.pin)
#define __io_write(x, y) \
if (y) \
__io_set(x); \
else \
__io_reset(x)
static rt_err_t soft_spi_configure(struct rt_spi_device *device,
struct rt_spi_configuration *configuration)
{
struct rt_spi_bus *spi_bus = (struct rt_spi_bus *)device->bus;
struct soft_spi_bus *instance = (struct soft_spi_bus *)spi_bus->parent.user_data;
// NOTE 2.5Mhz
instance->mode = configuration->mode;
gpio_bits_write(instance->ios->clk.port, instance->ios->clk.pin, (instance->mode & SOFT_SPI_CPOL)); // 空闲电平
return RT_EOK;
}
void soft_spi_bus_init(struct soft_spi_bus *bus, struct soft_spi_ios *ios)
{
gpio_init_type gpio_init_struct;
gpio_init_struct.gpio_pins = ios->clk.pin;
gpio_init_struct.gpio_mode = GPIO_MODE_OUTPUT;
gpio_init_struct.gpio_out_type = GPIO_OUTPUT_PUSH_PULL;
gpio_init_struct.gpio_drive_strength = GPIO_DRIVE_STRENGTH_MAXIMUM;
gpio_init(ios->clk.port, &gpio_init_struct);
gpio_init_struct.gpio_pins = ios->miso.pin;
gpio_init_struct.gpio_mode = GPIO_MODE_INPUT;
gpio_init_struct.gpio_pull = GPIO_PULL_NONE;
gpio_init_struct.gpio_out_type = GPIO_OUTPUT_OPEN_DRAIN;
gpio_init_struct.gpio_drive_strength = GPIO_DRIVE_STRENGTH_MAXIMUM;
gpio_init(ios->miso.port, &gpio_init_struct);
gpio_init_struct.gpio_pins = ios->mosi.pin;
gpio_init_struct.gpio_mode = GPIO_MODE_OUTPUT;
gpio_init_struct.gpio_out_type = GPIO_OUTPUT_PUSH_PULL;
gpio_init_struct.gpio_drive_strength = GPIO_DRIVE_STRENGTH_MAXIMUM;
gpio_init(ios->mosi.port, &gpio_init_struct);
bus->ios = ios;
// bus->delay_us = spi_delay;
__io_write(clk, bus->mode & SOFT_SPI_CPOL); // 空闲电平
}
static uint8_t soft_spi_transfer(struct soft_spi_bus *bus, uint8_t data)
{
#define __write_bit() \
if (bus->mode & SOFT_SPI_MSB) \
{ \
__io_write(mosi, (data & 0x80)); \
data <<= 1; \
} \
else \
{ \
__io_write(mosi, (data & 0x01)); \
data >>= 1; \
}
#define __read_bit() \
if (__io_read(miso)) \
{ \
data |= (bus->mode & SOFT_SPI_MSB) ? 0x01 : 0x80; \
}
for (int i = 0; i < 8; i++)
{
/**
* @brief
* MODE CPOL CPHA CLK_IDL MISO MOSI
* 0 0 0 0 RISING FALLING
* 1 0 1 0
* 2 1 0 1
* 3 1 1 1
*/
if (bus->mode & SOFT_SPI_CPHA)
{
// NOTE CPHA=1, CPOL=X. first edge write, second edge read
__io_write(clk, !(bus->mode & SOFT_SPI_CPOL));
__write_bit();
__NOP();
// bus->delay_us(bus->clktk);
__io_write(clk, (bus->mode & SOFT_SPI_CPOL));
// bus->delay_us(bus->clktk);
__NOP();
__read_bit();
}
else
{
// NOTE CPHA=0, CPOL=X. first edge read, second edge write
__write_bit();
__io_write(clk, !(bus->mode & SOFT_SPI_CPOL));
__NOP();
// bus->delay_us(bus->clktk);
__read_bit();
__io_write(clk, (bus->mode & SOFT_SPI_CPOL));
__NOP();
// bus->delay_us(bus->clktk);
}
}
// LOG_D("input data = %04x\n",gpio_input_data_read(bus->ios->miso.port));
LOG_D("output data = %04x\n", data);
return data;
}
static rt_uint32_t soft_spi_xfer(struct rt_spi_device *device, struct rt_spi_message *message)
{
struct rt_spi_bus *at32_spi_bus = (struct rt_spi_bus *)device->bus;
struct soft_spi_bus *bus = (struct soft_spi_bus *)at32_spi_bus->parent.user_data;
struct io_poSOFT *cs = device->parent.user_data;
rt_size_t message_length = 0, already_send_length = 0;
rt_uint16_t send_length = 0;
rt_uint8_t *recv_buf;
const rt_uint8_t *send_buf;
RT_ASSERT(device != NULL);
RT_ASSERT(message != NULL);
// rt_kprintf("device = %08x\n",device);
/* take cs */
if (message->cs_take)
{
// NOTE 这个板子CS需要取反,否则是CS是低电平
// gpio_bits_reset(cs->port, cs->pin);
gpio_bits_set(cs->port, cs->pin);
// LOG_D("spi take cs\n");
}
message_length = message->length;
recv_buf = message->recv_buf;
send_buf = message->send_buf;
while (message_length)
{
/* the HAL library use uint16 to save the data length */
if (message_length > 65535)
{
send_length = 65535;
message_length = message_length - 65535;
}
else
{
send_length = message_length;
message_length = 0;
}
/* calculate the start address */
already_send_length = message->length - send_length - message_length;
send_buf = (rt_uint8_t *)message->send_buf + already_send_length;
recv_buf = (rt_uint8_t *)message->recv_buf + already_send_length;
/* start once data exchange in dma mode */
if (message->send_buf && message->recv_buf)
{
for (uint8_t i = 0; i < send_length; i++)
{
recv_buf[i] = soft_spi_transfer(bus, send_buf[i]);
}
}
else if (message->send_buf)
{
for (uint8_t i = 0; i < send_length; i++)
{
LOG_D("****SEND****\n");
LOG_D("soft spi send data:%02x\n", send_buf[i]);
soft_spi_transfer(bus, send_buf[i]);
}
}
else
{
memset((void *)recv_buf, 0xff, send_length);
for (uint8_t i = 0; i < send_length; i++)
{
LOG_D("****READ****\n");
recv_buf[i] = soft_spi_transfer(bus, 0x00);
LOG_D("soft spi read data:%02x\n", recv_buf[i]);
}
}
}
/* release cs */
if (message->cs_release)
{
gpio_bits_reset(cs->port, cs->pin);
}
return message->length;
}
static struct rt_spi_ops at32_soft_spi_ops =
{
soft_spi_configure,
soft_spi_xfer};
int at_sf_spi_init(void)
{
rt_err_t result;
struct rt_spi_bus *rt_soft_spi_bus = RT_NULL;
rt_soft_spi_bus = (struct rt_spi_bus *)rt_malloc(sizeof(struct rt_spi_bus));
static soft_spi_bus_t soft_spi_bus = {
.mode = SOFT_SPI_MODE_3 | SOFT_SPI_MSB};
static soft_spi_ios_t soft_spi_bus_io =
{
.mosi = {GPIOB, GPIO_PINS_10},
.miso = {GPIOA, GPIO_PINS_2},
.clk = {GPIOA, GPIO_PINS_3},
};
soft_spi_bus_init(&soft_spi_bus, &soft_spi_bus_io);
rt_soft_spi_bus->parent.user_data = &soft_spi_bus;
result = rt_spi_bus_register(rt_soft_spi_bus, "sspi", &at32_soft_spi_ops);
LOG_D("sspi init result:%d\n", result);
return result;
}
// INIT_BOARD_EXPORT(rt_soft_spi_init);
INIT_BOARD_EXPORT(at_sf_spi_init);