嵌入式 NuttX OS 上运行Rust

在举行的第十九届“开源中国开源世界”大会上,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。

声明:本内容为作者独立观点,不代表电子星球立场。未经允许不得转载。授权事宜与稿件投诉,请联系:editor@netbroad.com
觉得内容不错的朋友,别忘了一键三连哦!
赞 2
收藏 3
关注 14
成为作者 赚取收益
全部留言
0/200
成为第一个和作者交流的人吧