#gps #lte #async-api #udp-socket #nb-iot #nrf91

nrf-modem

nRF91xx的异步调制解调器API

17个版本

新版本 0.5.0 2024年8月9日
0.4.3 2024年6月18日
0.4.1 2023年9月22日
0.3.2 2023年4月24日
0.1.1 2022年12月26日

#79 in 嵌入式开发

Download history 6/week @ 2024-05-20 313/week @ 2024-06-17 3/week @ 2024-06-24 85/week @ 2024-07-01 67/week @ 2024-07-22 16/week @ 2024-07-29 90/week @ 2024-08-05

173 每月下载量

MIT/Apache

160KB
3.5K SLoC

nRF-Modem

crates.io Documentation

这是一个库,为Nordic nRF91*系列芯片(系统封装)上的调制解调器提供高级异步API。支持的芯片包括以下

  • nRF9160
  • nRF9151
  • nRF9161

它可以与任何执行器一起使用。

使用

在您的程序或库中,您可以像通常那样依赖此包

[dependencies]
nrf-modem = "0.5.0"

nrf9160功能默认启用,以支持旧版本。要在其他支持的芯片(例如nrf9151)上使用此库,请选择该功能并禁用默认功能。

[dependencies]
nrf-modem = { version = "0.5.0", default-featues = false, features = ["nrf9151"] }

错误和恢复

释放LteLink和Gnss(这还包括所有套接字和GnssStream)可能会导致调制解调器保持活动状态。有一个内部互斥锁可以锁定。恐慌是对此的唯一合理反应。如果您有更好的想法,请提出问题或PR!异步deactivate函数出错的可能性要小得多,您将获得一个Result,这样您就知道出了什么问题。

如果发生任何问题,has_runtime_state_error()将返回true。一切都应该正常工作,但调制解调器可能没有正确关闭。这可以通过在确保不再使用调制解调器的任何部分时调用reset_runtime_state()函数来恢复。

设置

您必须做一些事情才能使用此库。

首先,确保已安装llvm-tools。这可以通过使用rustup component add llvm-tools-preview来完成。

库还需要一些libc函数。最佳导入方式是使用tinyrlibc。截至编写时,最新版本是0.3.0。此版本不包括所需的API,因此最好包含最新的master分支或任何更新的发布版本。

该库已与调制解调器固件版本 1.3.4 进行过测试,但可能与更早版本兼容。当该库开始要求更高版本时,这将被视为破坏性更改。但可能会遗漏某些内容,因此这仅提供“尽力而为”的保证。

非安全

Nordic 已经使调制解调器只能在非安全上下文中使用。请确保您处于该上下文,例如使用 SPM 或 TF-M。

中断

中断 EGU1IPC 必须路由到调制解调器软件。

// Interrupt Handler for LTE related hardware. Defer straight to the library.
#[interrupt]
#[allow(non_snake_case)]
fn IPC() {
    nrf_modem::ipc_irq_handler();
}

let mut cp = unwrap!(cortex_m::Peripherals::take());

// Enable the modem interrupts
unsafe {
    NVIC::unmask(pac::Interrupt::IPC);
    cp.NVIC.set_priority(pac::Interrupt::IPC, 0 << 5);
}

电源

当库初始化时,DC/DC 转换器会自动为您启用。这是调制解调器认证操作所必需的。

初始化

现在是对库进行初始化的时候了。在这里,您可以为主调解调器的连接性进行选择

nrf_modem::init(SystemMode {
    lte_support: true,
    lte_psm_support: true,
    nbiot_support: true,
    gnss_support: true,
    preference: ConnectionPreference::None,
})
.await
.unwrap();

现在库已准备好使用。

AT 命令

let response = nrf_modem::send_at::<64>("AT+CGMI").await.unwrap();
assert_eq!(response, "AT+CGMI\n\rNordic Semiconductor ASA\n\rOK\n\r");

DNS 请求

let google_ip = nrf_modem::get_host_by_name("www.google.com").await.unwrap();

Tcp 连接

let stream = nrf_modem::TcpStream::connect(SocketAddr::from((google_ip, 80))).await.unwrap();

stream
    .write("GET / HTTP/1.0\nHost: google.com\r\n\r\n".as_bytes())
    .await
    .unwrap();

let mut buffer = [0; 1024];
let received = stream.receive(&mut buffer).await.unwrap();

println!("Google response: {}", core::str::from_utf8(received).unwrap());

// Drop the stream async (normal Drop is ok too, but that's blocking)
stream.deactivate().await.unwrap();

Udp 套接字

let socket =
    nrf_modem::UdpSocket::bind(SocketAddr::from_str("0.0.0.0:53").unwrap())
        .await
        .unwrap();

// Do a DNS request
socket
    .send_to(
        &[
            0xdb, 0x42, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x77,
            0x77, 0x77, 0x0C, 0x6E, 0x6F, 0x72, 0x74, 0x68, 0x65, 0x61, 0x73, 0x74, 0x65, 0x72,
            0x6E, 0x03, 0x65, 0x64, 0x75, 0x00, 0x00, 0x01, 0x00, 0x01,
        ],
        SocketAddr::from_str("8.8.8.8:53").unwrap(),
    )
    .await
    .unwrap();
let (response, source_addr) = socket.receive_from(&mut buffer).await.unwrap();

println!("Result: {:X}", response);
println!("Source: {}", source_addr);

依赖项

~5–10MB
~177K SLoC