0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
会员中心
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

nRF Connect SDK Basic

jf_14701710 来源:jf_14701710 作者:jf_14701710 2025-08-20 10:41 次阅读
加入交流群
微信小助手二维码

扫码添加小助手

加入工程师交流群

用户在使用 nRF connect SDK 的时候经常会操作的外设有GPIO,I2C,SPI,UART。我们就以 nRF connect SDK 2.7.0 中的例程代码 nrfsamplesbluetoothperipheral_lbs 为基础,来演示上述外设的简单使用。使用的硬件是开发板 nRF52840 DK.

准备工作

首先我们在原本的工程目录的 boards 文件夹里,添加文件 nrf52840dk_nrf52840.overlay。通过这个文件我们可以修改 devicetree。 编译完成后,我们可以查看 buildzephyrzephyr.dts,以确认devicetree 的更改是否生效。

我们还可以通过修改 prj.conf 来修改 Kconfig。编译完成后,我们可以查看 buildzephyr.config 以确认 Kconfig 的更改是否生效。

wKgZPGilNdmACeSdAABK1LsANgo21.jpeg

GPIO 控制

首先我们演示如何删除原有的按键和LED的 node 。按照下面的代码,来修改 devicetree,就可以删除 button3 和 led3。

/ {
    aliases {
        /delete-property/ sw3;
        /delete-property/ led3;
    };
};


/delete-node/ &button3;
/delete-node/ &led3;

接着我们来更改控制 led2 的管脚。这里我们用 P0.04 控制 led2。

&led2 {
    gpios = < &gpio0 4 GPIO_ACTIVE_LOW >;
};

最后我们添加一个用户 GPIO 。这里添加了一个名为 user_gpios 的 node。然后又定义了 user_io0,它是 user_gpios 的 subnode。

/ {
    user_gpios {
        compatible = "gpio-leds";
        user_io0: user_io0 {
            gpios = < &gpio0 16 GPIO_ACTIVE_LOW >;
            label = "user gpio 0";
        };
    };
};

我们不仅在 devicetree 里添加这个 GPIO ,还要在 main.c 里添加代码使用这个GPIO。下面这句代码中,我们声明了结构体变量 user_gpio0,并用宏 GPIO_DT_SPEC_GET 根据 devicetree 里的定义初始化它。

const struct gpio_dt_spec user_gpio_0 = GPIO_DT_SPEC_GET(DT_NODELABEL(user_io0),gpios);

下面这段代码中 gpio_is_ready_dt 是用来检查 GPIO 的状态是否是就绪。用函数 gpio_pin_configure_dt 把 user_gpio_0 配置成输出。gpio_pin_toggle_dt 用来翻转 GPIO。

if (!gpio_is_ready_dt(&user_gpio_0)) {
    printk("%s: device not ready.n", user_gpio_0.port->name);
    return 0;
}
gpio_pin_configure_dt(&user_gpio_0, GPIO_OUTPUT_ACTIVE);

for (index = 0; index < 100; index++) {
    gpio_pin_toggle_dt(&user_gpio_0);
    k_sleep(K_MSEC(100));
}

从下面的代码可以看出翻转 GPIO 这个操作有两种 API 可以调用。二者的主要区别是 gpio_pin_toggle_dt 不需要指明引脚 。

/**
 * @brief Toggle pin level from a @p gpio_dt_spec.
 *
 * This is equivalent to:
 *
 *     gpio_pin_toggle(spec->port, spec->pin);
 *
 * @param spec GPIO specification from devicetree
 * @return a value from gpio_pin_toggle()
 */
static inline int gpio_pin_toggle_dt(const struct gpio_dt_spec *spec)
{
    return gpio_pin_toggle(spec->port, spec->pin);
}

I2C 设备控制

Nordic 的芯片中 I2C 接口是由外设 TWI 来实现的,I2C master 由 TWIM 实现, I2C master 由 TWIS 实现。这里将演示如何用一个 TWIM 来连接两个 I2C slave 设备。

首先我们还是先修改 devicetree。我们使用 i2c1 这个 node。 一方面按照应用的要求修改这个 node 的 propertise,另一方面在这个 node 里创建两个 sub-node。

i2c 的时钟频率通过 clock-frequency 来定义。

i2c 的引脚通过 pinctrl-0 和 pinctrl-1 定义。我们将在后面分析 i2c1_default 和 i2c1_sleep 的定义。

这两个 sub-node 一个是 user_i2c_sensor,另一个是 user_i2c_eeprom。这两个 sub-node 通过 propertise reg 来定义各自的 I2C 地址。

&i2c1 {
    status = "ok";
    clock-frequency = < I2C_BITRATE_STANDARD >;
    pinctrl-0 = < &i2c1_default >;
    pinctrl-1 = < &i2c1_sleep >;
    pinctrl-names = "default", "sleep";
    user_i2c_sensor: user_i2c_sensor@0 {
        compatible = "i2c-user-define";
        reg = < 0xA >;
    };
    user_i2c_eeprom: user_i2c_eeprom@0 {
        compatible = "i2c-user-define";
        reg = < 0x5 >;
    };
};

i2c1_default 和 i2c1_sleep的定义如下。TWIM_SDA 信号使用的是引脚 P0.04,TWIM_SCL 信号使用的是引脚 P0.03。

&pinctrl {
    i2c1_default: i2c1_default {
        group1 {
            psels = < NRF_PSEL(TWIM_SDA, 0, 4) >,
                < NRF_PSEL(TWIM_SCL, 0, 3) >;
        };
    };

    i2c1_sleep: i2c1_sleep {
        group1 {
            psels = < NRF_PSEL(TWIM_SDA, 0, 4) >,
                < NRF_PSEL(TWIM_SCL, 0, 3) >;
            low-power-enable;
        };
    };

};

修改 prj.conf 添加 CONFIG_I2C=y

修改完 devicetree 我们在来添加操作 i2c 的代码。分别定义 i2c1_sensor 和 i2c1_eeprom,它们对应刚才 i2c1 的两个子节点。

const struct i2c_dt_spec i2c1_sensor = I2C_DT_SPEC_GET(DT_NODELABEL(user_i2c_sensor));
const struct i2c_dt_spec i2c1_eeprom= I2C_DT_SPEC_GET(DT_NODELABEL(user_i2c_eeprom));

i2c 设备在读写操作前无需调用 API 来配置 ,直接调用下面的写函数。

err = i2c_write_dt(&i2c1_sensor, buf, 1);

err = i2c_write_dt(&i2c1_eeprom, buf, 1);

通过逻辑分析仪我们可以看到如下的总线数据,操作的目标地址分别是我们在 devicetree 里设置的数值 0x05 和 0x0A 。

wKgZPGilNdqATuRpAABLFQTcgaw05.jpeg

SPI 设备控制

Nordic 的芯片中 SPI 接口的 master 端通过 SPIM 实现, slave 端通过 SPIS 实现。这里将演示如何用一个 SPIM 来连接两个 SPI slave 设备。

首先修改 devicetree。

这里我们使用 spi2, 并且关闭 spi1。在 nordic 的nRF52 系列芯片中,相同数字编号的 TWIM, TWIS, SPIM, SPIS 是共用一组硬件模块的。在上面 i2c 中我们已经使用 i2c1, 所以这里我们就不能同时使用 spi1了。

cs-gpios 定义了 P0.26 和 P0.27 两 个CS 信号。 SPI 用不同的片选信号,区分不同的 slave 设备。

