美文网首页
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

    该笔记类别主要是在自己学习时做的一些记录,方便自己很久不用忘掉时进行快速回忆 1 简述 和串口不同,SPI属于总线...

  • 工欲善其事,必先利其器:通过Telnet访问SEGGER RTT

    Segger提供了RTT Viewer、RTT Client和RTT Logger3个工具用于RTT的访问,下面列...

  • RTT笔记-shell

    RTT笔记-shell 模式切换 finsh同时支持c-c-style模式和msh模式,默认进入msh模式,在该模...

  • RTT笔记-线程

    继续按照程序流程前进,前面提到的时钟初始化,堆初始化都还未跳出板级初始化rt_hw_board_init函数 然后...

  • RTT笔记-package

    该笔记类别主要是在自己学习时做的一些记录,方便自己很久不用忘掉时进行快速回忆 1 简述 被封装用于特定功能的代码,...

  • RTT笔记-进程

    修改!该文变更为快速查阅编程文档,RTT进程实现分析将另起一文 动态创建线程 静态创建线程 事件 消息队列 定义 ...

  • RTT笔记-ulog

    该笔记类别主要是在自己学习时做的一些记录,方便自己很久不用忘掉时进行快速回忆 1 导入组件 代码位置:rt-thr...

  • RTT笔记-AT组件

    1 导入系统 AT 组件中 AT Client 功能占用资源体积为 4.6K ROM 和 2.0K RAM; AT...

  • RTT笔记-fal

    该笔记类别主要是在自己学习时做的一些记录,方便自己很久不用忘掉时进行快速回忆 1 使用 1.1 导入文件 文件名位...

  • 2021-05-18_JavaEE容器SPI查找机制学习笔记3

    20210518_J2EE容器SPI查找机制学习笔记3 1概述 SPI全称Service Provider Int...

网友评论

      本文标题:RTT笔记-SPI

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