【Nordic博文分享系列】nRFConnectSDKBasic

Nordic半导体 2025-01-15 17:00




转载自:博客园 |作者:zhengyi_hot

用户在使用 nRF connect SDK(NCS) 的时候经常会操作的外设有GPIO,I2C,SPI,UART。

我们就以 NCS 2.7.0 中的例程代码 nrf\samples\bluetooth\peripheral_lbs 为基础,来演示上述外设的简单使用。

使用的硬件是开发板 nRF52840 DK





具体操作

1

准备工作

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

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




2

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



3

I2C 设备控制

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

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

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

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

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

&i2c1 {    status = "ok";    clock-frequency = ;    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>;    };      };     

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

&pinctrl {    i2c1_default: i2c1_default {        group1 {            psels = 0, 4)>,                0, 3)>;        };    };     i2c1_sleep: i2c1_sleep {        group1 {            psels = 0, 4)>,                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 。



4

SPI 设备控制

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

  • 首先修改 devicetree。

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

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

3. devicetree node spi2 下定义了两个 sub-node 分别是 user_spi_adc 和 user_spi_flash。 sub-node 里定义了三个 propertise。propertise compatible 的取值来自于我们在工程里新添加的文件 dts\bindings\spi-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 = 8)>;    };    user_spi_flash: user_spi_flash@0 {        compatible = "spi-user-define";        reg = <1>;        spi-max-frequency = 8)>;    };       };      

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

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:

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

spi2_default: spi2_default {    group1 {        psels = 0, 28)>,                0, 29)>,                0, 30)>;    };};spi2_sleep: spi2_sleep {    group1 {        psels = 0, 28)>,                0, 29)>,                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_SINGLEstatic 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 引脚。



5

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 = 1, 1)>;        bias-pull-up;    };    group2 {        psels = 1, 2)>;    };}; uart1_sleep: uart1_sleep {    group1 {        psels = 1, 1)>,            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 %d\n", err); err = uart_rx_enable(uart1, uart_rx_buf, MAX_UART_BUF_LEN, UART_RX_TIMEOUT_MS);//printk("uart_rx_enable return %d\n", err); err = uart_tx(uart1, uart_tx_buf, 6, SYS_FOREVER_MS);//printk("uart_tx return %d\n", 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_DONE\n");        break;     case UART_RX_RDY:        printk("UART_RX_RDY\n");        printk("received %d bytes\n", evt->data.rx.len);        break;     case UART_RX_DISABLED:        printk("UART_RX_DISABLED\n");        break;     case UART_RX_BUF_REQUEST:        printk("UART_RX_BUF_REQUEST\n");        uart_rx_buf_rsp(uart1, uart_rx_buf2, MAX_UART_BUF_LEN);        break;     case UART_RX_BUF_RELEASED:        printk("UART_RX_BUF_RELEASED\n");        break;     case UART_TX_ABORTED:        printk("UART_TX_ABORTED\n");        break;     default:        break;    }}
  • 我们在 DK 上把 TXD 引脚和 RXD 引脚短接来测试 UART 的收发,可以看到如下的 log 信息。UART 收到了自己发送的6字节的数据。 



6

UART 应用代码的优化

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

  1. Thread 线程

  2. Semaphore 信号量

  3. 线程间通讯 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_task\n");     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 item\n");                 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 函数。通过查询文件 build\zephyr\.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 被打印出来的。 


从应用的角度,我们希望 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_task\n");     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。

下面的代码中 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_task\n");     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 item\n");                 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 传输的数据。   






总结

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


【联系我们】

中文官网:www.nordicsemi.cn

英文官网:www.nordicsemi.com

微信公众号:nordicsemi


【Nordic 开发者论坛】

https://devzone.nordicsemi.com


【销售接洽】

北京分公司: +86 010 8438 2767

上海分公司: +86 21 6330 0620

深圳分公司: +86 755 8322 0147

sales.cn@nordicsemi.no

 点击“阅读原文” 探索更多Nordic资讯

