RT_threadのSPI设备学习笔记

19 篇文章 4 订阅
10 篇文章 10 订阅


前言

    SPI作为基本的通信协议,广泛应用在嵌入式产品中,它是一种高速、全双工、同步通信总线,目前接触的较多的是SPI、QSPI,Dual SPI暂时没接触到,这里不对SPI总线基础知识做记录,本文仅仅是Rt_thread系统下的SPI设备学习笔记,学习RT_thread中对SPI设备的封装,从其设备驱动层、设备框架驱动层以及应用层入手学习,最终实现使用stm32的SPI3驱动LCD显示屏,用QSPI驱动Flash芯片。


1 SPI设备的相关结构体

    这里有必要先了解下RT_thread中SPI设备的相关结构体定义,因为我们熟知RT_thread对IO设备采用的编程思路是面向对象,一切都是为了定义一个设备“类”,并实例化成一个设备对象,我们在应用层只需调用“类”成员函数/变量即可访问想要访问的设备。

1.1 SPI设备驱动层结构体

    在RT_thread系统下的SPI驱动层定义了几个结构体,分别是:
(1)片选引脚

struct stm32_hw_spi_cs/*片选脚*/
{
    GPIO_TypeDef* GPIOx;
    uint16_t GPIO_Pin;
};

(2)片上SPI和自定义SPI总线名称

struct stm32_spi_config
{
    SPI_TypeDef *Instance;
    char *bus_name;
};

(3)SPI设备的引脚号、总线名称以及设备名称。一般规范的写法是,总线名称是spix,挂载在总线上的设备名称为spixy

struct stm32_spi_device
{
    rt_uint32_t pin;
    char *bus_name;//总线名称
    char *device_name;//设备名称
};

(4)SPI驱动类,里面包含了常见的SPI_HandleTypeDef类型的变量,接着嵌套了三个结构体(类),后两者是属于设备框架驱动层的

/* stm32 spi dirver class */
struct stm32_spi
{
    SPI_HandleTypeDef handle;
    const struct stm32_spi_config *config;
    /*下面两个结构体是设备框架驱动层的*/
    struct rt_spi_configuration *cfg;
    struct rt_spi_bus spi_bus;
};

1.2 SPI设备框架驱动层结构体

    在RT_thread系统下的SPI框架驱动层定义了几个结构体,分别是:
(1)SPI的配置结构体,包含了模式,通信数据位数宽度,和最大通信频率

struct rt_spi_configuration
{
    rt_uint8_t mode;
    rt_uint8_t data_width;
    rt_uint16_t reserved;
    rt_uint32_t max_hz;
};

(2)SPI总线结构体,继承struct rt_device,同时又有模式变量,关键的还嵌套了spi的操作函数结构体,额外加上一个互斥锁机制,最后嵌套了表述SPI总线的拥有者属于哪个设备的struct rt_spi_device结构体。

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;
};

(3)SPI相关操作函数结构体,仅仅两个函数,一个配置,另一个数据传输。

struct rt_spi_ops
{
    rt_err_t (*configure)(struct rt_spi_device *device, struct rt_spi_configuration *configuration);
    rt_uint32_t (*xfer)(struct rt_spi_device *device, struct rt_spi_message *message);
};

(4)SPI的消息结构体,用户可以自定义每个待传输的消息结构体各参数的数值,从而可以
很方便的控制数据传输方式。注意其中的length要小于等于发送或接收的缓存大小,不然执行程序会导致缓冲区溢出。

struct rt_spi_message
{
    const void *send_buf;/*发送缓冲区指针*/
    void *recv_buf;/*接收缓冲区指针*/
    rt_size_t length;/*发送/接收字节数*/
    struct rt_spi_message *next;/*指向下一个消息,对于发送多条消息比较有用,单向链表*/

    unsigned cs_take    : 1;/*片选选中*/
    unsigned cs_release : 1;/*片选信号释放*/
};

(5)最后一个,SPI设备结构体

struct rt_spi_device
{
    struct rt_device parent;
    struct rt_spi_bus *bus;

    struct rt_spi_configuration config;
    void   *user_data;
};

/关于QSPI,另起笔记,这里暂不做记录/

2 SPI设备驱动层

    万丈高楼平地起,要使用SPI设备之前,需要先将SPI总线底层驱动写好,有SPI的初始化、SPI的收发驱动。RT_thread有stm32的SPI驱动,可以直接拿过来用。下面解析下在STM32平台下RT_thread中的SPI驱动。

2.1 SPI的初始化

    用函数stm32_spi_init更新struct stm32_spi结构体中的SPI_HandleTypeDef型变量handle,并且完成SPI的初始化。函数stm32_spi_init 的原型为:

static rt_err_t stm32_spi_init(struct stm32_spi *spi_drv, struct rt_spi_configuration *cfg)
{
	/*判断传参是否有效*/
    RT_ASSERT(spi_drv != RT_NULL);
    RT_ASSERT(cfg != RT_NULL);
	/*获取spix的handle句柄*/
    SPI_HandleTypeDef *spi_handle = &spi_drv->handle;
	/*更新主、从模式*/
    if (cfg->mode & RT_SPI_SLAVE)
    {
        spi_handle->Init.Mode = SPI_MODE_SLAVE;
    }
    else
    {
        spi_handle->Init.Mode = SPI_MODE_MASTER;
    }
	/*配置SPI总线的数据线方向:单线、双线*/
    if (cfg->mode & RT_SPI_3WIRE)
    {
        spi_handle->Init.Direction = SPI_DIRECTION_1LINE;
    }
    else
    {
        spi_handle->Init.Direction = SPI_DIRECTION_2LINES;
    }
	/*SPI总线的数据宽度*/
    if (cfg->data_width == 8)
    {
        spi_handle->Init.DataSize = SPI_DATASIZE_8BIT;
        spi_handle->TxXferSize = 8;
        spi_handle->RxXferSize = 8;
    }
    else if (cfg->data_width == 16)
    {
        spi_handle->Init.DataSize = SPI_DATASIZE_16BIT;
    }
    else
    {
        return RT_EIO;
    }
	/*配置SPI总线的模式,有4种*/
    if (cfg->mode & RT_SPI_CPHA)
    {
        spi_handle->Init.CLKPhase = SPI_PHASE_2EDGE;
    }
    else
    {
        spi_handle->Init.CLKPhase = SPI_PHASE_1EDGE;
    }

    if (cfg->mode & RT_SPI_CPOL)
    {
        spi_handle->Init.CLKPolarity = SPI_POLARITY_HIGH;
    }
    else
    {
        spi_handle->Init.CLKPolarity = SPI_POLARITY_LOW;
    }
	/*SPI的片选,这里都选软片选*/
    if (cfg->mode & RT_SPI_NO_CS)
    {
        spi_handle->Init.NSS = SPI_NSS_SOFT;
    }
    else
    {
        spi_handle->Init.NSS = SPI_NSS_SOFT;
    }
	/*SPI的总线波特率设置*/
    uint32_t SPI_APB_CLOCK;

    SPI_APB_CLOCK = HAL_RCC_GetPCLK2Freq();

    if (cfg->max_hz >= SPI_APB_CLOCK / 2)
    {
        spi_handle->Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_2;
    }
    else if (cfg->max_hz >= SPI_APB_CLOCK / 4)
    {
        spi_handle->Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_4;
    }
    else if (cfg->max_hz >= SPI_APB_CLOCK / 8)
    {
        spi_handle->Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_8;
    }
    else if (cfg->max_hz >= SPI_APB_CLOCK / 16)
    {
        spi_handle->Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_16;
    }
    else if (cfg->max_hz >= SPI_APB_CLOCK / 32)
    {
        spi_handle->Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_32;
    }
    else if (cfg->max_hz >= SPI_APB_CLOCK / 64)
    {
        spi_handle->Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_64;
    }
    else if (cfg->max_hz >= SPI_APB_CLOCK / 128)
    {
        spi_handle->Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_128;
    }
    else
    {
        /*  min prescaler 256 */
        spi_handle->Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_256;
    }

    LOG_D("sys freq: %d, pclk2 freq: %d, SPI limiting freq: %d, BaudRatePrescaler: %d",
          HAL_RCC_GetSysClockFreq(),
          SPI_APB_CLOCK,
          cfg->max_hz,
          spi_handle->Init.BaudRatePrescaler);
	/*SPI传输数据是高位优先还是低位优先*/
    if (cfg->mode & RT_SPI_MSB)
    {
        spi_handle->Init.FirstBit = SPI_FIRSTBIT_MSB;
    }
    else
    {
        spi_handle->Init.FirstBit = SPI_FIRSTBIT_LSB;
    }
	
    spi_handle->Init.TIMode = SPI_TIMODE_DISABLE;
    spi_handle->Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE;
    spi_handle->State = HAL_SPI_STATE_RESET;
    /*调用hal库函数初始化SPI*/
    if (HAL_SPI_Init(spi_handle) != HAL_OK)
    {
        return RT_EIO;
    }
    /*调用Hal库函数使能SPI*/
    __HAL_SPI_ENABLE(spi_handle);

    LOG_D("%s init done", spi_drv->config->bus_name);
    return RT_EOK;
}

    上面的函数被函数spi_configure调用,体现为:

