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);
相关推荐
友善的猴子6 小时前
Adobe Photoshop 2025 Mac中文 Ps图像编辑
macos·adobe·photoshop
伊织code7 小时前
macOS Chrome - 打开开发者工具,设置 Local storage
chrome·macos·设置·开发者工具·local storage
独隅8 小时前
在 macOS 上修改 最大文件描述符限制(Too many open files) 和 网络端口相关参数 需要调整系统级配置的详细步骤
网络·macos
追风林11 小时前
mac 解压 nsz 文件
macos
斑驳的岁月19 小时前
MacOs java环境配置+maven环境配置踩坑实录
java·macos·maven
18538162800余。1 天前
碰一碰发视频源码开发深度解析,定制化开发
macos·objective-c·cocoa
returnShitBoy1 天前
iOS 上的内存管理是如何处理的?
macos·ios·cocoa
友善的猴子2 天前
AlDente Pro for Mac电脑 充电限制保护工具
macos·电脑
世界尽头与你2 天前
MacOS红队常用攻击命令
安全·macos·网络安全
干净的坏蛋2 天前
mac 终端 code 命令打开 vscode,修改 cursor占用
ide·vscode·macos