Browse Source

[components][drivers][spi]: 基于SPI总线驱动框架添加模拟SPI总线扩展 (#5656)

* add soft-spi

* add spi-bit-ops.c/h to components/drivers/spi
* add a drv_soft_spi example for gd32303e-eval

Signed-off-by: kyle <kylepengchn@163.com>

* Fixed the format and the certificate.

Signed-off-by: kyle <kylepengchn@163.com>

* Update the certificate data.

Signed-off-by: kyle <kylepengchn@163.com>
kylepengchn 3 years ago
parent
commit
5810f4de7d

+ 6 - 0
bsp/gd32303e-eval/Kconfig

@@ -64,6 +64,12 @@ config RT_USING_SPI2
     select RT_USING_SPI
     default n 
 
+config RT_USING_SPI3
+    bool "Using SPI2 BUS (software simulation)"
+    select RT_USING_SPI
+    select RT_USING_SPI_BITOPS
+    default n 
+
 config RT_USING_I2C0
     bool "Using I2C0"
     select RT_USING_I2C

+ 5 - 1
bsp/gd32303e-eval/drivers/SConscript

@@ -15,7 +15,11 @@ CPPPATH = [cwd]
 # add spi drivers.
 if GetDepend('RT_USING_SPI'):
     src += ['drv_spi.c']
-    
+
+# add softspi drivers.
+if GetDepend('RT_USING_SPI_BITOPS'):
+    src += ['drv_soft_spi.c']
+
 # add i2c drivers.
 if GetDepend('RT_USING_I2C'):
     src += ['drv_i2c.c']

+ 303 - 0
bsp/gd32303e-eval/drivers/drv_soft_spi.c

@@ -0,0 +1,303 @@
+/*
+ * Copyright (c) 2006-2021, RT-Thread Development Team
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Change Logs:
+ * Date           Author       Notes
+ * 2021-10-11     kyle         first implementation.
+ */
+
+#include "drv_soft_spi.h"
+#include <board.h>
+#include <rtthread.h>
+
+#if defined(RT_USING_SPI) && defined(RT_USING_SPI_BITOPS) && defined(RT_USING_PIN)
+#include <rtdevice.h>
+#include "spi-bit-ops.h"
+
+#define DBG_TAG               "drv.SPI"
+#ifdef RT_SPI_DEBUG
+#define DBG_LVL               DBG_LOG
+#else
+#define DBG_LVL               DBG_WARNING
+#endif
+#include <rtdbg.h>
+
+#ifndef ITEM_NUM
+#define ITEM_NUM(items) sizeof(items) / sizeof(items[0])
+#endif
+
+struct gd32_spi_bit_data
+{
+    struct
+    {
+        rcu_periph_enum clk;
+        rt_uint32_t port;
+        rt_uint32_t pin;
+    } sclk, mosi, miso;
+};
+
+
+rt_inline FlagStatus GPIO_OUTPUT_BIT_GET(uint32_t gpio_periph, uint32_t pin)
+{
+    if((uint32_t)RESET !=(GPIO_OCTL(gpio_periph)&(pin))){
+        return SET;
+    }else{
+        return RESET;
+    }
+}
+
+rt_inline void GPIO_BIT_RESET(uint32_t gpio_periph, uint32_t pin)
+{
+    GPIO_BC(gpio_periph) = (uint32_t)pin;
+}
+
+rt_inline void GPIO_BIT_SET(uint32_t gpio_periph, uint32_t pin)
+{
+    GPIO_BOP(gpio_periph) = (uint32_t)pin;
+}
+
+rt_inline FlagStatus GPIO_INPUT_BIT_GET(uint32_t gpio_periph,uint32_t pin)
+{
+    if((uint32_t)RESET != (GPIO_ISTAT(gpio_periph)&(pin))){
+        return SET;
+    }else{
+        return RESET;
+    }
+}
+
+rt_inline void GPIO_INIT(uint32_t gpio_periph, uint32_t mode, uint32_t speed, uint32_t pin)
+{
+    uint16_t i;
+    uint32_t temp_mode = 0U;
+    uint32_t reg = 0U;
+
+    /* GPIO mode configuration */
+    temp_mode = (uint32_t)(mode & ((uint32_t)0x0FU));
+
+    /* GPIO speed configuration */
+    if(((uint32_t)0x00U) != ((uint32_t)mode & ((uint32_t)0x10U))){
+            /* output mode max speed:10MHz,2MHz,50MHz */
+            temp_mode |= (uint32_t)speed;
+    }
+
+    /* configure the eight low port pins with GPIO_CTL0 */
+    for(i = 0U;i < 8U;i++){
+        if((1U << i) & pin){
+            reg = GPIO_CTL0(gpio_periph);
+
+            /* clear the specified pin mode bits */
+            reg &= ~GPIO_MODE_MASK(i);
+            /* set the specified pin mode bits */
+            reg |= GPIO_MODE_SET(i, temp_mode);
+
+            /* set IPD or IPU */
+            if(GPIO_MODE_IPD == mode){
+                /* reset the corresponding OCTL bit */
+                GPIO_BC(gpio_periph) = (uint32_t)((1U << i) & pin);
+            }else{
+                /* set the corresponding OCTL bit */
+                if(GPIO_MODE_IPU == mode){
+                    GPIO_BOP(gpio_periph) = (uint32_t)((1U << i) & pin);
+                }
+            }
+            /* set GPIO_CTL0 register */
+            GPIO_CTL0(gpio_periph) = reg;
+        }
+    }
+    /* configure the eight high port pins with GPIO_CTL1 */
+    for(i = 8U;i < 16U;i++){
+        if((1U << i) & pin){
+            reg = GPIO_CTL1(gpio_periph);
+
+            /* clear the specified pin mode bits */
+            reg &= ~GPIO_MODE_MASK(i - 8U);
+            /* set the specified pin mode bits */
+            reg |= GPIO_MODE_SET(i - 8U, temp_mode);
+
+            /* set IPD or IPU */
+            if(GPIO_MODE_IPD == mode){
+                /* reset the corresponding OCTL bit */
+                GPIO_BC(gpio_periph) = (uint32_t)((1U << i) & pin);
+            }else{
+                /* set the corresponding OCTL bit */
+                if(GPIO_MODE_IPU == mode){
+                    GPIO_BOP(gpio_periph) = (uint32_t)((1U << i) & pin);
+                }
+            }
+            /* set GPIO_CTL1 register */
+            GPIO_CTL1(gpio_periph) = reg;
+        }
+    }
+}
+
+#define GPIO_SET_OUTPUT(port, pin)        GPIO_INIT(port, GPIO_MODE_OUT_PP, GPIO_OSPEED_50MHZ, pin)
+#define GPIO_SET_INPUT(port, pin)         GPIO_INIT(port, GPIO_MODE_IN_FLOATING, GPIO_OSPEED_50MHZ, pin)
+
+
+static void gpio_tog_sclk(void *data)
+{
+    struct gd32_spi_bit_data *bd = data;
+
+    if (GPIO_OUTPUT_BIT_GET(bd->sclk.port, bd->sclk.pin) == SET)
+    {
+        GPIO_BIT_RESET(bd->sclk.port, bd->sclk.pin);
+    }
+    else
+    {
+        GPIO_BIT_SET(bd->sclk.port, bd->sclk.pin);
+    }
+}
+
+static void gpio_set_sclk(void *data, rt_int32_t state)
+{
+    struct gd32_spi_bit_data *bd = data;
+
+    if (state)
+    {
+        GPIO_BIT_SET(bd->sclk.port, bd->sclk.pin);
+    }
+    else
+    {
+        GPIO_BIT_RESET(bd->sclk.port, bd->sclk.pin);
+    }
+}
+
+static void gpio_set_mosi(void *data, rt_int32_t state)
+{
+    struct gd32_spi_bit_data *bd = data;
+
+    if (state)
+    {
+        GPIO_BIT_SET(bd->mosi.port, bd->mosi.pin);
+    }
+    else
+    {
+        GPIO_BIT_RESET(bd->mosi.port, bd->mosi.pin);
+    }
+}
+
+static void gpio_set_miso(void *data, rt_int32_t state)
+{
+    struct gd32_spi_bit_data *bd = data;
+
+    if (state)
+    {
+        GPIO_BIT_SET(bd->miso.port, bd->miso.pin);
+    }
+    else
+    {
+        GPIO_BIT_RESET(bd->miso.port, bd->miso.pin);
+    }
+}
+
+static rt_int32_t gpio_get_sclk(void *data)
+{
+    struct gd32_spi_bit_data *bd = data;
+
+    return GPIO_INPUT_BIT_GET(bd->sclk.port, bd->sclk.pin);
+}
+
+static rt_int32_t gpio_get_mosi(void *data)
+{
+    struct gd32_spi_bit_data *bd = data;
+
+    return GPIO_INPUT_BIT_GET(bd->mosi.port, bd->mosi.pin);
+}
+
+static rt_int32_t gpio_get_miso(void *data)
+{
+    struct gd32_spi_bit_data *bd = data;
+
+    return GPIO_INPUT_BIT_GET(bd->miso.port, bd->miso.pin);
+}
+
+static void gpio_dir_mosi(void *data, rt_int32_t state)
+{
+    struct gd32_spi_bit_data *bd = data;
+
+    if (state)
+    {
+        GPIO_SET_INPUT(bd->mosi.port, bd->mosi.pin);
+    }
+    else
+    {
+        GPIO_SET_OUTPUT(bd->mosi.port, bd->mosi.pin);
+    }
+}
+
+static void gpio_dir_miso(void *data, rt_int32_t state)
+{
+    struct gd32_spi_bit_data *bd = data;
+
+    if (state)
+    {
+        GPIO_SET_INPUT(bd->miso.port, bd->miso.pin);
+    }
+    else
+    {
+        GPIO_SET_OUTPUT(bd->miso.port, bd->miso.pin);
+    }
+}
+
+static void gpio_udelay(rt_uint32_t us)
+{
+    int i = ((rcu_clock_freq_get(CK_SYS) / 4000000) * us);
+
+    while (i)
+    {
+        i--;
+    }
+}
+
+static void soft_spi_gpio_init(const struct gd32_spi_bit_data *bd)
+{
+    rcu_periph_clock_enable(bd->sclk.clk);
+    rcu_periph_clock_enable(bd->mosi.clk);
+    rcu_periph_clock_enable(bd->miso.clk);
+
+    gpio_init(bd->sclk.port, GPIO_MODE_OUT_PP, GPIO_OSPEED_50MHZ, bd->sclk.pin);
+    gpio_init(bd->mosi.port, GPIO_MODE_OUT_PP, GPIO_OSPEED_50MHZ, bd->mosi.pin);
+    gpio_init(bd->miso.port, GPIO_MODE_IN_FLOATING, GPIO_OSPEED_50MHZ, bd->miso.pin);
+
+    GPIO_BIT_SET(bd->sclk.port, bd->sclk.pin);
+    GPIO_BIT_SET(bd->mosi.port, bd->mosi.pin);
+    GPIO_BIT_SET(bd->miso.port, bd->miso.pin);
+}
+
+int rt_soft_spi_init(void)
+{
+    int result = 0;
+
+    {
+        static const struct gd32_spi_bit_data spi1_bdata =
+        {
+            .sclk = { RCU_GPIOB, GPIOB, GPIO_PIN_13},
+            .mosi = { RCU_GPIOB, GPIOB, GPIO_PIN_15},
+            .miso = { RCU_GPIOB, GPIOB, GPIO_PIN_14},
+        };
+        static struct rt_spi_bit_ops spi1_bops =
+        {
+            .data     = (void *)&spi1_bdata,
+            .tog_sclk = gpio_tog_sclk,
+            .set_sclk = gpio_set_sclk,
+            .set_mosi = gpio_set_mosi,
+            .set_miso = gpio_set_miso,
+            .get_sclk = gpio_get_sclk,
+            .get_mosi = gpio_get_mosi,
+            .get_miso = gpio_get_miso,
+            .dir_mosi = gpio_dir_mosi,
+            .dir_miso = gpio_dir_miso,
+            .udelay   = gpio_udelay,
+        };
+        struct rt_spi_bit_obj spi1_obj;
+
+        soft_spi_gpio_init(&spi1_bdata);
+        rt_spi_bit_add_bus(&spi1_obj, "spi3", &spi1_bops);
+    }
+
+    return result;
+}
+INIT_BOARD_EXPORT(rt_soft_spi_init);
+#endif

+ 24 - 0
bsp/gd32303e-eval/drivers/drv_soft_spi.h

@@ -0,0 +1,24 @@
+/*
+ * Copyright (c) 2006-2021, RT-Thread Development Team
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Change Logs:
+ * Date           Author       Notes
+ * 2021-10-11     kyle         first implementation.
+ */
+
+#ifndef __DRV_SOFT_SPI_H__
+#define __DRV_SOFT_SPI_H__
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+int rt_soft_spi_init(void);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // __DRV_SPI_H__

+ 12 - 1
components/drivers/Kconfig

@@ -228,7 +228,18 @@ config RT_USING_SPI
     bool "Using SPI Bus/Device device drivers"
     default n
 
-    if RT_USING_SPI  
+    if RT_USING_SPI
+        config RT_USING_SPI_BITOPS
+            select RT_USING_PIN
+            bool "Use GPIO to simulate SPI"
+            default n
+
+        if RT_USING_SPI_BITOPS
+            config RT_SPI_BITOPS_DEBUG
+                bool "Use simulate SPI debug message"
+                default n
+        endif
+
         config RT_USING_QSPI
             bool "Enable QSPI mode"
             default n

+ 3 - 0
components/drivers/spi/SConscript

@@ -6,6 +6,9 @@ src = ['spi_core.c', 'spi_dev.c']
 CPPPATH = [cwd, cwd + '/../include']
 LOCAL_CFLAGS = ''
 
+if GetDepend('RT_USING_SPI_BITOPS'):
+    src += ['spi-bit-ops.c']
+
 if GetDepend('RT_USING_QSPI'):
     src += ['qspi_core.c']
 

+ 525 - 0
components/drivers/spi/spi-bit-ops.c

@@ -0,0 +1,525 @@
+/*
+ * Copyright (c) 2006-2022, RT-Thread Development Team
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Change Logs:
+ * Date           Author        Notes
+ * 2021-10-11     kyle          first version
+ */
+
+#include <spi-bit-ops.h>
+#include <rtdevice.h>
+
+#define DBG_TAG               "SPI"
+#ifdef RT_SPI_BITOPS_DEBUG
+#define DBG_LVL               DBG_LOG
+#else
+#define DBG_LVL               DBG_ERROR
+#endif
+#include <rtdbg.h>
+
+#define TOG_SCLK(ops)        ops->tog_sclk(ops->data)
+#define SET_SCLK(ops, val)   ops->set_sclk(ops->data, val)
+#define SET_MOSI(ops, val)   ops->set_mosi(ops->data, val)
+#define SET_MISO(ops, val)   ops->set_miso(ops->data, val)
+#define GET_SCLK(ops)        ops->get_sclk(ops->data)
+#define GET_MOSI(ops)        ops->get_mosi(ops->data)
+#define GET_MISO(ops)        ops->get_miso(ops->data)
+#define DIR_MOSI(ops, val)   ops->dir_mosi(ops->data, val)
+#define DIR_MISO(ops, val)   ops->dir_miso(ops->data, val)
+
+rt_inline void spi_delay(struct rt_spi_bit_ops *ops)
+{
+    ops->udelay((ops->delay_us + 1) >> 1);
+}
+
+rt_inline void spi_delay2(struct rt_spi_bit_ops *ops)
+{
+    ops->udelay(ops->delay_us);
+}
+
+#define SCLK_H(ops)          SET_SCLK(ops, 1)
+#define SCLK_L(ops)          SET_SCLK(ops, 0)
+#define MOSI_H(ops)          SET_MOSI(ops, 1)
+#define MOSI_L(ops)          SET_MOSI(ops, 0)
+#define MOSI_IN(ops)         DIR_MOSI(ops, 1)
+#define MOSI_OUT(ops)        DIR_MOSI(ops, 0)
+#define MISO_IN(ops)         DIR_MISO(ops, 1)
+#define MISO_OUT(ops)        DIR_MISO(ops, 0)
+
+rt_inline rt_size_t spi_xfer_4line_data8(struct rt_spi_bit_ops       *ops,
+                                         struct rt_spi_configuration *config,
+                                         const void                  *send_buf,
+                                         void                        *recv_buf,
+                                         rt_size_t                    length)
+{
+    int i = 0;
+
+    RT_ASSERT(ops != RT_NULL);
+    RT_ASSERT(length != 0);
+
+    {
+        const rt_uint8_t *send_ptr = send_buf;
+        rt_uint8_t *recv_ptr = recv_buf;
+        rt_uint32_t size = length;
+
+        while (size--)
+        {
+            rt_uint8_t tx_data = 0xFF;
+            rt_uint8_t rx_data = 0xFF;
+            rt_uint8_t bit  = 0;
+
+            if (send_buf != RT_NULL)
+            {
+                tx_data = *send_ptr++;
+            }
+
+            for (i = 0; i < 8; i++)
+            {
+                if (config->mode & RT_SPI_MSB) { bit = tx_data & (0x1 << (7 - i)); }
+                else                           { bit = tx_data & (0x1 << i); }
+
+                if (bit) MOSI_H(ops);
+                else     MOSI_L(ops);
+
+                spi_delay2(ops);
+
+                TOG_SCLK(ops);
+
+                if (config->mode & RT_SPI_MSB) { rx_data <<= 1; bit = 0x01; }
+                else                           { rx_data >>= 1; bit = 0x80; }
+
+                if (GET_MISO(ops)) { rx_data |=  bit; }
+                else               { rx_data &= ~bit; }
+
+                spi_delay2(ops);
+
+                if (!(config->mode & RT_SPI_CPHA) || (size != 0) || (i < 7))
+                {
+                    TOG_SCLK(ops);
+                }
+            }
+
+            if (recv_buf != RT_NULL)
+            {
+                *recv_ptr++ = rx_data;
+            }
+        }
+    }
+
+    return length;
+}
+
+rt_inline rt_size_t spi_xfer_4line_data16(struct rt_spi_bit_ops       *ops,
+                                          struct rt_spi_configuration *config,
+                                          const void                  *send_buf,
+                                          void                        *recv_buf,
+                                          rt_size_t                    length)
+{
+    int i = 0;
+
+    RT_ASSERT(ops != RT_NULL);
+    RT_ASSERT(length != 0);
+
+    {
+        const rt_uint16_t *send_ptr = send_buf;
+        rt_uint16_t *recv_ptr = recv_buf;
+        rt_uint32_t size = length;
+
+        while (size--)
+        {
+            rt_uint16_t tx_data = 0xFFFF;
+            rt_uint16_t rx_data = 0xFFFF;
+            rt_uint16_t bit  = 0;
+
+            if (send_buf != RT_NULL)
+            {
+                tx_data = *send_ptr++;
+            }
+
+            for (i = 0; i < 16; i++)
+            {
+                if (config->mode & RT_SPI_MSB) { bit = tx_data & (0x1 << (15 - i)); }
+                else                           { bit = tx_data & (0x1 << i); }
+
+                if (bit) MOSI_H(ops);
+                else     MOSI_L(ops);
+
+                spi_delay2(ops);
+
+                TOG_SCLK(ops);
+
+                if (config->mode & RT_SPI_MSB) { rx_data <<= 1; bit = 0x0001; }
+                else                           { rx_data >>= 1; bit = 0x8000; }
+
+                if (GET_MISO(ops)) { rx_data |=  bit; }
+                else               { rx_data &= ~bit; }
+
+                spi_delay2(ops);
+
+                if (!(config->mode & RT_SPI_CPHA) || (size != 0) || (i < 15))
+                {
+                    TOG_SCLK(ops);
+                }
+            }
+
+            if (recv_buf != RT_NULL)
+            {
+                *recv_ptr++ = rx_data;
+            }
+        }
+    }
+
+    return length;
+}
+
+rt_inline rt_size_t spi_xfer_3line_data8(struct rt_spi_bit_ops       *ops,
+                                         struct rt_spi_configuration *config,
+                                         const void                  *send_buf,
+                                         void                        *recv_buf,
+                                         rt_size_t                    length)
+{
+    int i = 0;
+
+    RT_ASSERT(ops != RT_NULL);
+    RT_ASSERT(length != 0);
+
+    {
+        const rt_uint8_t *send_ptr = send_buf;
+        rt_uint8_t *recv_ptr = recv_buf;
+        rt_uint32_t size = length;
+        rt_uint8_t send_flg = 0;
+
+        if ((send_buf != RT_NULL) || (recv_buf == RT_NULL))
+        {
+            MOSI_OUT(ops);
+            send_flg = 1;
+        }
+        else
+        {
+            MOSI_IN(ops);
+        }
+
+        while (size--)
+        {
+            rt_uint8_t tx_data = 0xFF;
+            rt_uint8_t rx_data = 0xFF;
+            rt_uint8_t bit  = 0;
+
+            if (send_buf != RT_NULL)
+            {
+                tx_data = *send_ptr++;
+            }
+
+            if (send_flg)
+            {
+                for (i = 0; i < 8; i++)
+                {
+                    if (config->mode & RT_SPI_MSB) { bit = tx_data & (0x1 << (7 - i)); }
+                    else                           { bit = tx_data & (0x1 << i); }
+
+                    if (bit) MOSI_H(ops);
+                    else     MOSI_L(ops);
+
+                    spi_delay2(ops);
+
+                    TOG_SCLK(ops);
+
+                    spi_delay2(ops);
+
+                    if (!(config->mode & RT_SPI_CPHA) || (size != 0) || (i < 7))
+                    {
+                        TOG_SCLK(ops);
+                    }
+                }
+
+                rx_data = tx_data;
+            }
+            else
+            {
+                for (i = 0; i < 8; i++)
+                {
+                    spi_delay2(ops);
+
+                    TOG_SCLK(ops);
+
+                    if (config->mode & RT_SPI_MSB) { rx_data <<= 1; bit = 0x01; }
+                    else                           { rx_data >>= 1; bit = 0x80; }
+
+                    if (GET_MOSI(ops)) { rx_data |=  bit; }
+                    else               { rx_data &= ~bit; }
+
+                    spi_delay2(ops);
+
+                    if (!(config->mode & RT_SPI_CPHA) || (size != 0) || (i < 7))
+                    {
+                        TOG_SCLK(ops);
+                    }
+                }
+
+            }
+
+            if (recv_buf != RT_NULL)
+            {
+                *recv_ptr++ = rx_data;
+            }
+        }
+
+        if (!send_flg)
+        {
+            MOSI_OUT(ops);
+        }
+    }
+
+    return length;
+}
+
+rt_inline rt_size_t spi_xfer_3line_data16(struct rt_spi_bit_ops       *ops,
+                                          struct rt_spi_configuration *config,
+                                          const void                  *send_buf,
+                                          void                        *recv_buf,
+                                          rt_size_t                    length)
+{
+    int i = 0;
+
+    RT_ASSERT(ops != RT_NULL);
+    RT_ASSERT(length != 0);
+
+    {
+        const rt_uint16_t *send_ptr = send_buf;
+        rt_uint16_t *recv_ptr = recv_buf;
+        rt_uint32_t size = length;
+        rt_uint8_t send_flg = 0;
+
+        if ((send_buf != RT_NULL) || (recv_buf == RT_NULL))
+        {
+            MOSI_OUT(ops);
+            send_flg = 1;
+        }
+        else
+        {
+            MOSI_IN(ops);
+        }
+
+        while (size--)
+        {
+            rt_uint16_t tx_data = 0xFFFF;
+            rt_uint16_t rx_data = 0xFFFF;
+            rt_uint16_t bit  = 0;
+
+            if (send_buf != RT_NULL)
+            {
+                tx_data = *send_ptr++;
+            }
+
+            if (send_flg)
+            {
+                for (i = 0; i < 16; i++)
+                {
+                    if (config->mode & RT_SPI_MSB) { bit = tx_data & (0x1 << (15 - i)); }
+                    else                           { bit = tx_data & (0x1 << i); }
+
+                    if (bit) MOSI_H(ops);
+                    else     MOSI_L(ops);
+
+                    spi_delay2(ops);
+
+                    TOG_SCLK(ops);
+
+                    spi_delay2(ops);
+
+                    if (!(config->mode & RT_SPI_CPHA) || (size != 0) || (i < 15))
+                    {
+                        TOG_SCLK(ops);
+                    }
+                }
+
+                rx_data = tx_data;
+            }
+            else
+            {
+                for (i = 0; i < 16; i++)
+                {
+                    spi_delay2(ops);
+
+                    TOG_SCLK(ops);
+
+                    if (config->mode & RT_SPI_MSB) { rx_data <<= 1; bit = 0x0001; }
+                    else                           { rx_data >>= 1; bit = 0x8000; }
+
+                    if (GET_MOSI(ops)) { rx_data |=  bit; }
+                    else               { rx_data &= ~bit; }
+
+                    spi_delay2(ops);
+
+                    if (!(config->mode & RT_SPI_CPHA) || (size != 0) || (i < 15))
+                    {
+                        TOG_SCLK(ops);
+                    }
+                }
+
+            }
+
+            if (recv_buf != RT_NULL)
+            {
+                *recv_ptr++ = rx_data;
+            }
+        }
+
+        if (!send_flg)
+        {
+            MOSI_OUT(ops);
+        }
+    }
+
+    return length;
+}
+
+rt_err_t spi_bit_configure(struct rt_spi_device *device, struct rt_spi_configuration *configuration)
+{
+    struct rt_spi_bit_obj *obj = rt_container_of(device->bus, struct rt_spi_bit_obj, bus);
+    struct rt_spi_bit_ops *ops = obj->ops;
+
+    RT_ASSERT(device != RT_NULL);
+    RT_ASSERT(configuration != RT_NULL);
+
+    if (configuration->mode & RT_SPI_SLAVE)
+    {
+        return -RT_EIO;
+    }
+
+    if (configuration->mode & RT_SPI_CPOL)
+    {
+        SCLK_H(ops);
+    }
+    else
+    {
+        SCLK_L(ops);
+    }
+
+    if (configuration->max_hz < 200000)
+    {
+        ops->delay_us = 1;
+    }
+    else
+    {
+        ops->delay_us = 0;
+    }
+
+    rt_memcpy(&obj->config, configuration, sizeof(struct rt_spi_configuration));
+
+    return RT_EOK;
+}
+
+rt_uint32_t spi_bit_xfer(struct rt_spi_device *device, struct rt_spi_message *message)
+{
+    struct rt_spi_bit_obj *obj = rt_container_of(device->bus, struct rt_spi_bit_obj, bus);
+    struct rt_spi_bit_ops *ops = obj->ops;
+    struct rt_spi_configuration *config = &obj->config;
+    rt_base_t cs_pin = (rt_base_t)device->parent.user_data;
+
+    RT_ASSERT(device != NULL);
+    RT_ASSERT(message != NULL);
+
+#ifdef RT_SPI_BITOPS_DEBUG
+    if (!ops->tog_sclk || !ops->set_sclk || !ops->get_sclk)
+    {
+        LOG_E("SPI bus error, SCLK line not defined");
+    }
+    if (!ops->set_mosi || !ops->get_mosi)
+    {
+        LOG_E("SPI bus error, MOSI line not defined");
+    }
+    if (!ops->set_miso || !ops->get_miso)
+    {
+        LOG_E("SPI bus error, MISO line not defined");
+    }
+#endif
+
+    /* take CS */
+    if (message->cs_take)
+    {
+        LOG_I("spi take cs\n");
+        rt_pin_write(cs_pin, PIN_LOW);
+        spi_delay(ops);
+
+        /* spi phase */
+        if (config->mode & RT_SPI_CPHA)
+        {
+            spi_delay(ops);
+            TOG_SCLK(ops);
+        }
+    }
+
+    if (config->mode & RT_SPI_3WIRE)
+    {
+        if (config->data_width <= 8)
+        {
+            spi_xfer_3line_data8(ops,
+                                 config,
+                                 message->send_buf,
+                                 message->recv_buf,
+                                 message->length);
+        }
+        else if (config->data_width <= 16)
+        {
+            spi_xfer_3line_data16(ops,
+                                  config,
+                                  message->send_buf,
+                                  message->recv_buf,
+                                  message->length);
+        }
+    }
+    else
+    {
+        if (config->data_width <= 8)
+        {
+            spi_xfer_4line_data8(ops,
+                                 config,
+                                 message->send_buf,
+                                 message->recv_buf,
+                                 message->length);
+        }
+        else if (config->data_width <= 16)
+        {
+            spi_xfer_4line_data16(ops,
+                                  config,
+                                  message->send_buf,
+                                  message->recv_buf,
+                                  message->length);
+        }
+    }
+
+    /* release CS */
+    if (message->cs_release)
+    {
+        spi_delay(ops);
+        rt_pin_write(cs_pin, PIN_HIGH);
+        LOG_I("spi release cs\n");
+    }
+
+    return message->length;
+}
+
+static const struct rt_spi_ops spi_bit_bus_ops =
+{
+    .configure = spi_bit_configure,
+    .xfer      = spi_bit_xfer,
+};
+
+rt_err_t rt_spi_bit_add_bus(struct rt_spi_bit_obj *obj,
+                            const char            *bus_name,
+                            struct rt_spi_bit_ops *ops)
+{
+    obj->ops = ops;
+    obj->config.data_width = 8;
+    obj->config.max_hz     = 1 * 1000 * 1000;
+    obj->config.mode       = RT_SPI_MASTER | RT_SPI_MSB | RT_SPI_MODE_0;
+
+    /* idle status */
+    if (obj->config.mode & RT_SPI_CPOL) SCLK_H(ops);
+    else                                SCLK_L(ops);
+
+    return rt_spi_bus_register(&obj->bus, bus_name, &spi_bit_bus_ops);
+}

+ 53 - 0
components/drivers/spi/spi-bit-ops.h

@@ -0,0 +1,53 @@
+/*
+ * Copyright (c) 2006-2018, RT-Thread Development Team
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Change Logs:
+ * Date           Author        Notes
+ * 2021-10-11     kyle         first version
+ */
+
+#ifndef __SPI_BIT_OPS_H__
+#define __SPI_BIT_OPS_H__
+
+#include <rtdevice.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct rt_spi_bit_ops
+{
+    void *const data;            /* private data for lowlevel routines */
+    void (*const tog_sclk)(void *data);
+    void (*const set_sclk)(void *data, rt_int32_t state);
+    void (*const set_mosi)(void *data, rt_int32_t state);
+    void (*const set_miso)(void *data, rt_int32_t state);
+    rt_int32_t (*const get_sclk)(void *data);
+    rt_int32_t (*const get_mosi)(void *data);
+    rt_int32_t (*const get_miso)(void *data);
+
+    void (*const dir_mosi)(void *data, rt_int32_t state);
+    void (*const dir_miso)(void *data, rt_int32_t state);
+
+    void (*const udelay)(rt_uint32_t us);
+    rt_uint32_t delay_us;  /* sclk、mosi and miso line delay */
+};
+
+struct rt_spi_bit_obj
+{
+    struct rt_spi_bus bus;
+    struct rt_spi_bit_ops *ops;
+    struct rt_spi_configuration config;
+};
+
+rt_err_t rt_spi_bit_add_bus(struct rt_spi_bit_obj *obj, 
+                            const char            *bus_name,
+                            struct rt_spi_bit_ops *ops);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif