RTT笔记-SPI

2018-12-11  本文已影响0人  lissettecarlr

该笔记类别主要是在自己学习时做的一些记录,方便自己很久不用忘掉时进行快速回忆

1 简述

和串口不同,SPI属于总线类型,所以操作方式也不同,串口是通过rt_device_find打开设备,然后进行读写,而SPI是将某个设备挂载到这个总线上,然后针对这个设备再进行读写。

2 函数说明

名称 说明
rt_spi_bus_attach_device 将函数挂载到指定SPI总线上
rt_spi_configure 对spi模式等进行配置,如高地位,主从,时钟极性频率等

3 函数使用

rt_spi_bus_attach_device挂载设备

//成功返回RT_EOK
rt_err_t rt_spi_bus_attach_device(struct rt_spi_device *device,
                                  const char           *name,
                                  const char           *bus_name,
                                  void                 *user_data)

参数说明

参数名 说明
device 挂载的设备,由用户自定义如static struct rt_spi_device spi_dev_led
name 设备名称,字符串,自定义,主要在串口打印时方便标识
bus name SPI总线名称,可在msh shell输入list_device 命令查看,这是在SPI驱动里面被定义的
user_data 一般为SPI设备的CS引脚指针,进行数据传输时SPI控制器会操作此引脚进行片选
rt_spi_transfer_message 核心数据传输,通过配置message完成各种传输方式
rt_spi_send 衍生函数,发送一条数据,忽略接收
rt_spi_send_then_send 此函数可以连续发送2个缓冲区的数据,忽略接收到的数据
rt_spi_send_then_recv 发送一条数据,忽略接收,发送一条空数据,接收数据

使用示例

#define SPI_BUS_NAME                "spi1"  /* SPI总线名称 */
#define SPI_SSD1351_DEVICE_NAME     "spi10" /* SPI设备名称 */

... ...

static struct rt_spi_device spi_dev_ssd1351; /* SPI设备ssd1351对象 */
static struct stm32_hw_spi_cs  spi_cs;  /* SPI设备CS片选引脚 */

... ...

static int rt_hw_ssd1351_config(void)
{
    rt_err_t res;

    /* oled use PC8 as CS */
    spi_cs.pin = CS_PIN;
    rt_pin_mode(spi_cs.pin, PIN_MODE_OUTPUT);    /* 设置片选管脚模式为输出 */

    res = rt_spi_bus_attach_device(&spi_dev_ssd1351, SPI_SSD1351_DEVICE_NAME, SPI_BUS_NAME, (void*)&spi_cs);
    if (res != RT_EOK)
    {
        OLED_TRACE("rt_spi_bus_attach_device!\r\n");
        return res;
    }

    ... ...
}

rt_spi_configure模式参数配置

rt_err_t rt_spi_configure(struct rt_spi_device *device,
                          struct rt_spi_configuration *cfg)

参数说明

参数 说明
device 设备,也就是在挂载的时候填充了他,之后针对该设备操作都是使用它来表示
cfg 配置参数,老套路,自定义rt_spi_configuration 结构体,填充后传入
struct rt_spi_configuration
{
    rt_uint8_t mode;        //spi模式
    rt_uint8_t data_width;  //数据宽度,可取8位、16位、32位
    rt_uint16_t reserved;   //保留
    rt_uint32_t max_hz;     //最大频率
};

其中mode可以由多个模式或操作来并用,各种模式宏定义如下

/* 设置数据传输顺序是MSB位在前还是LSB位在前 */
#define RT_SPI_LSB      (0<<2)                        /* bit[2]: 0-LSB */
#define RT_SPI_MSB      (1<<2)                        /* bit[2]: 1-MSB */

/* 设置SPI的主从模式 */
#define RT_SPI_MASTER   (0<<3)                        /* SPI master device */
#define RT_SPI_SLAVE    (1<<3)                        /* SPI slave device */

/* 设置时钟极性和时钟相位 */
#define RT_SPI_MODE_0   (0 | 0)                       /* CPOL = 0, CPHA = 0 */
#define RT_SPI_MODE_1   (0 | RT_SPI_CPHA)             /* CPOL = 0, CPHA = 1 */
#define RT_SPI_MODE_2   (RT_SPI_CPOL | 0)             /* CPOL = 1, CPHA = 0 */
#define RT_SPI_MODE_3   (RT_SPI_CPOL | RT_SPI_CPHA)   /* CPOL = 1, CPHA = 1 */

#define RT_SPI_CS_HIGH  (1<<4)                        /* Chipselect active high */
#define RT_SPI_NO_CS    (1<<5)                        /* No chipselect */
#define RT_SPI_3WIRE    (1<<6)                        /* SI/SO pin shared */
#define RT_SPI_READY    (1<<7)                        /* Slave pulls low to pause */

配置示例

        struct rt_spi_configuration cfg;
        cfg.data_width = 8;
        cfg.mode = RT_SPI_MASTER | RT_SPI_MODE_0 | RT_SPI_MSB;
        cfg.max_hz = 20 * 1000 *1000; /* 20M,SPI max 42MHz,ssd1351 4-wire spi */

        rt_spi_configure(&spi_dev_ssd1351, &cfg);

rt_spi_transfer_message数据传输

struct rt_spi_message *rt_spi_transfer_message(struct rt_spi_device  *device,struct rt_spi_message *message)

message参数的结构体

struct rt_spi_message
{
    const void *send_buf;          /* 发送缓冲区指针 */
    void *recv_buf;                /* 接收缓冲区指针 */
    rt_size_t length;              /* 发送/接收 数据字节数 */
    struct rt_spi_message *next;   /* 指向继续发送的下一条消息的指针 */