static rt_err_t spi_configure(struct rt_spi_device *device,
                              struct rt_spi_configuration *configuration)
{
	/*先判断传参有效性*/
    RT_ASSERT(device != RT_NULL);
    RT_ASSERT(configuration != RT_NULL);
	/*获取device->bus隶属的struct stm32_spi类型的结构体句柄*/
	/*然后更新结构体中的cfg*/
    struct stm32_spi *spi_drv =  rt_container_of(device->bus, struct stm32_spi, spi_bus);
    spi_drv->cfg = configuration;
	
    return stm32_spi_init(spi_drv, configuration);
}

2.2 SPI总线的收发

static rt_uint32_t spixfer(struct rt_spi_device *device, struct rt_spi_message *message)
{
	/*检查传参有效性*/
    RT_ASSERT(device != RT_NULL);
    RT_ASSERT(device->bus != RT_NULL);
    RT_ASSERT(device->bus->parent.user_data != RT_NULL);
    RT_ASSERT(message != RT_NULL);
	/*获取SPI设备对象的硬件SPI信息*/
    struct stm32_spi *spi_drv =  rt_container_of(device->bus, struct stm32_spi, spi_bus);
    SPI_HandleTypeDef * spi_handle = &spi_drv->handle;
    /*RT_thread中的SPI设备一般用成员user_data存放SPI的片选信息*/
    /*获取片选引脚信息*/
    struct stm32_hw_spi_cs *cs = device->parent.user_data;
    rt_int32_t length = message->length;//消息长度
    /*总线数据宽度*/
    rt_int32_t data_width = spi_drv->cfg->data_width;
	/*若选中片选*/
    if (message->cs_take)
    {
        HAL_GPIO_WritePin(cs->GPIOx, cs->GPIO_Pin, GPIO_PIN_RESET);//片选选中
    }
    /*总线数据宽度是8位*/
    if (data_width == 8)
    {
        const rt_uint8_t * send_ptr = message->send_buf;
        rt_uint8_t * recv_ptr = message->recv_buf;
        /*读取和写入SPI的DR寄存器,实现数据收发*/
        while (length--)
        {
            rt_uint8_t data = ~0;

            if(send_ptr != RT_NULL)
            {
                data = *send_ptr++;
            }
            
            /* send data once */
            while (__HAL_SPI_GET_FLAG(spi_handle, SPI_FLAG_TXE) == RESET);
            *(volatile rt_uint8_t *)(&spi_handle->Instance->DR) = data;

            /* receive data once */
            SET_BIT(spi_handle->Instance->CR2, SPI_RXFIFO_THRESHOLD_HF);
            while (__HAL_SPI_GET_FLAG(spi_handle, SPI_FLAG_RXNE) == RESET);
            data = *(volatile rt_uint8_t *)(&spi_handle->Instance->DR);
            
            if(recv_ptr != RT_NULL)
            {
                *recv_ptr++ = data;
            }
        }

    } else//1位的
    {
    	/**/
    }
	/*这里需要先判断忙标志是否松开,然后再释放片选,保证数据正确完整*/
    /* Wait until Busy flag is reset before disabling SPI */
    while (__HAL_SPI_GET_FLAG(spi_handle, SPI_FLAG_BSY) == SET);

    if (message->cs_release)
    {
        HAL_GPIO_WritePin(cs->GPIOx, cs->GPIO_Pin, GPIO_PIN_SET);
    }

    return message->length;
}

    现在SPI的初始化和收发函数已经有了,把它们俩封装进struct rt_rt_spi_ops结构体里面:

static const struct rt_spi_ops stm_spi_ops =
{
    .configure = spi_configure,
    .xfer = spixfer,
};

    应用层的程序可以间接调用stm_spi_ops结构体中的函数实现SPI的初始化和通信。为了实现这一功能,需要将stm_spi_ops中的成员赋值给rt_device设备对象的函数指针。下面的函数rt_hw_spi_bus_init() 的功能是给SPI总线对象赋值,并将它注册到系统中。

static int rt_hw_spi_bus_init(void)
{
    rt_err_t result;
    for (int i = 0; i < sizeof(spi_config) / sizeof(spi_config[0]); i++)
    {
        spi_bus_obj[i].config = &spi_config[i];
        spi_bus_obj[i].spi_bus.parent.user_data = &spi_config[i];
        spi_bus_obj[i].handle.Instance = spi_config[i].Instance;

        result = rt_spi_bus_register(&spi_bus_obj[i].spi_bus, spi_config[i].bus_name, &stm_spi_ops);
        RT_ASSERT(result == RT_EOK);

        LOG_D("%s bus init done", spi_config[i].bus_name);
    }

    return result;
}

    注册好SPI总线之后,就可以往总线上挂载设备了,使用函数rt_hw_spi_device_attach() 将SPI设备挂载到SPI总线上,并且将SPI设备注册到IO设备管理器中。

rt_err_t rt_hw_spi_device_attach(const char *bus_name, const char *device_name, GPIO_TypeDef* cs_gpiox, uint16_t cs_gpio_pin)
{
    RT_ASSERT(bus_name != RT_NULL);
    RT_ASSERT(device_name != RT_NULL);

    rt_err_t result;
    struct rt_spi_device *spi_device;
    struct stm32_hw_spi_cs *cs_pin;

    /* initialize the cs pin && select the slave*/
    GPIO_InitTypeDef GPIO_Initure;
    GPIO_Initure.Pin = cs_gpio_pin;
    GPIO_Initure.Mode = GPIO_MODE_OUTPUT_PP;
    GPIO_Initure.Pull = GPIO_PULLUP;
    GPIO_Initure.Speed = GPIO_SPEED_FREQ_HIGH;
    HAL_GPIO_Init(cs_gpiox, &GPIO_Initure);
    HAL_GPIO_WritePin(cs_gpiox, cs_gpio_pin, GPIO_PIN_SET);

    /* 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 = (struct stm32_hw_spi_cs *)rt_malloc(sizeof(struct stm32_hw_spi_cs));
    RT_ASSERT(cs_pin != RT_NULL);
    cs_pin->GPIOx = cs_gpiox;
    cs_pin->GPIO_Pin = cs_gpio_pin;
    result = rt_spi_bus_attach_device(spi_device, device_name, bus_name, (void *)cs_pin);

    if (result != RT_EOK)
    {
        LOG_E("%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;
}

2 SPI设备框架驱动层

2.1 SPI总线注册函数

    上面提到的函数rt_spi_bus_register() 原型如下:

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;
	/*spi总线注册到IO设备管理器中*/
    result = rt_spi_bus_device_init(bus, name);
    if (result != RT_EOK)
        return result;

    /* 注册成功之后初始化互斥锁 */
    rt_mutex_init(&(bus->lock), name, RT_IPC_FLAG_FIFO);
    /* set ops */
    bus->ops = ops;
    /* initialize owner */
    bus->owner = RT_NULL;
    /* set bus mode */
    bus->mode = RT_SPI_BUS_MODE_SPI;

    return RT_EOK;
}

    里面调用函数rt_spi_bus_device_init的原型是:

/*初始化总线设备*/
rt_err_t rt_spi_bus_device_init(struct rt_spi_bus *bus, const char *name)
{
    struct rt_device *device;
    RT_ASSERT(bus != RT_NULL);
	/*更新总线的父类*/
    device = &bus->parent;

    /* set device type */
    device->type    = RT_Device_Class_SPIBUS;
    /* initialize device interface */
#ifdef RT_USING_DEVICE_OPS
    device->ops     = &spi_bus_ops;
#else
    device->init    = RT_NULL;
    device->open    = RT_NULL;
    device->close   = RT_NULL;
    device->read    = _spi_bus_device_read;
    device->write   = _spi_bus_device_write;
    device->control = _spi_bus_device_control;
#endif

    /* 将SPI总线注册到设备管理器中 */
    return rt_device_register(device, name, RT_DEVICE_FLAG_RDWR);
}

2.2 总线挂载设备函数

    在前面的函数rt_hw_spi_device_attach() 中调用了函数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;

    /* 获取spi总线,返回SPI总线句柄 */
    bus = rt_device_find(bus_name);
    if (bus != RT_NULL && bus->type == RT_Device_Class_SPIBUS)
    {
        device->bus = (struct rt_spi_bus *)bus;

        /* 初始化SPI设备 */
        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_err_t rt_spidev_device_init(struct rt_spi_device *dev, const char *name)
{
    struct rt_device *device;
    RT_ASSERT(dev != RT_NULL);
	/*更新SPI设备的父类*/
    device = &(dev->parent);

    /* set device type */
    device->type    = RT_Device_Class_SPIDevice;
#ifdef RT_USING_DEVICE_OPS
    device->ops     = &spi_device_ops;
#else
    device->init    = RT_NULL;
    device->open    = RT_NULL;
    device->close   = RT_NULL;
    device->read    = _spidev_device_read;
    device->write   = _spidev_device_write;
    device->control = _spidev_device_control;
#endif

    /* 将SPI设备注册到IO设备管理器中 */
    return rt_device_register(device, name, RT_DEVICE_FLAG_RDWR);
}

   在SPI总线注册好了以后,我们就可以将设备(例如:SPI flash、SPI接口型的LCD、其他SPI接口芯片)虚拟成SPI设备spix0,spix1,spix2,…,将它们挂载到总线上,其中spix表示SPI总线的名称。

2.3 SPI总线和设备对象的成员函数

   注意,RT_thread中不但将总线看成IO设备对象,还对总线上的设备看成IO设备对象,所以整个框架说到底最终是配置两个结构体,一个是struct rt_spi_bus,另一个是struct rt_spi_device

2.3.1 SPI总线成员函数

   在前面的函数rt_spi_bus_device_init中对SPI总线控制块关联上了3个操作函数:
(1)_spi_bus_device_read
(2)_spi_bus_device_write
(3)_spi_bus_device_control
这3个函数比较简单,原型如下:

static rt_size_t _spi_bus_device_read(rt_device_t dev,
                                      rt_off_t    pos,
                                      void       *buffer,
                                      rt_size_t   size)
{
    struct rt_spi_bus *bus;

    bus = (struct rt_spi_bus *)dev;
    RT_ASSERT(bus != RT_NULL);
    RT_ASSERT(bus->owner != RT_NULL);

    return rt_spi_transfer(bus->owner, RT_NULL, buffer, size);
}

static rt_size_t _spi_bus_device_write(rt_device_t dev,
                                       rt_off_t    pos,
                                       const void *buffer,
                                       rt_size_t   size)
{
    struct rt_spi_bus *bus;

    bus = (struct rt_spi_bus *)dev;
    RT_ASSERT(bus != RT_NULL);
    RT_ASSERT(bus->owner != RT_NULL);

    return rt_spi_transfer(bus->owner, buffer, RT_NULL, size);
}

static rt_err_t _spi_bus_device_control(rt_device_t dev,
                                        int         cmd,
                                        void       *args)
{
    /* TODO: 自己按需求添加命令扩展 */
    switch (cmd)
    {
    case 0: /* set device */
        break;
    case 1: 
        break;
    }

    return RT_EOK;
}

   前两个函数都是调用函数rt_spi_transfer进行总线读写,我们看看函数rt_spi_transfer(归属文件名“spi_core.c”)的原型是什么:

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;
	/*首先判断SPI设备是否有效、总线是否有效*/
    RT_ASSERT(device != RT_NULL);
    RT_ASSERT(device->bus != RT_NULL);
	/*占用互斥锁*/
    result = rt_mutex_take(&(device->bus->lock), RT_WAITING_FOREVER);
    if (result == RT_EOK)
    {
   		/*如果总线上的SPI设备不是当前的SPI设备*/
        if (device->bus->owner != device)
        {
            /* not the same owner as current, re-configure SPI bus */
            result = device->bus->ops->configure(device, &device->config);
            //实际上调用spi_configure(struct rt_spi_device *device, struct rt_spi_configuration *configuration)
            if (result == RT_EOK)
            {
                /* 将总线归属于当前设备 */
                device->bus->owner = device;
            }
            else
            {
                /* configure SPI bus failed */
                rt_set_errno(-RT_EIO);
                result = 0;
                goto __exit;
            }
        }

        /* 初始化待传输的消息 */
        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;//表明这里只发送一次消息

        /* 开始传输消息 */
        result = device->bus->ops->xfer(device, &message);
        //实际上调用spixfer(struct rt_spi_device *device, struct rt_spi_message *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 用到了互斥信号量,所以不能在中断服务函数中调用函数rt_spi_transfer,会出现assertion错误

2.3.2 SPI设备成员函数

   SPI设备成员函数有3个,跟SPI总线的成员函数基本上一样,读写用的还是同样的函数rt_spi_transfer()

static rt_size_t _spidev_device_read(rt_device_t dev, rt_off_t pos, void *buffer, rt_size_t size)
{
    struct rt_spi_device *device;

    device = (struct rt_spi_device *)dev;
    RT_ASSERT(device != RT_NULL);
    RT_ASSERT(device->bus != RT_NULL);

    return rt_spi_transfer(device, RT_NULL, buffer, size);
}

static rt_size_t _spidev_device_write(rt_device_t dev, rt_off_t pos, const void *buffer, rt_size_t size)
{
    struct rt_spi_device *device;

    device = (struct rt_spi_device *)dev;
    RT_ASSERT(device != RT_NULL);
    RT_ASSERT(device->bus != RT_NULL);

    return rt_spi_transfer(device, buffer, RT_NULL, size);
}

static rt_err_t _spidev_device_control(rt_device_t dev, int cmd, void *args)
{
    switch (cmd)
    {
    case 0: /* set device */
        break;
    case 1: 
        break;
    }

    return RT_EOK;
}

3 SPI应用层

   一般情况下MCU的SPI器件都是作为主机和从机通讯,在RT_thread中将SPI主机虚拟为SPI总线设备,应用程序使用SPI设备管理接口来访问SPI从机器件,主要接口有:

函数描述
rt_device_find()根据SPI设备名称查找设备获取设备句柄
rt_spi_transfer_message()自定义传输数据
rt_spi_transfer()传输一次数据
rt_spi_send()发送一次数据
rt_spi_recv()接收一次数据
rt_spi_send_then_send()连续两次发送
rt_spi_send_then_recv()先发送后接收
rt_spi_configure()配置SPI

   上面的这几个函数最终调用的函数前面已经介绍,这里不增加篇幅记录。

   以上都是对学习RT_thread系统BSP下的SPI设备的记录笔记,如果有哪里说错或者哪里记录有问题的,还请大佬们不吝赐教,一起学习RT_thread

总结

   相比于裸机下或者其他RTOS系统,我们写SPI驱动实现对外设访问的代码不算多,总体没有Rtthread的复杂,RT_thread的复杂性主要表现在它对设备的程序包装。在使用RT_thread的SPI设备驱动外设时,主要注意几个点:
(1)调用函数rt_hw_spi_device_attach() 将SPI设备挂载到SPI总线上,并完成注册。
(2)使用函数rt_device_find() 查找SPI设备返回SPI设备的句柄。
(3)根据外设使用函数rt_spi_configure() 稍微修改SPI设备驱动层的配置,比如SPI的模式、速率可能需要修改。
(4)对SPI设备进行读写操作。

  • 5
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值