devicetree node spi2 下定义了两个 sub-node 分别是 user_spi_adc 和 user_spi_flash。 sub-node 里定义了三个 propertise。propertise compatible 的取值来自于我们在工程里新添加的文件 dtsbindingsspi-user-define.yaml。 propertise reg 的取值和前面的 propertise cs-gpios 呼应,reg = <0> 的 sub-node 使用 cs-gpios 里面定义的第一个 CS 引脚。reg = <1> 的 sub-node 使用 cs-gpios 里面定义的第二个 CS 引脚。propertise spi-max-frequency 定义 SPI 的时钟频率。两个不同的 SPI 设备可以使用不同的时钟频率驱动。

&spi1 {
    status = "disabled";
};
&spi2 {
    status = "okay";
    cs-gpios = < &gpio0 26 GPIO_ACTIVE_LOW >,
               < &gpio0 27 GPIO_ACTIVE_LOW >;
    pinctrl-0 = < &spi2_default >;
    pinctrl-1 = < &spi2_sleep >;
    pinctrl-names = "default", "sleep";

    user_spi_adc: user_spi_adc@0 {
        compatible = "spi-user-define";
        reg = < 0 >;
        spi-max-frequency = < DT_FREQ_M(8) >;
    };
    user_spi_flash: user_spi_flash@0 {
        compatible = "spi-user-define";
        reg = < 1 >;
        spi-max-frequency = < DT_FREQ_M(8) >;
    };
};

来看一下我们新添加的 dtsbindingsspi-user-define.yaml 里面的内容。如下图 spi-user-define.yaml 里面包含了 spi-device.yaml 文件,这个文件的位置在目录 zephyrdtsbindingsspi 。

compatible: "spi-user-define"

include: [spi-device.yaml]

spi-device.yaml 文件里面定义了 spi 节点需要的一些 propertise。 比如我们在 sub-node 里定义的 propertise spi-max-frequency。

# Copyright (c) 2018, I-SENSE group of ICCS
# SPDX-License-Identifier: Apache-2.0

# Common fields for SPI devices

include: [base.yaml, power.yaml]

on-bus: spi

properties:
  reg:
    required: true
  spi-max-frequency:
    type: int
    required: true
    description: Maximum clock frequency of device's SPI interface in Hz
  duplex:
    type: int
    default: 0
    description: |
      Duplex mode, full or half. By default it's always full duplex thus 0
      as this is, by far, the most common mode.
      Use the macros not the actual enum value, here is the concordance
      list (see dt-bindings/spi/spi.h)
        0    SPI_FULL_DUPLEX
        2048 SPI_HALF_DUPLEX
    enum:
      - 0
      - 2048
  frame-format:
    type: int
    default: 0
    description: |
      Motorola or TI frame format. By default it's always Motorola's,
      thus 0 as this is, by far, the most common format.
      Use the macros not the actual enum value, here is the concordance
      list (see dt-bindings/spi/spi.h)
        0     SPI_FRAME_FORMAT_MOTOROLA
        32768 SPI_FRAME_FORMAT_TI
    enum:
      - 0
      - 32768
  spi-cpol:

SPI 引脚定义如下 CLK P0.28, MISO P0.29, MOSI P0.30。

spi2_default: spi2_default {
    group1 {
        psels = < NRF_PSEL(SPIM_SCK, 0, 28) >,
                < NRF_PSEL(SPIM_MISO, 0, 29) >,
                < NRF_PSEL(SPIM_MOSI, 0, 30) >;
    };
};
spi2_sleep: spi2_sleep {
    group1 {
        psels = < NRF_PSEL(SPIM_SCK, 0, 28) >,
                < NRF_PSEL(SPIM_MISO, 0, 29) >,
                < NRF_PSEL(SPIM_MOSI, 0, 30) >;
        low-power-enable;
    };
};

修改 prj.conf 添加 CONFIG_SPI=y CONFIG_SPI_ASYNC=y。

在 main.c 里添加 SPI 的应用代码。下面这段代码定义了两个结构体变量,并通过宏 SPI_DT_SPEC_GET 用 devicetree 里的参数初始化了这两个结构体变量。

