基于rt_thread的软件模拟spi

spi的通信方式和协议我之前已经写过了,这里不再多写了。最近在研究rtthread,里面有硬件spi,最近因为项目需要用io口来模拟spi,但是网上能找到的资料不多,所以自己研究了一下,搞出来两套写法,一套是基于rtthread封装好的spi-bit-ops这个文件中的函数来注册一个软件spi。另一种就是纯自己手搓出来的。两套我都测试过了,都可以用,但是频率差了好多。下面简单贴一下基于rtthread已经给出的RT_USING_SPI_BITOPS这个宏定义来写。

struct at32_soft_spi_config
{
    rt_uint8_t clk;
    rt_uint8_t miso;
    rt_uint8_t mosi;
    const char *bus_name;
};
/* at32 sspi dirver class */
struct at32_sspi
{
    
    struct rt_spi_bus spi_bus;
    struct rt_spi_configuration config;
    struct rt_spi_bit_ops ops;
};

#ifdef BSP_USING_SSPI
#define SSPI_BUS_CONFIG            \
    {                              \
        .clk = BSP_SSPI_CLK_PIN,   \
        .miso = BSP_SSPI_MISO_PIN, \
        .mosi = BSP_SSPI_MOSI_PIN, \
        .bus_name = "sspi",        \
    }
#endif

static const struct at32_soft_spi_config soft_spi_config[] =
    {
#ifdef BSP_USING_SSPI
        SSPI_BUS_CONFIG,
#endif
};

static struct at32_sspi sspi_obj[sizeof(soft_spi_config) / sizeof(soft_spi_config[0])];

static void at32_sspi_gpio_init(struct at32_sspi *sspi)
{
    struct at32_soft_spi_config *cfg = (struct at32_soft_spi_config *)sspi->ops.data;
    
    rt_pin_mode(cfg->clk, PIN_MODE_OUTPUT);
    rt_pin_mode(cfg->mosi, PIN_MODE_OUTPUT);
    rt_pin_mode(cfg->miso, PIN_MODE_INPUT);

    // rt_pin_write(cfg->clk, PIN_LOW);
    // rt_pin_write(cfg->mosi, PIN_LOW);
    // rt_pin_write(cfg->miso, PIN_LOW);
}

static void sspi_tog_sclk(void *data)
{
    struct at32_soft_spi_config *cfg = (struct at32_soft_spi_config *)data;

    if (rt_pin_read(cfg->clk) == SET)
    {
        rt_pin_write(cfg->clk, PIN_LOW);
    }
    else
    {
        rt_pin_write(cfg->clk, PIN_HIGH);
    }
}
static void sspi_set_sclk(void *data, rt_int32_t state)
{
    struct at32_soft_spi_config *cfg = (struct at32_soft_spi_config *)data;
    rt_pin_write(cfg->clk, state);
    // if (state)
    // {
    //     rt_pin_write(cfg->clk, PIN_HIGH);
    // }
    // else
    // {
    //     rt_pin_write(cfg->clk, PIN_LOW);
    // }
}

static void sspi_set_mosi(void *data, rt_int32_t state)
{
    struct at32_soft_spi_config *cfg = (struct at32_soft_spi_config *)data;
    rt_pin_write(cfg->mosi, state);
    // if (state)
    // {
    //     rt_pin_write(cfg->mosi, PIN_HIGH);
    // }
    // else
    // {
    //     rt_pin_write(cfg->mosi, PIN_LOW);
    // }
}

static void sspi_set_miso(void *data, rt_int32_t state)
{
    struct at32_soft_spi_config *cfg = (struct at32_soft_spi_config *)data;
    rt_pin_write(cfg->miso, state);
    // if (state)
    // {
    //     rt_pin_write(cfg->miso, PIN_HIGH);
    // }
    // else
    // {
    //     rt_pin_write(cfg->miso, PIN_LOW);
    // }
}

static rt_int32_t sspi_get_sclk(void *data)
{
    struct at32_soft_spi_config *cfg = (struct at32_soft_spi_config *)data;
    return rt_pin_read(cfg->miso);
}

static rt_int32_t sspi_get_mosi(void *data)
{
    struct at32_soft_spi_config *cfg = (struct at32_soft_spi_config *)data;
    return rt_pin_read(cfg->mosi);
}

static rt_int32_t sspi_get_miso(void *data)
{
    struct at32_soft_spi_config *cfg = (struct at32_soft_spi_config *)data;
    return rt_pin_read(cfg->miso);
}

static void sspi_dir_mosi(void *data, rt_int32_t state)
{
    struct at32_soft_spi_config *cfg = (struct at32_soft_spi_config *)data;
    if (state)
    {
        rt_pin_mode(cfg->mosi, PIN_MODE_INPUT_PULLUP);
    }
    else
    {
        rt_pin_mode(cfg->mosi, PIN_MODE_OUTPUT_OD);
    }
}

