【RT-Thread】nxp rt10xx 设备驱动框架之--spi搭建和使用

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

开发前准备

  • 硬件平台:nxp rt10xx单片机
  • IDE: Keil

1.Kconfig 修改和menuconfig配置

Env环境menuconfigRT-Thread Components->Device Drivers 设备驱动默认为n,所以需要开启。

请添加图片描述

先在Kconfig中添加如下语句,然后在Env环境menuconfigHardware Drivers Config->On-Chip Peripheral Drivers 使能SPI,本章使用SPI4

请添加图片描述

2.工程添加SPI驱动框架和BSP驱动接口

设备驱动框架:spi_core.c spi_dev.c BSP接口:drv_spi.c fsl_lpspi.c fsl_lpspi_edma.c

请添加图片描述

3.添加或修改drv_spi.c

笔者查阅文件,基本不用改动什么

  • 定义spi device

我们基本只需要实现框架提供的:configurexfer 就行

/**
 * SPI Virtual BUS, one device must connected to a virtual BUS
 */
struct rt_spi_device
{
    struct rt_device parent;
    struct rt_spi_bus *bus;

    struct rt_spi_configuration config;
    void   *user_data;
};

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

/**
 * SPI operators
 */
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);
};

static struct rt_spi_ops imxrt_spi_ops =
{
    .configure = spi_configure,
    .xfer      = spixfer
};
  • 设备创建注册
int rt_hw_spi_bus_init(void)
{
    int i;
    rt_err_t ret = RT_EOK;

    /*Set clock source for LPSPI*/
    CLOCK_SetMux(kCLOCK_LpspiMux, EXAMPLE_LPSPI_CLOCK_SOURCE_SELECT);
    CLOCK_SetDiv(kCLOCK_LpspiDiv, EXAMPLE_LPSPI_CLOCK_SOURCE_DIVIDER);

    spi_get_dma_config();

    for (i = 0; i < sizeof(lpspis) / sizeof(lpspis[0]); i++)
    {
        lpspis[i].spi_bus.parent.user_data = &lpspis[i];

        ret = rt_spi_bus_register(&lpspis[i].spi_bus, lpspis[i].bus_name, &imxrt_spi_ops);

        if(RT_TRUE == lpspis[i].dma_flag)
        {
            lpspi_dma_config(&lpspis[i]);
        }
        else
        {
            lpspi_normal_config(&lpspis[i]);
        }
        char sem_name[RT_NAME_MAX];
        rt_sprintf(sem_name, "%s_s", lpspis[i].bus_name);
        lpspis[i].xfer_sem = rt_sem_create(sem_name, 0, RT_IPC_FLAG_PRIO);
    }

    return ret;
}
INIT_BOARD_EXPORT(rt_hw_spi_bus_init);
  • 基于imxrt spi device 驱动相关函数
void normal_xfer_callback(LPSPI_Type *base, lpspi_master_handle_t *handle, status_t status, void *userData)
{
    /* xfer complete callback */
    struct imxrt_spi *spi = (struct imxrt_spi *)userData;
    rt_sem_release(spi->xfer_sem);
}

void edma_xfer_callback(LPSPI_Type *base, lpspi_master_edma_handle_t *handle, status_t status, void *userData)
{
    /* xfer complete callback */
    struct imxrt_spi *spi = (struct imxrt_spi *)userData;
    rt_sem_release(spi->xfer_sem);
}

rt_err_t rt_hw_spi_device_attach(const char *bus_name, const char *device_name, rt_uint32_t pin)
{
    rt_err_t ret = RT_EOK;

    struct rt_spi_device *spi_device = (struct rt_spi_device *)rt_malloc(sizeof(struct rt_spi_device));
    RT_ASSERT(spi_device != RT_NULL);

    struct imxrt_sw_spi_cs *cs_pin = (struct imxrt_sw_spi_cs *)rt_malloc(sizeof(struct imxrt_sw_spi_cs));
    RT_ASSERT(cs_pin != RT_NULL);

    cs_pin->pin = pin;
    rt_pin_mode(pin, PIN_MODE_OUTPUT);
    rt_pin_write(pin, PIN_HIGH);

    ret = rt_spi_bus_attach_device(spi_device, device_name, bus_name, (void *)cs_pin);

    return ret;
}

