美文网首页
RTT笔记-SPI

RTT笔记-SPI

作者: lissettecarlr | 来源:发表于2018-12-11 15:06 被阅读0次

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

    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的

    相关文章

      网友评论

          本文标题:RTT笔记-SPI

          本文链接:https://www.haomeiwen.com/subject/voxehqtx.html