#define SPI_OP     SPI_OP_MODE_MASTER | SPI_MODE_CPOL | SPI_MODE_CPHA 
                   | SPI_WORD_SET(8) | SPI_LINES_SINGLE
static struct spi_dt_spec spim2_adc = SPI_DT_SPEC_GET(DT_NODELABEL(user_spi_adc), SPI_OP, 0);
static struct spi_dt_spec spim2_flash = SPI_DT_SPEC_GET(DT_NODELABEL(user_spi_flash), SPI_OP, 0);

spi 驱动支持多 buffer 所以要定义 buffer 个数,和每个 buffer 的长度。同样 spi 在读写之前无需调用配置函数,直接调用读写函数就行。

struct spi_buf_set tx_bufs;
struct spi_buf spi_tx_buf;

tx_bufs.buffers = &spi_tx_buf;
tx_bufs.count = 1;
spi_tx_buf.buf = buf;
spi_tx_buf.len = 2;

err = spi_write_dt(&spim2_adc, &tx_bufs);
err = spi_write_dt(&spim2_flash, &tx_bufs);

下面是SPI的波形。可以看到和不同的 spi slave 设备通讯的时候, spi master 会拉低不同的 CS 引脚。

wKgZO2ilNdqAfdkyAAAzjUGgVu089.jpeg

UART 控制

Nordic 的芯片中 UART 接口叫做 UARTE。这里的 E 是指 EasyDMA , UART 可以使用 DMA 来连续收发。

修改 Devicetree。这里使用 uart1。propertise current-speed 设置 uart 的波特率。

&uart1 {
    status = "okay";
    current-speed = < 115200 >;
    pinctrl-0 = < &uart1_default >;
    pinctrl-1 = < &uart1_sleep >;
    pinctrl-names = "default", "sleep";
};

TXD pin 为 P1.02, RXD pin 为 P1.01。

 uart1_default: uart1_default {
    group1 {
        psels = < NRF_PSEL(UART_RX, 1, 1) >;
        bias-pull-up;
    };
    group2 {
        psels = < NRF_PSEL(UART_TX, 1, 2) >;
    };
};

uart1_sleep: uart1_sleep {
    group1 {
        psels = < NRF_PSEL(UART_RX, 1, 1) >,
            < NRF_PSEL(UART_TX, 1, 2) >;
        low-power-enable;
    };
};

修改 prj.conf 在里面添加 CONFIG_UART_ASYNC_API=y CONFIG_UART_ASYNC_RX_HELPER=y。

修改 main.c 添加 uart 收发代码。 uart_callback_set 设置 callback 函数 uart_cb。因为这里采用的是异步收发的模式,所以设置callback 函数是必备的。uart_rx_enable 使能接收。uart_tx 发送数据。

err = uart_callback_set(uart1, uart_cb, NULL);
//printk("uart_callback_set return %dn", err);

err = uart_rx_enable(uart1, uart_rx_buf, MAX_UART_BUF_LEN, UART_RX_TIMEOUT_MS);
//printk("uart_rx_enable return %dn", err);

err = uart_tx(uart1, uart_tx_buf, 6, SYS_FOREVER_MS);
//printk("uart_tx return %dn", err);

callback 函数 uart_cb 可能由多种事件触发。比如当接收到数据后会触发回调,并在参数 EVT 传递 UART_RX_RDY 和接收到的数据和长度。

