在舉行的第十九屆“開源中國開源世界”大會上,XIAOMI Lela 開源負責人杜超宣布將對外公開超過1000萬行的 Xiaomi Vela 源碼。Xiaomi Vela 是小米基于開源實時操作系統 NuttX 打造的物聯網嵌入式軟件平臺,Vela 在各種物聯網硬件平臺上提供統一的軟件服務,支持豐富的組件和易用的框架,打通碎片化的物聯網應用場景。
NuttX 運行 Rust應用
NuttX上豐富的基礎生態,與Rust的安全特性互相結合,是一個不錯的方案,既能避免C開發引發的潛在內存問題,又能結合Rust的安全和高效,快速完成安全的產品。目前有開源的NuttX的Rust hal(https://github.com/lupyuen/nuttx-embedded-hal),該crate綁定了GPIO、I2C、SPI等常用外設。因此能在Rust代碼中調用NuttX的驅動外設。下面展示一個簡單的Rust代碼。
#![no_std] // Use the Rust Core Library instead of the Rust Standard Library, which is not compatible with embedded systems
extern "C" {
// 導入C的標準puts函數
fn puts(s: *const u8) -> i32;
}
#[no_mangle]
extern "C" fn rust_main() {
// Rust認為任何來自C的接口都是非安全的,因此需要使用 unsafe包裹
unsafe {
// 打印字符到 NuttX 終端
puts(
b"Hello World!\0" // Byte String terminated with null
.as_ptr() // Convert to pointer
);
}
}
在nuttx-embedded-hal
庫中基于NuttX的POSIX接口寫的Rust標準接口也非常方便。如SPI接口的部分實現如下。
/// NuttX SPI Struct
pub struct Spi {
/// NuttX File Descriptor
fd: i32,
}
/// NuttX Implementation of SPI Bus
impl Spi {
/// Create an SPI Bus from a Device Path (e.g. "/dev/spitest0")
pub fn new(path: &str) -> Result {
// Open the NuttX Device Path (e.g. "/dev/spitest0") for read-write
let fd = open(path, O_RDWR);
if fd < 0 { return Err(fd) }
// Return the SPI Bus
Ok(Self { fd })
}
}
/// NuttX Implementation of SPI Bus
impl Drop for Spi {
/// Close the SPI Bus
fn drop(&mut self) {
unsafe { close(self.fd) };
}
}
/// NuttX Implementation of SPI Write
impl spi::Write for Spi{
/// Error Type
type Error = i32;
/// Write SPI data
fn write(&mut self, words: &[u8]) -> Result<(), Self::Error> {
// Transmit data
let bytes_written = unsafe {
write(self.fd, words.as_ptr(), words.len() as u32)
};
assert_eq!(bytes_written, words.len() as i32);
Ok(())
}
}
/// NuttX Implementation of SPI Transfer
impl spi::Transfer for Spi {
/// Error Type
type Error = i32;
/// Transfer SPI data
fn transfer<'w>(&mut self, words: &'w mut [u8]) -> Result<&'w [u8], Self::Error> {
// Transmit data
let bytes_written = unsafe {
write(self.fd, words.as_ptr(), words.len() as u32)
};
assert_eq!(bytes_written, words.len() as i32);
// Read response
let bytes_read = unsafe {
read(self.fd, words.as_mut_ptr(), words.len() as u32)
};
assert_eq!(bytes_read, words.len() as i32);
// Return response
Ok(words)
}
}
使用也非常簡單, 與Rust生的驅動幾乎沒有差別。
// Import SPI Trait
use embedded_hal::blocking::spi;
// Open SPI Bus /dev/spitest0
let mut spi = nuttx_embedded_hal::Spi
::new("/dev/spitest0")
.expect("open spi failed");
// Open GPIO Output /dev/gpio1 for Chip Select
let mut cs = nuttx_embedded_hal::OutputPin
::new("/dev/gpio1")
.expect("open gpio failed");
// Set Chip Select to Low
cs.set_low()
.expect("cs failed");
// Transmit and receive SPI data
let mut data: [ u8; 5 ] = [ 0x1d, 0x00, 0x08, 0x00, 0x00 ];
spi.transfer(&mut data)
.expect("spi failed");
// Show the received SPI data
for i in 0..data.len() {
println!("{:02x}", data[i as usize]);
}
// Set Chip Select to High
cs.set_high()
.expect("cs failed");
對于喜歡Rus語言的嵌入式工程師們來說這是個非常棒的事情,再也不需要重新手搓寄存器來開發驅動,站在巨人的肩膀上開發更有意思的應用。
NuttX是一種輕量級的RTOS,嚴格遵守POSIX標準,廣泛應用與各種控制器。從普通的8位的AVR單片機到arm、mips、risc-v、x86等各種架構的芯片。常見的廠商如ST、NXP、GD、ESP等廠商都有適配的驅動。由于嚴格遵從POSIX標準,因此對于不同的芯片,開發方式都幾乎相同,同時一些Linux應用也非常容易遷移到NuttX。