Nordic半导体 Nordic 半导体开发支持蓝牙智能、ANT+和2.4GHz应用的超低功耗短距无线通信技术,用于物联网 、可穿戴产品、智能家居、玩具等应用。Nordic 提供现成可用的设计框架、世界级文档资料和支持,以加快专业工程师和业余爱好者的开发速度。
评论
  • 新年伊始,又到了对去年做总结,对今年做展望的时刻 不知道你在2024年初立的Flag都实现了吗? 2025年对自己又有什么新的期待呢? 2024年注定是不平凡的一年, 一年里我测评了50余块开发板, 写出了很多科普文章, 从一个小小的工作室成长为科工公司。 展望2025年, 中国香河英茂科工, 会继续深耕于,具身机器人、飞行器、物联网等方面的研发, 我觉得,要向未来学习未来, 未来是什么? 是掌握在孩子们生活中的发现,和精历, 把最好的技术带给孩子,
    丙丁先生 2025-01-11 11:35 454浏览
  • 01. 什么是过程能力分析?过程能力研究利用生产过程中初始一批产品的数据,预测制造过程是否能够稳定地生产符合规格的产品。可以把它想象成一种预测。通过历史数据的分析,推断未来是否可以依赖该工艺持续生产高质量产品。客户可能会要求将过程能力研究作为生产件批准程序 (PPAP) 的一部分。这是为了确保制造过程能够持续稳定地生产合格的产品。02. 基本概念在定义制造过程时,目标是确保生产的零件符合上下规格限 (USL 和 LSL)。过程能力衡量制造过程能多大程度上稳定地生产符合规格的产品。核心概念很简单:
    优思学院 2025-01-12 15:43 516浏览
  •   在信号处理过程中,由于信号的时域截断会导致频谱扩展泄露现象。那么导致频谱泄露发生的根本原因是什么?又该采取什么样的改善方法。本文以ADC性能指标的测试场景为例,探讨了对ADC的输出结果进行非周期截断所带来的影响及问题总结。 两个点   为了更好的分析或处理信号,实际应用时需要从频域而非时域的角度观察原信号。但物理意义上只能直接获取信号的时域信息,为了得到信号的频域信息需要利用傅里叶变换这个工具计算出原信号的频谱函数。但对于计算机来说实现这种计算需要面对两个问题: 1.
    TIAN301 2025-01-14 14:15 107浏览
  • 随着数字化的不断推进,LED显示屏行业对4K、8K等超高清画质的需求日益提升。与此同时,Mini及Micro LED技术的日益成熟,推动了间距小于1.2 Pitch的Mini、Micro LED显示屏的快速发展。这类显示屏不仅画质卓越,而且尺寸适中,通常在110至1000英寸之间,非常适合应用于电影院、监控中心、大型会议、以及电影拍摄等多种室内场景。鉴于室内LED显示屏与用户距离较近,因此对于噪音控制、体积小型化、冗余备份能力及电气安全性的要求尤为严格。为满足这一市场需求,开关电源技术推出了专为
    晶台光耦 2025-01-13 10:42 495浏览
  • 食物浪费已成为全球亟待解决的严峻挑战,并对环境和经济造成了重大影响。最新统计数据显示,全球高达三分之一的粮食在生产过程中损失或被无谓浪费,这不仅导致了资源消耗,还加剧了温室气体排放,并带来了巨大经济损失。全球领先的光学解决方案供应商艾迈斯欧司朗(SIX:AMS)近日宣布,艾迈斯欧司朗基于AS7341多光谱传感器开发的创新应用来解决食物浪费这一全球性难题。其多光谱传感解决方案为农业与食品行业带来深远变革,该技术通过精确判定最佳收获时机,提升质量控制水平,并在整个供应链中有效减少浪费。 在2024
    艾迈斯欧司朗 2025-01-14 18:45 50浏览
  • 随着全球向绿色能源转型的加速,对高效、可靠和环保元件的需求从未如此强烈。在这种背景下,国产固态继电器(SSR)在实现太阳能逆变器、风力涡轮机和储能系统等关键技术方面发挥着关键作用。本文探讨了绿色能源系统背景下中国固态继电器行业的前景,并强调了2025年的前景。 1.对绿色能源解决方案日益增长的需求绿色能源系统依靠先进的电源管理技术来最大限度地提高效率并最大限度地减少损失。固态继电器以其耐用性、快速开关速度和抗机械磨损而闻名,正日益成为传统机电继电器的首选。可再生能源(尤其是太阳能和风能
    克里雅半导体科技 2025-01-10 16:18 322浏览
  • 在不断发展的电子元件领域,继电器——作为切换电路的关键设备,正在经历前所未有的技术变革。固态继电器(SSR)和机械继电器之间的争论由来已久。然而,从未来发展的角度来看,固态继电器正逐渐占据上风。本文将从耐用性、速度和能效三个方面,全面剖析固态继电器为何更具优势,并探讨其在行业中的应用与发展趋势。1. 耐用性:经久耐用的设计机械继电器:机械继电器依靠物理触点完成电路切换。然而,随着时间的推移,这些触点因电弧、氧化和材料老化而逐渐磨损,导致其使用寿命有限。因此,它们更适合低频或对切换耐久性要求不高的
    腾恩科技-彭工 2025-01-10 16:15 97浏览
  • 流量传感器是实现对燃气、废气、生活用水、污水、冷却液、石油等各种流体流量精准计量的关键手段。但随着工业自动化、数字化、智能化与低碳化进程的不断加速,采用传统机械式检测方式的流量传感器已不能满足当代流体计量行业对于测量精度、测量范围、使用寿命与维护成本等方面的精细需求。流量传感器的应用场景(部分)超声波流量传感器,是一种利用超声波技术测量流体流量的新型传感器,其主要通过发射超声波信号并接收反射回来的信号,根据超声波在流体中传播的时间、幅度或相位变化等参数,间接计算流体的流量,具有非侵入式测量、高精
    华普微HOPERF 2025-01-13 14:18 477浏览
  • PNT、GNSS、GPS均是卫星定位和导航相关领域中的常见缩写词,他们经常会被用到,且在很多情况下会被等同使用或替换使用。我们会把定位导航功能测试叫做PNT性能测试,也会叫做GNSS性能测试。我们会把定位导航终端叫做GNSS模块,也会叫做GPS模块。但是实际上他们之间是有一些重要的区别。伴随着技术发展与越发深入,我们有必要对这三个词汇做以清晰的区分。一、什么是GPS?GPS是Global Positioning System(全球定位系统)的缩写,它是美国建立的全球卫星定位导航系统,是GNSS概
    德思特测试测量 2025-01-13 15:42 487浏览
  • 根据Global Info Research(环洋市场咨询)项目团队最新调研,预计2030年全球无人机电池和电源产值达到2834百万美元,2024-2030年期间年复合增长率CAGR为10.1%。 无人机电池是为无人机提供动力并使其飞行的关键。无人机使用的电池类型因无人机的大小和型号而异。一些常见的无人机电池类型包括锂聚合物(LiPo)电池、锂离子电池和镍氢(NiMH)电池。锂聚合物电池是最常用的无人机电池类型,因为其能量密度高、设计轻巧。这些电池以输出功率大、飞行时间长而著称。不过,它们需要
    GIRtina 2025-01-13 10:49 180浏览
  • ARMv8-A是ARM公司为满足新需求而重新设计的一个架构,是近20年来ARM架构变动最大的一次。以下是对ARMv8-A的详细介绍: 1. 背景介绍    ARM公司最初并未涉足PC市场,其产品主要针对功耗敏感的移动设备。     随着技术的发展和市场需求的变化,ARM开始扩展到企业设备、服务器等领域,这要求其架构能够支持更大的内存和更复杂的计算任务。 2. 架构特点    ARMv8-A引入了Execution State(执行状
    丙丁先生 2025-01-12 10:30 465浏览
  • 随着通信技术的迅速发展,现代通信设备需要更高效、可靠且紧凑的解决方案来应对日益复杂的系统。中国自主研发和制造的国产接口芯片,正逐渐成为通信设备(从5G基站到工业通信模块)中的重要基石。这些芯片凭借卓越性能、成本效益及灵活性,满足了现代通信基础设施的多样化需求。 1. 接口芯片在通信设备中的关键作用接口芯片作为数据交互的桥梁,是通信设备中不可或缺的核心组件。它们在设备内的各种子系统之间实现无缝数据传输,支持高速数据交换、协议转换和信号调节等功能。无论是5G基站中的数据处理,还是物联网网关
    克里雅半导体科技 2025-01-10 16:20 442浏览
  • 数字隔离芯片是现代电气工程师在进行电路设计时所必须考虑的一种电子元件,主要用于保护低压控制电路中敏感电子设备的稳定运行与操作人员的人身安全。其不仅能隔离两个或多个高低压回路之间的电气联系,还能防止漏电流、共模噪声与浪涌等干扰信号的传播,有效增强电路间信号传输的抗干扰能力,同时提升电子系统的电磁兼容性与通信稳定性。容耦隔离芯片的典型应用原理图值得一提的是,在电子电路中引入隔离措施会带来传输延迟、功耗增加、成本增加与尺寸增加等问题,而数字隔离芯片的目标就是尽可能消除这些不利影响,同时满足安全法规的要
    华普微HOPERF 2025-01-15 09:48 61浏览
我要评论
0
点击右上角,分享到朋友圈 我知道啦
请使用浏览器分享功能 我知道啦