static uint32_t imxrt_get_lpspi_freq(void)
{
    uint32_t freq = 0;

    /* CLOCK_GetMux(kCLOCK_LpspiMux):
       00b: derive clock from PLL3 PFD1 720M
       01b: derive clock from PLL3 PFD0 720M
       10b: derive clock from PLL2      528M
       11b: derive clock from PLL2 PFD2 396M
    */
    switch(CLOCK_GetMux(kCLOCK_LpspiMux))
    {
    case 0:
        freq = CLOCK_GetFreq(kCLOCK_Usb1PllPfd1Clk);
        break;

    case 1:
        freq = CLOCK_GetFreq(kCLOCK_Usb1PllPfd0Clk);
        break;

    case 2:
        freq = CLOCK_GetFreq(kCLOCK_SysPllClk);
        break;

    case 3:
        freq = CLOCK_GetFreq(kCLOCK_SysPllPfd2Clk);
        break;
    }

    freq /= (CLOCK_GetDiv(kCLOCK_LpspiDiv) + 1U);

    return freq;
}

static void lpspi_normal_config(struct imxrt_spi *spi)
{
    RT_ASSERT(spi != RT_NULL);

    LPSPI_MasterTransferCreateHandle(spi->base,
                                    &spi->spi_normal,
                                    normal_xfer_callback,
                                    spi);
    LOG_D(LOG_TAG" %s normal config done\n", spi->bus_name);
}

static void lpspi_dma_config(struct imxrt_spi *spi)
{
    RT_ASSERT(spi != RT_NULL);

    DMAMUX_SetSource(DMAMUX, spi->dma->rx_channel, spi->dma->rx_request);
    DMAMUX_EnableChannel(DMAMUX, spi->dma->rx_channel);
    EDMA_CreateHandle(&spi->dma->rx_edma, DMA0, spi->dma->rx_channel);

    DMAMUX_SetSource(DMAMUX, spi->dma->tx_channel, spi->dma->tx_request);
    DMAMUX_EnableChannel(DMAMUX, spi->dma->tx_channel);
    EDMA_CreateHandle(&spi->dma->tx_edma, DMA0, spi->dma->tx_channel);

    LPSPI_MasterTransferCreateHandleEDMA(spi->base,
                                        &spi->dma->spi_edma,
                                        edma_xfer_callback,
                                        spi,
                                        &spi->dma->rx_edma,
                                        &spi->dma->tx_edma);

    LOG_D("%s dma config done\n", spi->bus_name);
}

static rt_err_t spi_configure(struct rt_spi_device *device, struct rt_spi_configuration *cfg)
{
    lpspi_master_config_t masterConfig;
    struct imxrt_spi *spi = RT_NULL;

    RT_ASSERT(cfg != RT_NULL);
    RT_ASSERT(device != RT_NULL);

    spi = (struct imxrt_spi *)(device->bus->parent.user_data);
    RT_ASSERT(spi != RT_NULL);

    if(cfg->data_width != 8 && cfg->data_width != 16 && cfg->data_width != 32)
    {
        return RT_EINVAL;
    }

    LPSPI_MasterGetDefaultConfig(&masterConfig);

    if(cfg->max_hz > 40*1000*1000)
    {
        cfg->max_hz = 40*1000*1000;
    }
    masterConfig.baudRate     = cfg->max_hz;
    masterConfig.bitsPerFrame = cfg->data_width;

    if(cfg->mode & RT_SPI_MSB)
    {
        masterConfig.direction = kLPSPI_MsbFirst;
    }
    else
    {
        masterConfig.direction = kLPSPI_LsbFirst;
    }

    if(cfg->mode & RT_SPI_CPHA)
    {
        masterConfig.cpha = kLPSPI_ClockPhaseSecondEdge;
    }
    else
    {
        masterConfig.cpha = kLPSPI_ClockPhaseFirstEdge;
    }

    if(cfg->mode & RT_SPI_CPOL)
    {
        masterConfig.cpol = kLPSPI_ClockPolarityActiveLow;
    }
    else
    {
        masterConfig.cpol = kLPSPI_ClockPolarityActiveHigh;
    }

//    masterConfig.whichPcs                      = kLPSPI_Pcs0;
    masterConfig.pinCfg                        = kLPSPI_SdiInSdoOut;
    masterConfig.dataOutConfig                 = kLpspiDataOutTristate;
    masterConfig.pcsToSckDelayInNanoSec        = 1000000000 / masterConfig.baudRate;
    masterConfig.lastSckToPcsDelayInNanoSec    = 1000000000 / masterConfig.baudRate;
    masterConfig.betweenTransferDelayInNanoSec = 1000000000 / masterConfig.baudRate;

    LPSPI_MasterInit(spi->base, &masterConfig, imxrt_get_lpspi_freq());
    spi->base->CFGR1 |= LPSPI_CFGR1_PCSCFG_MASK;

    return RT_EOK;
}

