rtthread中的spi驱动外设设备的挂载知识点

rtthread中的spi驱动外设设备的挂载知识点:


1:再rtthread的官方4.0.0版本中,

在scons工具中添加spi的设备注册,可以看到
spi的驱动文件drv_spi,使用spi需要把rtthread中的drivede中的spi_core,spi_dev文件添加进去 
在spi_core中可以看到以下函数

rt_err_t rt_spi_bus_register(struct rt_spi_bus       *bus,
                             const char              *name,
                             const struct rt_spi_ops *ops)
{
    rt_err_t result;

    result = rt_spi_bus_device_init(bus, name);  
    if (result != RT_EOK)
        return result;

    /* initialize mutex lock */
    rt_mutex_init(&(bus->lock), name, RT_IPC_FLAG_FIFO);
    /* set ops */
    bus->ops = ops;
    /* initialize owner */
    bus->owner = RT_NULL;  //一开hi初始化为null
    /* set bus mode */
    bus->mode = RT_SPI_BUS_MODE_SPI;

    return RT_EOK;
}


rt_spi_bus_register注册函数中可以看到传参的时候有几个参数

struct rt_spi_bus
{
    struct rt_device parent;
    rt_uint8_t mode;
    const struct rt_spi_ops *ops;

    struct rt_mutex lock;
    struct rt_spi_device *owner;
};


结合参数和rt_spi_bus可以看出注册总线其实就是在初始化总线的结构体
通过把ops实现好的结构体付给bus总线,其中在rt_spi_bus_register函数可以看到,bus->owner为RT_NULL(这个变量很重要)


2:rt_spi_ops结构体

rt_spi_ops这个结构体的参数主要就是实现两个函数指针
一个是对spi设备的配置->configure,一个是对发送接收的实现接口->xfer,函数的实现在drv_spi.c文件接下来我们可以看到将spi设备绑定到spi总线上的函数rt_spi_bus_attach_device

rt_err_t rt_spi_bus_attach_device(struct rt_spi_device *device,
                                  const char           *name,
                                  const char           *bus_name,
                                  void                 *user_data)
{
    rt_err_t result;
    rt_device_t bus;

    /* get physical spi bus */
    bus = rt_device_find(bus_name);
    if (bus != RT_NULL && bus->type == RT_Device_Class_SPIBUS)
    {
        device->bus = (struct rt_spi_bus *)bus;

        /* initialize spidev device */
        result = rt_spidev_device_init(device, name);
        if (result != RT_EOK)
            return result;

        rt_memset(&device->config, 0, sizeof(device->config));
        device->parent.user_data = user_data;

        return RT_EOK;
    }

    /* not found the host bus */
    return -RT_ERROR;
}


这个函数的重点就是在于rt_spidev_device_init绑定设备的spi名称到总线上

3:接下来讲一下spi外设设备的初始化配置

#define SPI_BUS_NAME                "spi0"
#define SPI_dac104s085_DEVICE_NAME     "spi01"

#define CS_PIN   29  
/*
STM32F303VET6 SPI0 default GPIOs
        PA4------SPI0NSS
    PA5------SPI0SCK
    PA6------SPI0MISO
    PA7------SPI0MOSI
*/

struct stm32_hw_spi_cs
{
    rt_uint32_t pin;
};

