rtthread 软件SPI驱动, 支持mode0~3,MSB,LSB

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);
相关推荐
AD钙奶-lalala1 小时前
在 macOS 上搭建 Flutter 开发环境
flutter·macos
~二向箔~6 小时前
Mac,苹果电脑移动硬盘不显示
macos
白玉cfc6 小时前
Objective-C常用命名规范总结
开发语言·ios·objective-c
songgeb7 小时前
viewWillAppear与viewWillDisappear不匹配问题
ios·objective-c·swift
vastgrassland8 小时前
对WWDC 2025 Keynote 内容的预测
macos·ios·wwdc
fukai77228 小时前
WWDC 2025 macOS 26有哪些更新点
macos·ios·wwdc
ReadThroughLife13 小时前
【已解决】MACOS M4 芯片使用 Docker Desktop 工具安装 MICROSOFT SQL SERVER
microsoft·macos·docker·容器
獨枭1 天前
配置 macOS 上的 Ruby 开发环境
开发语言·macos·ruby
库奇噜啦呼1 天前
push [特殊字符] present
macos·ios·cocoa
安和昂1 天前
【iOS】多线程NSOperation,NSOperationQueue
macos·ios·cocoa