    unsigned cs_take    : 1;       /* 值为1,CS引脚拉低,值为0,不改变引脚状态 */
    unsigned cs_release : 1;       /* 值为1,CS引脚拉高,值为0,不改变引脚状态 */
};

由于SPI一发必定一收,如果无需接收则可以将对于buffer置NULL。这里的message其实表示的是一次通信,类似一个数据包,当有多个message时,可以通过next参数将其连接起来,然后自动发送,无需使用时也可以置NULL。

rt_spi_send发送一条数据

由rt_spi_transfer_message衍生出来的,也就是固定了message的配置,目的就是单纯的发送一条数据

rt_size_t rt_spi_send(struct rt_spi_device *device,
                      const void           *send_buf,
                      rt_size_t             length)

参数很明显,无需多言,其中message被固定为了下列配置

    struct rt_spi_message msg;

    msg.send_buf   = send_buf;
    msg.recv_buf   = RT_NULL;
    msg.length     = length;
    msg.cs_take    = 1;
    msg.cs_release = 1;
    msg.next       = RT_NULL;

示例

len = rt_spi_send(&spi_dev_ssd1351, &cmd, 1);
if (len != 1)
{
     return -RT_ERROR;
}

rt_spi_send_then_send连续发送两条

rt_err_t rt_spi_send_then_send(struct rt_spi_device *device,
                               const void           *send_buf1,
                               rt_size_t             send_length1,
                               const void           *send_buf2,
                               rt_size_t             send_length2);

发送第一条前打开片选,发完第二条再关闭片选,message配置被固定为

    struct rt_spi_message msg1,msg2;

    msg1.send_buf   = send_buf1;
    msg1.recv_buf   = RT_NULL;
    msg1.length     = send_length1;
    msg1.cs_take    = 1;
    msg1.cs_release = 0;
    msg1.next       = &msg2;

    msg2.send_buf   = send_buf2;
    msg2.recv_buf   = RT_NULL;
    msg2.length     = send_length2;
    msg2.cs_take    = 0;
    msg2.cs_release = 1;
    msg2.next       = RT_NULL;

rt_spi_send_then_recv发而不收,发空再收

该函数通常应用在读取设备寄存器值得时候,首先发送寄存器地址,这个时候返回的是无效数据,然后需要再次发送一个数据来换回真实寄存器数据

rt_err_t rt_spi_send_then_recv(struct rt_spi_device *device,
                               const void           *send_buf,
                               rt_size_t             send_length,
                               void                 *recv_buf,
                               rt_size_t             recv_length);

message配置被固定为

    struct rt_spi_message msg1,msg2;

    msg1.send_buf   = send_buf;
    msg1.recv_buf   = RT_NULL;
    msg1.length     = send_length;
    msg1.cs_take    = 1;
    msg1.cs_release = 0;
    msg1.next       = &msg2;

    msg2.send_buf   = RT_NULL;
    msg2.recv_buf   = recv_buf;
    msg2.length     = recv_length;
    msg2.cs_take    = 0;
    msg2.cs_release = 1;
    msg2.next       = RT_NULL;

rt_spi_sendrecv8()和rt_spi_sendrecv16()函数是对此函数的封装,rt_spi_sendrecv8()发送一个字节数据同时收到一个字节数据,rt_spi_sendrecv16()发送2个字节数据同时收到2个字节数据

实例

开发板我使用的是STM32F103ZET6 原子的战舰版,其中spi2连接着w25Q128,片选是PB12。由于我仅仅测试spi功能,所以就读取该设备chip ID。

//SPI测试,使用SPI2,片选PB12
struct stm32_hw_spi_cs
{
    rt_uint32_t pin;
};
static struct rt_spi_device spi_dev_w25qxx;
static struct stm32_hw_spi_cs  spi_cs;
uint8_t rcv[6]={0,0,0,0,0,0};
uint8_t sed[6]={0X90,0,0,0,0,0};

void spi_thread_entry(void *paramter)
{
     rt_err_t res;
   rt_kprintf("spi_thread_entry\n");
     spi_cs.pin = 73;
     rt_pin_mode(spi_cs.pin, PIN_MODE_OUTPUT);
     
      res = rt_spi_bus_attach_device(&spi_dev_w25qxx, "w25qxx", "spi2", (void*)&spi_cs);
    if (res != RT_EOK)
    {
        rt_kprintf("rt_spi_bus_attach_device!\r\n");
    }
        else
        {
           rt_kprintf("spi--ok");
        }
        //配置
        struct rt_spi_configuration cfg;
    cfg.data_width = 8;
    cfg.mode = RT_SPI_MASTER | RT_SPI_MODE_0 | RT_SPI_MSB;
    cfg.max_hz = 20 * 1000 *1000; /* 20M,SPI max 42MHz,ssd1351 4-wire spi */
    rt_spi_configure(&spi_dev_w25qxx, &cfg);
        
    //读取ID
    struct rt_spi_message msg1;
    msg1.send_buf   = sed;
    msg1.recv_buf   = rcv;
    msg1.length     = 6;
    msg1.cs_take    = 1;
    msg1.cs_release = 1;
    msg1.next       = RT_NULL;
        rt_spi_transfer_message(&spi_dev_w25qxx,&msg1);
    rt_kprintf("%02X,%02X\n",rcv[4],rcv[5]);
        
}

启动该进程

rt_thread_t w25qxx_hander;
w25qxx_hander = rt_thread_create("w25qxx_thd"
                                    ,spi_thread_entry
                                    ,RT_NULL
                                    ,1024
                                    ,2
                                    ,10
                                    );

if (w25qxx_hander != RT_NULL)
{
    rt_thread_startup(w25qxx_hander);
}

最后串口输出EF,17,则表示通信成功,否则一般是返回两个0XFF的

上一篇 下一篇

猜你喜欢

热点阅读