static struct rt_spi_device spi_dev_dac104s085; 
static struct stm32_hw_spi_cs  spi_cs;    


 int rt_hw_dac104s085_config(void)
{
    rt_err_t res;
 
    spi_cs.pin = CS_PIN;
    rt_pin_mode(spi_cs.pin, GPIO_MODE_OUT_PP);   
    rt_pin_write(CS_PIN, PIN_HIGH);
    res = rt_spi_bus_attach_device(&spi_dev_dac104s085, SPI_dac104s085_DEVICE_NAME, SPI_BUS_NAME, (void*)&spi_cs);
    if (res != RT_EOK)
    {
        OLED_TRACE("rt_spi_bus_attach_device!\r\n");
        return res;
    }
    spi_dev_dac104s085.bus->owner = &spi_dev_dac104s085;   
    {
        struct rt_spi_configuration cfg;
        cfg.data_width = 16;
        cfg.mode =  RT_SPI_MODE_1 | RT_SPI_MSB;
        cfg.max_hz = 40 * 1000 *1000; /* 40M,SPI max 42MHz, 3-wire spi */
        if(spi_dev_dac104s085.bus->owner==RT_NULL)
        {
            rt_kprintf("RT_OK\r\n");
        }
        rt_kprintf("owner= %d\r\n",*spi_dev_dac104s085.bus->owner);
        rt_spi_configure(&spi_dev_dac104s085, &(cfg));
    
    }
    return RT_EOK;
   
}

INIT_DEVICE_EXPORT(rt_hw_dac104s085_config);   

 
从上面的函数可以看到我们是在配置spi设备的结构体

其中你可以看到中间多了一句spi_dev_dac104s085.bus->owner = &spi_dev_dac104s085;   
这个的主要作用就是将bus->owner变量赋值为自身
因为上面讲到总线注册的时候这里是初始化为null的


4:原因假设

假如这里不进行赋值,你可以看到spi的配置不成功,原因就在于spi_core.c中有一个配置的函数rt_spi_configure

rt_err_t rt_spi_configure(struct rt_spi_device        *device,
                          struct rt_spi_configuration *cfg)
{
    rt_err_t result;

    RT_ASSERT(device != RT_NULL);

    /* set configuration */
    device->config.data_width = cfg->data_width;
    device->config.mode       = cfg->mode & RT_SPI_MODE_MASK ;
    device->config.max_hz     = cfg->max_hz ;

    if (device->bus != RT_NULL)
    {
        result = rt_mutex_take(&(device->bus->lock), RT_WAITING_FOREVER);
        if (result == RT_EOK)
        {
                    
            if (device->bus->owner == device)  //判断不等就不初始化
            {
                device->bus->ops->configure(device, &device->config);
            }

            /* release lock */
            rt_mutex_release(&(device->bus->lock));
        }
    }

    return RT_EOK;
}

里面有对device->bus->owner进行判断,假如为null,这里就执行不到device->bus->ops->configure(device, &device->config);
这个ops中的配置函数了,

然后你在外部进行调用rt_spi_send函数对spi进行发送的时候就会发现只有这个发送函数在任意的地方调用了才配置spi设备成功

rt_err_t dac104s085_write_data(const rt_uint16_t data,const int lendth)
{
    rt_size_t len;
    len = rt_spi_send(&spi_dev_dac104s085, &data, lendth);
  
    if (len != lendth)
    {
        OLED_TRACE("dac104s085_write_data error. %d\r\n",len); 
        return -RT_ERROR;
    }
    else       
    {
        return RT_EOK;
    }
}

原因就在rt_spi_send里面的实现机制了

跳进rt_spi_send可以看到里面调用的是rt_spi_transfer

rt_size_t rt_spi_transfer(struct rt_spi_device *device,
                          const void           *send_buf,
                          void                 *recv_buf,
                          rt_size_t             length)
{
    rt_err_t result;
    struct rt_spi_message message;

    RT_ASSERT(device != RT_NULL);
    RT_ASSERT(device->bus != RT_NULL);

    result = RT_EOK;//rt_mutex_take(&(device->bus->lock), RT_WAITING_FOREVER);
    if (result == RT_EOK)
    {
        if (device->bus->owner != device)
        {
            /* not the same owner as current, re-configure SPI bus */  //与之前的绑定设备不同就重新初始化一遍设备
            result = device->bus->ops->configure(device, &device->config);
            if (result == RT_EOK)
            {
                /* set SPI bus owner */
                device->bus->owner = device;
            }
            else
            {
                /* configure SPI bus failed */
                rt_set_errno(-RT_EIO);
                result = 0;
                goto __exit;
            }
        }

        /* initial message */
        message.send_buf   = send_buf;
        message.recv_buf   = recv_buf;
        message.length     = length;
        message.cs_take    = 1;
        message.cs_release = 1;
        message.next       = RT_NULL;

        /* transfer message */
        result = device->bus->ops->xfer(device, &message);
        if (result == 0)
        {
            rt_set_errno(-RT_EIO);
            goto __exit;
        }
    }
    else
    {
        rt_set_errno(-RT_EIO);

        return 0;
    }

__exit:
   // rt_mutex_release(&(device->bus->lock));

    return result;
}