static void uart_cb(const struct device *dev, struct uart_event *evt, void *user_data)
{
    ARG_UNUSED(dev);

    //LOG_INF("uart_cb evt->type:%d", evt->type);
    switch (evt->type) {
    case UART_TX_DONE:
        printk("UART_TX_DONEn");
        break;

    case UART_RX_RDY:
        printk("UART_RX_RDYn");
        printk("received %d bytesn", evt->data.rx.len);
        break;

    case UART_RX_DISABLED:
        printk("UART_RX_DISABLEDn");
        break;

    case UART_RX_BUF_REQUEST:
        printk("UART_RX_BUF_REQUESTn");
        uart_rx_buf_rsp(uart1, uart_rx_buf2, MAX_UART_BUF_LEN);
        break;

    case UART_RX_BUF_RELEASED:
        printk("UART_RX_BUF_RELEASEDn");
        break;

    case UART_TX_ABORTED:
        printk("UART_TX_ABORTEDn");
        break;

    default:
        break;
    }
}

我们在 DK 上把 TXD 引脚和 RXD 引脚短接来测试 UART 的收发,可以看到如下的 log 信息。UART 收到了自己发送的6字节的数据。

wKgZPGilNduAY7_BAACdqEMlnvU82.jpeg

UART 应用代码的优化

上面的 uart 演示代码中,我们只实现了简单的收发。下面我们将进一步在此基础上优化 UART 的收发代码。这一部分的修改都在 main.c 里,主要涉及下面几个部分:

Thread 线程

Semaphore 信号量

线程间通讯 Message queue

线程 下面的代码中通过 K_THREAD_DEFINE 定义了 一个独立的线程来处理 uart 相关的代码。线程处理函数 uart_thread_task 中:也是先用 uart_callback_set 设置了回调函数;再用 uart_rx_enable 使能了接收;然后是一个 for 循环,在里面不断的接收消息,根据消息中的指令发送数据,或者处理接收到的数据。

#define UART_THREAD_STACK_SIZE    512
#define UART_THREAD_PRIORITY      -1

void uart_thread_task(void)
{
    int err;
    struct uart_data_item_type uart_msgq;

    k_sem_take(&uart_thread_start, K_FOREVER);

    printk("uart_thread_taskn");

    err = uart_callback_set(uart1, uart_cb, NULL);

    err = uart_rx_enable(uart1, uart_rx_buf, MAX_UART_BUF_LEN, UART_RX_TIMEOUT_MS);

    for (;;) {
        k_msgq_get(&uart_data_msgq, &uart_msgq, K_FOREVER);
        printk("received uart data itemn");

        switch(uart_msgq.cmd) {
        case UART_CMD_TX:
        memcpy(uart_tx_buf,&uart_msgq.data, sizeof(uint32_t));
        err = uart_tx(uart1, uart_tx_buf, sizeof(uint32_t), SYS_FOREVER_MS);
        break;

        case UART_CMD_DATA_PROCESS:
        break;

        default:
            break;
        }
    }
}

K_THREAD_DEFINE(uart_thread_id, UART_THREAD_STACK_SIZE, uart_thread_task, NULL, NULL,
                NULL, UART_THREAD_PRIORITY, 0, 0);

上面的代码中用 K_THREAD_DEFINE 定义线程的时候,需要指定此线程的优先级 UART_THREAD_PRIORITY。UART_THREAD_PRIORITY 的数据类型是 integer,可以是正数也可以是负数。优先级的数字越小,优先级越高,负数的优先级比正数高。thread 的优先级取值为负数时,此 thread 为协同线程 cooperative thread 。当这种线程正在执行的时候,其它更高优先级的线程不能打断它,必须等它执行完再执行下一个线程。当 thread 的优先级取值为正数,此 thread 为抢占线程 preemptible thread。当这种线程正在执行的时候,其它更高优先级的线程可以打断它,跳转到高优先级的任务。等高优先级的线程执行完才返回原 thread 继续执行。回到例程代码,从应用的角度出发,我们希望 uart_thread_task 的执行优先级大于 main 函数。通过查询文件 buildzephyr.config 我们得知 CONFIG_MAIN_THREAD_PRIORITY 的取值为 0,也就是说 main thread 当前的优先级为 0, 所以我们定义了 UART_THREAD_PRIORITY 为 -1。这样 uart thread 的优先级就高于 main thread, 而且 uart thread 的执行不会被其它更高优先级的 thread 打断。需要注意的是这里的不能被打断只是对 thread 而言,中断是可以打断 cooperative thread 的。

