Skip to content

SPI MASTER FLASH

SPI简介

SPI(Serial Peripheral Interface,串行外设接口)是一种高速、全双工、同步通信总线,常用于短距离通讯,主要应用于 EEPROM、FLASH、实时时钟、AD 转换器、还有数字信号处理器和数字信号解码器之间。

关于SPI总线介绍可详细查看RT-Thread官网的介绍:https://www.rt-thread.org/document/site/#/rt-thread-version/rt-thread-standard/programming-manual/device/spi/spi

驱动支持

访问SPI总线设备前,需要保证相关驱动配置已选中。

Windows环境:利用RT-Thread官方env工具直接在代码根目录下执行menuconfig进入配置。

Linux环境:在代码根目录下执行scons --menuconfig进入配置。

SPI MASTER驱动所在路径如下:

RT-Thread Components -> Device Drivers -> Using SPI Bus/Device device drivers

打开驱动配置

上述配置选中后,相关的GPIO配置也需要配置,打开源码文件:board/board.h需要打开相关注释,如下所示:

#define BSP_USING_SOFTWARE_SPIM
#define BSP_SPIM_MOSI_PIN    GET_PIN(A, 3)//"A3"
#define BSP_SPIM_MISO_PIN    GET_PIN(A, 2)//"A2"
#define BSP_SPIM_SCK_PIN     GET_PIN(A, 0)//"A0"

注:这里使用的是软件模拟SPI操作,也就是通过GPIO模拟SPI协议进行通信,有利于指定其他GPIO用以验证SPI基本读写功能。

访问FLASH

访问SPI MASTER设备,库中提供了以下接口:

函数 描述
rt_hw_spi_device_attach() 往SPI总线上挂载从设备
rt_device_find() 根据 SPI 设备名称查找设备获取设备句柄
rt_spi_transfer_message() 收发SPI MASTER数据

往SPI总线上挂载从设备

SPI总线可以挂载多个从设备,每多挂载一个从设备都需要先注册,绑定其CS(片选)脚,函数原型如下所示:

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);
  • 参数

    [IN]bus_name:总线设备名称,目前注册到的系统中的SPI总线名称为"spim"。

    [IN]device_name:从设备名称,可以为自定义字符串,字符串长度不能大于8个字符,且不能与系统已有的其他设备名称相同。

    [IN]cs_gpiox:GPIO配置控制器,由于AP板子的UC8088只有一个GPIO配置控制器,因此,这里参数不作处理,直接赋值为RT_NULL即可。

    [IN]cs_gpio_pin:绑定的GPIO引脚,需要指定GPIO引脚作为CS脚。

    // GPIO定义所在文件相对路径:
    // libraries/UC8088_HAL_Driver/inc/uc_gpio.h
    typedef enum
    {
        GPIO_PIN_0  = 0,
        GPIO_PIN_1  = 1,
        GPIO_PIN_2  = 2,
        GPIO_PIN_3  = 3,
        GPIO_PIN_4  = 4,
        GPIO_PIN_5  = 5,
        GPIO_PIN_6  = 6,
        GPIO_PIN_7  = 7,
        GPIO_PIN_8  = 8,
        GPIO_PIN_9  = 9,
        GPIO_PIN_10 = 10,
        GPIO_PIN_11 = 11,
        GPIO_PIN_12 = 12,
        GPIO_PIN_13 = 13,
        GPIO_PIN_14 = 14,
        GPIO_PIN_15 = 15,
        GPIO_PIN_16 = 16,
        GPIO_PIN_17 = 17,
        GPIO_PIN_18 = 18,
        GPIO_PIN_19 = 19,
        GPIO_PIN_20 = 20,
        GPIO_PIN_21 = 21,
        GPIO_PIN_22 = 22,
        GPIO_PIN_24 = 24,
        GPIO_PIN_25 = 25,
        GPIO_PIN_26 = 26,
        GPIO_PIN_27 = 27,
        GPIO_PIN_28 = 28,
        GPIO_PIN_29 = 29,
    } GPIO_PIN; /* pin number */
    
  • 返回值

    RT_EOK:成功

    其他:错误,RT-Thread定义的错误类型

    注:实际使用该函数时,可以不关心函数返回值,这是因为其内部发生错误时将产生断言,使系统停止工作。

查找SPI MASTER设备

查找SPI MASTER设备,函数原型如下所示:

rt_device_t rt_device_find(const char* name);
  • 参数

    [IN]name:SPI从设备名称,与往SPI总线上挂载从设备函数参数device_name保持一致。

  • 返回值

    RT_NULL:失败,设备不存在。

    非RT_NULL:成功,该返回值是操作SPI MASTER设备的句柄,在后续的接口中作为函数入参。

收发SPI MASTER数据

收发指定SPI MASTER设备数据,函数原型如下所示:

struct rt_spi_message* rt_spi_transfer_message(
        struct rt_spi_device*  device,
        struct rt_spi_message* message);
  • 参数

    [IN]device:SPI MASTER设备句柄,查找设备函数的返回值

    [INOUT]message:输入/输出数据,对应结构体为 struct rt_spi_message定义如下:

    // 结构体定义所在文件相对路径:
    // rt-thread\components\drivers\include\drivers\spi.h
    struct rt_spi_message
    {
        const void* send_buf;           // 发送数据内容缓存,需要rt_malloc()申请空间
        void* recv_buf;                 // 接收数据内容缓存,需要rt_malloc()申请空间
        rt_size_t length;               // 发送或接收字节长度
        struct rt_spi_message* next;    // 下一次SPI通信数据,若无置为RT_NULL
    
        unsigned cs_take    : 1;        // 第一个收/发数据时,置1,表示开始产生SPI时钟信号,后续数据该位置0
        unsigned cs_release : 1;        // 最后一个收/发数据时,置1,表示结束产生SPI时钟信号
    };
    
  • 返回值

    RT_NULL,错误,message为RT_NULL。

    不为空,则返回message指针。

    注:执行该函数后可通过以下函数获取是否成功状态:

    // 函数声明所在文件相对路径:
    // rt-thread/include/rtthread.h
    // 函数返回RT-Thread定义的错误类型
    rt_err_t rt_get_errno(void);
    

FLASH使用示例

使用示例需要配置menuconfig打开配置(默认关闭),路径如下:

wiota APP -> open spi master, default close(_SPI_MASTER_FLASH_APP_)

以下提供SPI MASTER FLASH测试实例,测试步骤如下:

  1. 擦除数据。
  2. 写入数据。
  3. 读出数据。
#include <rtthread.h>
#ifdef _SPIM_FLASH_APP_
#include <rtdevice.h>
#include <stdlib.h>
#include <time.h>
#include "uc_gpio.h"
#include "drv_spi.h"
#include "uc_spim_flash_app.h"

#define SPIM_NAME               ("spim0")
#define FLASH_CLK               (6 * 1000 * 1000)
#define FLASH_CS_PIN            (GPIO_PIN_13)
#define FLASH_DATA_ADDR         (0x7D000)
#define FLASH_DATA_LEN          (1024)

static struct rt_spi_device *spim_dev = RT_NULL;

static int spim_flash_app_init(void)
{
    spim_dev = spim_api_init(SPIM_NAME, FLASH_CS_PIN, FLASH_CLK);
    if (spim_dev)
    {
        return RT_EOK;
    }
    else
    {
        return RT_ERROR;
    }
}

void spim_flash_app_sample(void)
{
    rt_err_t ret = RT_EOK;
    rt_uint8_t *wr_buf = (rt_uint8_t *)rt_calloc(1, FLASH_DATA_LEN);
    rt_uint8_t *rd_buf = (rt_uint8_t *)rt_malloc(FLASH_DATA_LEN);
    rt_int32_t index = 0;

    rt_kprintf("enter spim_flash_app_sample()\r\n");
    srand(time(0));
    for (index = 0; index < FLASH_DATA_LEN; index++)
    {
        wr_buf[index] = rand() & 0xFF;
    }

    do
    {
        ret = spim_flash_app_init();
        if (ret != RT_EOK)
        {
            rt_kprintf("spi flash init failed!\n");
            break;
        }

        flash_erase(spim_dev, FLASH_DATA_ADDR, FLASH_DATA_LEN);
        flash_page_program(spim_dev, FLASH_DATA_ADDR, wr_buf, FLASH_DATA_LEN, SPIM_BIG);
        flash_page_read(spim_dev, rd_buf, FLASH_DATA_LEN, FLASH_DATA_ADDR, SPIM_BIG);

        if (rt_memcmp(wr_buf, rd_buf, FLASH_DATA_LEN))
        {
            rt_kprintf("data diff!\n");
        }
        else
        {
            rt_kprintf("data same\n");
        }
    } while (0);

    rt_free(wr_buf);
    rt_free(rd_buf);
}

#endif
Back to top