static void sspi_dir_miso(void *data, rt_int32_t state)
{
    struct at32_soft_spi_config *cfg = (struct at32_soft_spi_config *)data;
    if (state)
    {
        rt_pin_mode(cfg->miso, PIN_MODE_INPUT_PULLUP);
    }
    else
    {
        rt_pin_mode(cfg->miso, PIN_MODE_OUTPUT_OD);
    }
}

static void sspi_udelay(rt_uint32_t us)
{
    rt_uint32_t ticks;
    rt_uint32_t told, tnow, tcnt = 0;
    rt_uint32_t reload = SysTick->LOAD;

    ticks = us * reload / (1000000 / RT_TICK_PER_SECOND);
    told = SysTick->VAL;
    while (1)
    {
        tnow = SysTick->VAL;
        if (tnow != told)
        {
            if (tnow < told)
            {
                tcnt += told - tnow;
            }
            else
            {
                tcnt += reload - tnow + told;
            }
            told = tnow;
            if (tcnt >= ticks)
            {
                break;
            }
        }
    }
}

static struct rt_spi_bit_ops at32_sspi_bit_ops_default =
    {
        .data = RT_NULL,
        .tog_sclk = sspi_tog_sclk,
        .set_sclk = sspi_set_sclk,
        .set_mosi = sspi_set_mosi,
        .set_miso = sspi_set_miso,
        .get_sclk = sspi_get_sclk,
        .get_mosi = sspi_get_mosi,
        .get_miso = sspi_get_miso,
        .dir_mosi = sspi_dir_mosi,
        .dir_miso = sspi_dir_miso,
        .udelay = sspi_udelay,
};

rt_err_t rt_sf_spi_device_attach(const char *bus_name, const char *device_name, rt_base_t pinnum)
{
    gpio_init_type gpio_init_struct;

    RT_ASSERT(bus_name != RT_NULL);
    RT_ASSERT(device_name != RT_NULL);

    rt_err_t result;
    struct rt_spi_device *spi_device;
    rt_base_t cs_pin;


    rt_pin_mode(pinnum, PIN_MODE_OUTPUT);
    //片选线要拉高,我这里电平时反的所以拉低了
    rt_pin_write(pinnum, PIN_LOW);

    /* attach the device to spi bus */
    spi_device = (struct rt_spi_device *)rt_malloc(sizeof(struct rt_spi_device));
    RT_ASSERT(spi_device != RT_NULL);
    cs_pin = (rt_base_t *)rt_malloc(sizeof(rt_base_t));
    RT_ASSERT(cs_pin != RT_NULL);
    cs_pin = pinnum;
    result = rt_spi_bus_attach_device(spi_device, device_name, bus_name, (void *)cs_pin);
    ;
    if (result != RT_EOK)
    {
        LOG_D("%s attach to %s faild, %d\n", device_name, bus_name, result);
    }

    RT_ASSERT(result == RT_EOK);

    LOG_D("%s attach to %s done", device_name, bus_name);

    return result;
}

int rt_soft_spi_init(void)
{
    rt_size_t obj_num = sizeof(sspi_obj) / sizeof(struct at32_sspi);
    rt_err_t result;
    for (int i = 0; i < obj_num; i++)
    {
        sspi_obj[i].ops.data = (void *)&soft_spi_config[i];
        at32_sspi_bit_ops_default.data = (void *)&soft_spi_config[i];
        at32_sspi_gpio_init(&sspi_obj[i]);
        result = rt_spi_bit_add_bus(&(sspi_obj[i].spi_bus), soft_spi_config[i].bus_name, &at32_sspi_bit_ops_default);
        RT_ASSERT(result == RT_EOK);

        LOG_D("software simulation %s init done, pin clk: %d, pin miso %d,pin miso %d\n",
              soft_spi_config[i].bus_name,
              soft_spi_config[i].clk,
              soft_spi_config[i].miso,
              soft_spi_config[i].mosi);
    }
}

INIT_BOARD_EXPORT(rt_soft_spi_init);

上面注册完成后,使用函数

rt_err_t rt_sf_spi_device_attach(const char *bus_name, const char *device_name, rt_base_t pinnum)

就可以把对应的传感器挂载在软件SPI上了。还有就是rtthread官方的spi-bit-ops里面的xfer读写有点小bug,我这里把这个函数更改了一点。

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);
        rt_pin_write(cs_pin, PIN_HIGH);
        spi_delay(ops);

        /* spi phase */
    }
    //这里如果放在cs_take里面的话,在模式1和3时clk时钟线翻转会少一次从而导致第二个字节clk的时钟完全混乱,要注意
    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);
        rt_pin_write(cs_pin, PIN_LOW);
        LOG_I("spi release cs\n");
    }

    return message->length;
}

上面这个可以直接使用,但是频率只有400K,如果去掉延时可能还能高一些,但是最高也就400K了。
如果用自己纯手搓的代码最高速度能到2.5M,所以这个能看懂并且会用就行了。

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值