信号量 函数 uart_thread_task 的优先级比 main 函数高,所以会先于main 函数执行。如果之前的函数 uart_thread_task 里没有 k_sem_take(&uart_thread_start, K_FOREVER),就会出现如下图的现象。我们看到 uart thread 的 log 是先于 main thread 被打印出来的。

wKgZO2ilNdyASkHZAACxi5yzJDg78.jpeg

从应用的角度,我们希望 uart_thread_task 在 main 函数启动完广播之后再执行。这就引入了一个不同线程之间的同步问题。Zephyr RTOS 中可以通过 semaphore 解决不同 thread 间的同步问题。下面的代码中通过 K_SEM_DEFINE 定义了一个为 uart_thread_start 的 semaphore 。 函数 uart_thread_task 执行到函数 k_sem_take 时,如果 uart_thread_start 没有被释放,当前 thread 会被挂起等待,直到 semaphore 被释放。

static K_SEM_DEFINE(uart_thread_start, 0, 1);

#define UART_THREAD_STACK_SIZE    512
#define UART_THREAD_PRIORITY      -1

void uart_thread_task(void)
{
    int err;
    struct uart_data_item_type uart_msgq;

    k_sem_take(&uart_thread_start, K_FOREVER);

    printk("uart_thread_taskn");

    err = uart_callback_set(uart1, uart_cb, NULL);

    err = uart_rx_enable(uart1, uart_rx_buf, MAX_UART_BUF_LEN, UART_RX_TIMEOUT_MS);

    for (;;) {

在 main 里通过 k_sem_give 释放 uart_thread_start。uart 线程会打断当前的 main thread 从 k_sem_take 继续执行。

err = spi_write_dt(&spim2_adc, &tx_bufs);
err = spi_write_dt(&spim2_flash, &tx_bufs);

k_sem_give(&uart_thread_start);

struct uart_data_item_type main_msgq;

main_msgq.cmd = UART_CMD_TX;
main_msgq.data = 0;

for (;;) {

    while (k_msgq_put(&uart_data_msgq, &main_msgq, K_NO_WAIT) != 0) {
        /* message queue is full: purge old data & try again */
        k_msgq_purge(&uart_data_msgq);
    }
    main_msgq.data++;

    dk_set_led(RUN_STATUS_LED, (++blink_status) % 2);
    k_sleep(K_MSEC(RUN_LED_BLINK_INTERVAL));
}

线程间通讯 演示代码中 main thread 会把要发送的数据通过线程通讯发送到 uart thread, uart thread 调用驱动函数发送。zephyr 中提供了多种线程间通讯方式,具体如下图,这里使用的是 message queue。

wKgZPGilNdyADvoPAABtzOcdD7823.jpeg

下面的代码中 K_MSGQ_DEFINE 定义了一个名为 uart_data_msgq 的 message queue。uart_data_msgq 的缓冲区里最多可以容纳 8 个消息。

struct uart_data_item_type {
    uint8_t cmd;
    uint32_t data;
};

K_MSGQ_DEFINE(uart_data_msgq, sizeof(struct uart_data_item_type), 8, 4);

下面这段代码来自于 main thread 的 main 函数。代码会定时循环把待发送的数据打包成一个 message,然后推送到 message queue 里面。

struct uart_data_item_type main_msgq;

main_msgq.cmd = UART_CMD_TX;
main_msgq.data = 0;

for (;;) {

    while (k_msgq_put(&uart_data_msgq, &main_msgq, K_NO_WAIT) != 0) {
        /* message queue is full: purge old data & try again */
        k_msgq_purge(&uart_data_msgq);
    }
    main_msgq.data++;

    dk_set_led(RUN_STATUS_LED, (++blink_status) % 2);
    k_sleep(K_MSEC(RUN_LED_BLINK_INTERVAL));
}

下面的代码来自 uart thread 的 uart_thread_task 函数。 函数等待 message queue 里推送来的 message。得到 message 后,根据里面的 cmd 字段来处理发送或者接收数据。

void uart_thread_task(void)
{
    int err;
    struct uart_data_item_type uart_msgq;

    k_sem_take(&uart_thread_start, K_FOREVER);

    printk("uart_thread_taskn");

    err = uart_callback_set(uart1, uart_cb, NULL);

    err = uart_rx_enable(uart1, uart_rx_buf, MAX_UART_BUF_LEN, UART_RX_TIMEOUT_MS);

    for (;;) {
        k_msgq_get(&uart_data_msgq, &uart_msgq, K_FOREVER);
        printk("received uart data itemn");

        switch(uart_msgq.cmd) {
        case UART_CMD_TX:
        memcpy(uart_tx_buf,&uart_msgq.data, sizeof(uint32_t));
        err = uart_tx(uart1, uart_tx_buf, sizeof(uint32_t), SYS_FOREVER_MS);
        break;

        case UART_CMD_DATA_PROCESS:
        break;

        default:
            break;
        }
    }
}

下面是加入线程间通讯的代码后得到的 log,当我们把 TX 和 RX 引脚短接后可以看出 uart thread 不断的发送从 main thread 传输的数据。

wKgZPGilNb6AbhUUAAJR31FvtbQ998.png

总结

本文从实际操作出发,介绍了用户最常用的一些外设如 GPIO, I2C, SPI, UART 的配置和使用方法。并介绍了一些简单 RTOS 组件的应用如 thread, semaphore, message queue。希望能帮助 Nordic 用户加快 nRF Connect SDK 的开发速度。

审核编辑 黄宇

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • NRF
    NRF
    +关注

    关注

    0

    文章

    50

    浏览量

    38608
  • SDK
    SDK
    +关注

    关注

    3

    文章

    1093

    浏览量

    51199
收藏 人收藏
加入交流群
微信小助手二维码

扫码添加小助手

加入工程师交流群

    评论

    相关推荐
    热点推荐

    Nordic 推出nRF Connect for Cloud 的无线物联网设计方案

    Nordic Semiconductor宣布推出nRF Connect for Cloud,用于免费评估、测试和验证基于云并且采用Nordic nRF51和nRF52系列多协议低功耗蓝
    的头像 发表于 06-21 15:05 9686次阅读

    nRF Connect SDK(NCS)/Zephyr固件升级详解 – 重点讲述MCUboot和蓝牙空中升级

    如何在nRF Connect SDK(NCS)中实现蓝牙空中升级?MCUboot和B0两个Bootloader有什么区别?MCUboot升级使用的image格式是怎么样的?什么是SMP协议?CBOR
    的头像 发表于 05-09 14:14 3000次阅读
    <b class='flag-5'>nRF</b> <b class='flag-5'>Connect</b> <b class='flag-5'>SDK</b>(NCS)/Zephyr固件升级详解 – 重点讲述MCUboot和蓝牙空中升级

    Nordic nRF5 SDK和softdevice介绍

    Connect SDK。一般来说,开发nRF51/52产品推荐使用nRF5 SDK,开发nRF
    的头像 发表于 08-20 09:54 2621次阅读
    Nordic <b class='flag-5'>nRF</b>5 <b class='flag-5'>SDK</b>和softdevice介绍

    Nordic nRF Connect SDK 官方开发文档、学习资料下载链接

    /14174427.html nRF Connect SDK详解 https://lgl88911.gitee.io/ 其他博客 https://academy.nordicsemi.com/courses
    发表于 04-23 13:48

    如何调试nRF5 SDK

    本文将讲述Nordic nRF5 SDK的主要调试手段,以帮助大家快速定位问题,并解决问题。一般来说,你可以通过打log方式,IDE的debug模式,SDK自带的app_error_check函数
    发表于 04-26 23:13

    Nordic无线开发---nRF Connect SDK 3.0更新版的安装入门介绍

    系统上git状态错误的问题 nRF Connect SDK,简称NCS,是Nordic最新的SDK平台。该平台支持Nordic的三大产品线: 1.短距离 2.4G MCU: Bluet
    发表于 10-29 20:40

    深度技术解析低功耗蓝牙厂商nordic的nRF Connect SDK裸机选项方案

    自2018年以来,Nordic Semiconductor一直致力于开发基于Zephyr实时操作系统( RTOS)的nRF Connect SDK,并不断发展,提供最佳的工具与软件,助力开发者在
    发表于 10-29 21:17

    使用nRF52840芯片的USB Host 功能参考例程

    Host 示例 Nordic 的 nRF5 SDK(或 nRF Connect SDK,取决于你使用的开发框架)中包含专门的 USB Ho
    发表于 10-31 12:47

    深度技术解析nRF Connect SDK裸机选项方案

    nRF Connect SDK裸机选项是nRF Connect SDK 环境中新增的替代开发路径
    发表于 10-31 23:11

    讲述Nordic nRF5 SDK的主要调试手段,以帮助大家快速定位问题

    nRF5 SDK日志打印功能是通过nRF_Log模块实现的(上面展示的日志都是通过nRF_Log打印出来的),SDK包含的大部分例子都自带打
    的头像 发表于 04-15 15:38 1.4w次阅读
    讲述Nordic <b class='flag-5'>nRF</b>5 <b class='flag-5'>SDK</b>的主要调试手段,以帮助大家快速定位问题

    DFU协议简介 NCS DFU升级步骤说明

    nRF Connect SDK (NCS) / Zephyr 固件升级,主要包括MCUboot和蓝牙空中升级。
    的头像 发表于 05-11 12:51 1.1w次阅读

    基于XIAO nRF52840的钥匙寻找器

    BLE nRF52840 Sense × 1 蜂鸣器 × 1 LED × 1 软件 nRF Connect SDK    Seeed Fusion 核心组件及作用 这款智能钥匙寻
    的头像 发表于 01-17 11:03 966次阅读
    基于XIAO <b class='flag-5'>nRF</b>52840的钥匙寻找器

    nRF5 SDK软件架构及softdevice工作原理

    本文将介绍Nordic nRF5 SDK软件架构以及softdevice工作原理,以加深大家对Nordic产品开发的理解,这样开发过程中碰到问题时,大家也知道如何去调试。 如果你刚开始接触nRF
    的头像 发表于 06-23 11:08 520次阅读
    <b class='flag-5'>nRF</b>5 <b class='flag-5'>SDK</b>软件架构及softdevice工作原理

    如何调试nRF5 SDK

    本文将讲述Nordic nRF5 SDK的主要调试手段,以帮助大家快速定位问题,并解决问题。一般来说,你可以通过打log方式,IDE的debug模式,SDK自带的app_error_check函数
    的头像 发表于 06-24 08:59 523次阅读
    如何调试<b class='flag-5'>nRF</b>5 <b class='flag-5'>SDK</b>

    nRF Connect SDK 使用 nPM2100 评估套件 (PCA10170) 为 nPM2100 电源管理 IC (PMIC) 的开发

    and libraries nRF Connect SDK 提供了多个 PMIC 示例 ,演示了使用 nPM2100 EK 的 nPM2100 的特性和功能。 nRF
    的头像 发表于 07-28 17:48 469次阅读
    <b class='flag-5'>nRF</b> <b class='flag-5'>Connect</b> <b class='flag-5'>SDK</b> 使用 nPM2100 评估套件 (PCA10170) 为 nPM2100 电源管理 IC (PMIC) 的开发