static rt_uint32_t spixfer(struct rt_spi_device *device, struct rt_spi_message *message)
{
    lpspi_transfer_t transfer;
    status_t status;
    RT_ASSERT(device != RT_NULL);
    RT_ASSERT(device->bus != RT_NULL);
    RT_ASSERT(device->bus->parent.user_data != RT_NULL);

    struct imxrt_spi *spi = (struct imxrt_spi *)(device->bus->parent.user_data);
    struct imxrt_sw_spi_cs *cs = device->parent.user_data;

    if(message->cs_take)
    {
        rt_pin_write(cs->pin, PIN_LOW);
    }

    transfer.dataSize = message->length;
    transfer.rxData   = (uint8_t *)(message->recv_buf);
    transfer.txData   = (uint8_t *)(message->send_buf);

    if(RT_FALSE == spi->dma_flag)
    {
#ifdef BSP_USING_BLOCKING_SPI
        status = LPSPI_MasterTransferBlocking(spi->base, &transfer);
#else
        status = LPSPI_MasterTransferNonBlocking(spi->base, &spi->spi_normal, &transfer);
#endif
    }
    else
    {
        status = LPSPI_MasterTransferEDMA(spi->base,&spi->dma->spi_edma,&transfer);
    }
    rt_sem_take(spi->xfer_sem, RT_WAITING_FOREVER);

    if(message->cs_release)
    {
        rt_pin_write(cs->pin, PIN_HIGH);
    }

    if (status != kStatus_Success)
    {
        LOG_E("%s transfer error : %d", spi->bus_name,status);
        message->length = 0;
    }

    return message->length;
}

4.搭建应用层demo

底层IO初始化,片选信号已经在底层重新封装,不必再初始化(rt10xx spi 默认有片选外设,实际应用中不一定用到,可以选其他IO一样可以使用)

 	IOMUXC_SetPinMux(IOMUXC_GPIO_B0_03_LPSPI4_SCK, 0U); 
	// IOMUXC_SetPinMux(IOMUXC_GPIO_B0_00_LPSPI4_PCS0, 0U);  IOMUXC_GPIO_B0_00_GPIO2_IO00
	IOMUXC_SetPinMux(IOMUXC_GPIO_B0_02_LPSPI4_SDO, 0U); 
	IOMUXC_SetPinMux(IOMUXC_GPIO_B0_01_LPSPI4_SDI, 0U); 

	IOMUXC_SetPinConfig(IOMUXC_GPIO_B0_03_LPSPI4_SCK, 0x10B0u);
	// IOMUXC_SetPinConfig(IOMUXC_GPIO_B0_00_LPSPI4_PCS0, 0x10B0u);
	IOMUXC_SetPinConfig(IOMUXC_GPIO_B0_02_LPSPI4_SDO, 0x10B0u);
	IOMUXC_SetPinConfig(IOMUXC_GPIO_B0_01_LPSPI4_SDI, 0x10B0u);	

应用中初始化SPI4(我们需要特别注意spi总线spi设备的概念),然后发送一串数据:0xaa,0x55,0x66,0x33

/**************************************************START OF FILE*****************************************************/






/*------------------------------------------------------------------------------------------------------------------
Includes
*/
#include <rtthread.h>
#include <rtdevice.h>
#include "drv_gpio.h"
#include "drv_spi.h" 


/*------------------------------------------------------------------------------------------------------------------
Macros
*/
#define SPI_BUS_NAME         "spi4"
#define SPI_DEVICE_NAME      "spi40"

#define SPI4_PIN_CS          GET_PIN(2, 0)

/*------------------------------------------------------------------------------------------------------------------
Variables
*/
struct rt_spi_device *spi_dev = NULL;
rt_uint32_t pincs;

/*------------------------------------------------------------------------------------------------------------------
Functions
*/
void spi_sample(void)
{
    rt_uint8_t sendBuf[4] = {0xaa,0x55,0x66,0x33};

	pincs = SPI4_PIN_CS;
	
	rt_hw_spi_device_attach(SPI_BUS_NAME, SPI_DEVICE_NAME, pincs);
	
    /* 查找 spi 设备获取设备句柄 */
    spi_dev = (struct rt_spi_device *)rt_device_find(SPI_DEVICE_NAME);
    if (!spi_dev)
    {
        rt_kprintf("spi sample run failed! can't find %s device!\n", SPI_BUS_NAME);
    }
    else
    {
        struct rt_spi_configuration cfg;
        cfg.data_width = 8;
        cfg.mode = RT_SPI_MASTER | RT_SPI_MODE_0 | RT_SPI_MSB; /* SPI Compatible Modes 0 */
        cfg.max_hz = 1000 * 1000; 
        rt_spi_configure(spi_dev, &cfg);		
        rt_spi_send_then_recv(spi_dev, sendBuf, 4, NULL, 0);
    }
}
/* 导出到 msh 命令列表中 */
MSH_CMD_EXPORT(spi_sample, spi sample);


/****************************************************END OF FILE*****************************************************/


SPI信号接入逻辑分析仪,然后输入命令spi_sample运行应用,查看SPI总线结果

请添加图片描述

请添加图片描述

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值