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);
相关推荐
Swift社区1 天前
如何在 SwiftUI 中对 CoreImage 滤镜做实时预览
ios·swiftui·cocoa
李玮豪Jimmy1 天前
MacOS 上安装 JDK 并实现多版本灵活切换
macos
mixboot1 天前
VoxCPM 1.5.0 macOS
macos·voxcpm
甜可儿1 天前
mac环境安装不同版本maven
java·macos·maven
bl4ckpe4ch1 天前
从零开始Mac OS 开荒整理笔记
笔记·macos·开荒
七月巫山晴1 天前
【iOS】OC中的一些宏
前端·ios·objective-c
张飞签名上架2 天前
深耕全球市场:App上架iOS与Google Play全流程指南
macos·ios·cocoa·ios上架·上架·谷歌上架
DisonTangor2 天前
Mac Studio配备1.5 TB显存——基于雷电5的远程直接内存访问技术
人工智能·macos·开源·aigc
天庭鸡腿哥2 天前
输入序列号,可激活正版软件!
microsoft·macos·visual studio·everything
FreeBuf_3 天前
朝鲜黑客组织“传染性面试“瞄准macOS:新型“DriverFixer“窃密工具浮出水面
macos·面试·职场和发展