理解nordic ncs设备驱动模型--SPIM

一、 Zephyr Project介绍

Zephyr Project是Linux基金会推出的一个Apache2.0开源项目,版权非常友好,适合用于商业项目开发。包含RTOS、编译系统、各类第三方库。NCS中的例程基本都跑在Zephyr RTOS上,Zephyr不单单是一个用来做多线程的RTOS,它更大的价值在于其自带的各种开源的协议栈、框架、软件包、驱动代码等。如果不是为了使用这些现成的协议栈和软件包,只是单纯使用RTOS,就和其他RTOS没有区别了。

Zephyr采用Kconfig对这些软件包进行管理,可以方便地使能或剪裁。而为了使Zephyr自带的硬件驱动代码能够通用,Zephyr采用了DeviceTree来描述硬件。各个半导体厂商把自己的硬件描述成标准DeviceTree,并且按照Zephyr的接口提供驱动代码,然后一起提交给Zephyr。在方便地使用Zephyr中协议栈的同时,用户还能简单方便地使用到各个半导体厂家的硬件功能。

二、SPIM使用示例

1. prj.conf(系统剪裁)

#使能SPI驱动器
CONFIG_SPI=y
#如果使用SPI ASYNC
CONFIG_SPI_ASYNC=y   

#使能RTT控制台
CONFIG_CONSOLE=n
CONFIG_UART_CONSOLE=n
CONFIG_LOG=y
CONFIG_USE_SEGGER_RTT=y
CONFIG_LOG_BACKEND_RTT=y
CONFIG_LOG_BACKEND_UART=n

2.设备树配置

//pinctrl 是一个模拟节点,用于引脚分配
&pinctrl {
    spi_master_default: spi_master_default {
        group1 {
            psels = <NRF_PSEL(SPIM_SCK, 0, 4)>,
                    <NRF_PSEL(SPIM_MOSI, 0, 5)>,
                    <NRF_PSEL(SPIM_MISO, 0, 6)>;
        };
    };

    spi_master_sleep: spi_master_sleep {
        group1 {
            psels = <NRF_PSEL(SPIM_SCK, 0, 4)>,
                    <NRF_PSEL(SPIM_MOSI, 0, 5)>,
                    <NRF_PSEL(SPIM_MISO, 0, 6)>;
            low-power-enable;
        };
    };
};

//配置SPI1设备节点信息
my_spi_master: &spi1 {
    compatible = "nordic,nrf-spim";
    status = "okay";
    pinctrl-0 = <&spi_master_default>;
    pinctrl-1 = <&spi_master_sleep>;
    pinctrl-names = "default", "sleep";
    cs-gpios = <&gpio0 7 GPIO_ACTIVE_LOW>;
    reg_my_spi_master:  spi-dev-a@0 {
        reg = <0>;
    };
};

3.spim_driver.c编写


#include <zephyr/kernel.h>
#include <zephyr/device.h>
#include <zephyr/devicetree.h>
#include <zephyr/drivers/spi.h>
#include <zephyr/logging/log.h>

/* 1000 msec = 1 sec */
#define SLEEP_TIME_MS   200

//
#define MY_SPI_MASTER DT_NODELABEL(my_spi_master)
#define MY_SPI_MASTER_CS_DT_SPEC SPI_CS_GPIOS_DT_SPEC_GET(DT_NODELABEL(reg_my_spi_master))

//定义一个device,并通过设备树获取设备节点
const struct device *spi_dev =  DEVICE_DT_GET(MY_SPI_MASTER);

//RTT logger info
#define LOG_MODULE_NAME spi_master
LOG_MODULE_REGISTER(LOG_MODULE_NAME);

/**
 * @brief 初始化SPI设备
 * 
 * 该函数用于初始化SPI主设备及其相关的片选GPIO引脚。
 * 主要包括获取设备句柄、检查设备是否就绪等操作。
 * 
 * @param 无
 * 
 * @return 无
 */
static void spi_master_init(void)
{
    //spi_dev = DEVICE_DT_GET(MY_SPI_MASTER);
    if(!device_is_ready(spi_dev)) {
        printk("SPI master device not ready!\n");
    }
    struct gpio_dt_spec spim_cs_gpio = MY_SPI_MASTER_CS_DT_SPEC;
    if(!device_is_ready(spim_cs_gpio.port)){
        printk("SPI master chip select device not ready!\n");
    }
}

static struct spi_config spi_cfg = {
    .operation = SPI_WORD_SET(8) | SPI_TRANSFER_MSB,
    .frequency = 4000000,
    .slave = 0,
    //通过reg_my_spi_master找到父节点(spi1),并获取cs-gpios节点spec
    //.cs = {.gpio = MY_SPI_MASTER_CS_DT_SPEC, .delay = 0},
    //或者通过父节点找到cs_gpios节点信息
    .cs = {
    .gpio = {
        .port = DEVICE_DT_GET(DT_GPIO_CTLR(DT_NODELABEL(my_spi_master), cs_gpios)),
        .pin = DT_GPIO_PIN(DT_NODELABEL(my_spi_master), cs_gpios),
        .dt_flags = GPIO_ACTIVE_LOW,
    },
    .delay = 0,
    }
};

/**
 * @brief 通过SPI总线同时发送和接收数据
 * 
 * @param tx_buffer 指向发送数据缓冲区的指针
 * @param rx_buffer 指向接收数据缓冲区的指针
 * @param len       要传输的数据长度(字节数)
 * 
 * @return 返回spi_transceive函数的执行结果,通常为0表示成功,负值表示失败
 * 
 * 该函数使用SPI全双工通信方式,同时发送和接收指定长度的数据。
 * 发送数据来自tx_buffer,接收到的数据存储在rx_buffer中。
 */
static int spim_write_read_data(uint8_t *tx_buffer, uint8_t *rx_buffer, size_t len)
{

    const struct spi_buf tx_buf = {
        .buf = tx_buffer,
        .len = len,
    };
    const struct spi_buf_set tx = {
        .buffers = &tx_buf,
        .count = 1
    };

    struct spi_buf rx_buf = {
        .buf = rx_buffer,
        .len = len,
    };
    const struct spi_buf_set rx = {
        .buffers = &rx_buf,
        .count = 1
    };
    return spi_transceive(spi_dev, &spi_cfg, &tx, &rx);
}

int main(void)
{
    int ret;
    uint8_t tx_buffer[2] = {0x00,0x01};
    uint8_t rx_buffer[2];

    spi_master_init();

    LOG_INF("SPI master example started\n");

    while (1) {
        ret = spim_write_read_data(tx_buffer,rx_buffer,2);
        if(ret != 0){
            LOG_INF("SPI master error: %i\n", ret);
        }
        k_msleep(SLEEP_TIME_MS);
        LOG_INF("APP Running\r\n");
    }

    return 0;
}

4.spi发送实测波形

image
可以看到,CS引脚的拉低时间过长,约48us,后续会介绍通过nrfx配置寄存器的方式,直接配置spi驱动器,解决该问题。

posted @ 2025-12-11 17:25  羊的第七章  阅读(6)  评论(0)    收藏  举报