rt_spi_transfer里面有一段代码就是
device->bus->owner != device
判断device->bus->owner不等于之前注册的spi设备,就会重新去配置一下,所以在一开始如果不对device->bus->owner配置
就会出现只有在调用了发送函数才可以正常配置spi设备

问题点思考:

然后我这里比较郁闷,既然rtthread的rt_spi_transfer,rt_spi_configure里面要去判断device->bus->owner是否是注册了的设备,
为什么不再rt_spi_bus_attach_device这个函数中就帮用户进行初始化device->bus->owner呢?

另外在spi的发送函数rt_spi_transfer中可以看到里面是调用了rt_mutex_take,和rt_mutex_release导致不能再中断中使用
spi的发送函数,不知道网上各位大神有没有什么号的方式去改善这个spi的发送函数在中断中调用的机制呢?

我这里是直接把这两个给注销掉了,然后就可以在中断中调用了

最后备注:drv_spi.c,drv_spi.h使用的是rtthread中的gd32里面的文件,因为我的芯片就是gd32的

另外在rtthread的控制台中输入list_device出现
msh />list_device
device           type         ref count
-------- -------------------- ----------
spi01    SPI Device           0       
i2c0     I2C Bus              0       
timer2   Timer Device         1       
timer1   Timer Device         1       
pin      Miscellaneous Device 0       
spi1     SPI Bus              0       
spi0     SPI Bus              0       
uart1    Character Device     0       
uart0    Character Device     3    

出现bus总线数目为0,设备为0 ,不知道是不是正常的,但是程序确实是正常工作的
另外spi的设备如果数目想不是0 的话要做的工作就对spi进行查找rt_device_find()并打开rt_device_open() spi的设备,就可以正常看到spi的设备不为0了

  • 7
    点赞
  • 36
    收藏
    觉得还不错? 一键收藏
  • 7
    评论
您好!对于在RT-Thread使用SPI,您可以按照以下步骤进行操作: 1. 硬件连接:首先,确保您的SPI设备已正确连接到单片机。根据具体的硬件平台,可能需要连接SPI主控制器的引脚(如SCK、MISO、MOSI)以及片选引脚。 2. 配置硬件:在RT-Thread,您需要配置SPI主控制器以使其正常工作。这可以通过在RT-Thread的配置文件(rtconfig.h)进行设置。找到与所使用的硬件平台相关的配置项,启用SPI驱动程序。 3. 初始化SPI:在应用程序,您需要初始化SPI设备以准备进行数据传输。这可以通过使用RT-Thread提供的API函数来完成。通常,您需要指定SPI设备号、工作模式(如CPOL和CPHA)、数据位宽等参数进行初始化。 4. 数据传输:一旦SPI设备初始化完成,您可以使用相应的API函数来进行数据传输。这可能涉及到发送数据、接收数据或同时进行发送和接收。具体的API函数取决于RT-Thread使用的SPI驱动程序。 5. 关闭SPI:当您完成SPI通信后,应及时关闭SPI设备以释放资源。这可以通过调用相应的API函数来实现。 请注意,以上步骤的具体实现可能因所使用的硬件平台和RT-Thread版本而有所不同。确保参考相关的文档和示例代码进行正确配置和使用SPI功能。 如果您有特定的硬件平台和RT-Thread版本,请提供相关的详细信息,我可以为您提供更具体的帮助。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值