1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516
//! Rust Embedded HAL for Apache NuttX RTOS
use core::{
str::FromStr,
};
use embedded_hal::{
blocking::{
delay::{DelayMs, DelayUs},
i2c,
spi,
},
digital::v2,
};
use crate::{
close, ioctl, read, usleep, write,
i2c_msg_s, i2c_transfer_s, size_t, ssize_t,
GPIOC_WRITE, I2CIOC_TRANSFER, I2C_M_READ, O_RDWR,
String,
};
/// NuttX Implementation of I2C Read
impl i2c::Read for I2c {
/// Error Type
type Error = i32;
/// TODO: Read `buf` from I2C Port
fn read(&mut self, _addr: u8, _buf: &mut [u8]) -> Result<(), Self::Error> {
// Not implemented, because BL602 needs the I2C Sub Address for read to work
panic!();
}
}
/// NuttX Implementation of I2C Write
impl i2c::Write for I2c {
/// Error Type
type Error = i32;
/// Write `buf` to I2C Port.
/// We assume this is a Write I2C Register operation, with Register ID at `buf[0]`.
/// TODO: Handle other kinds of I2C operations
fn write(&mut self, addr: u8, buf: &[u8]) -> Result<(), Self::Error> {
// Copy to local buffer because we need a mutable reference
let mut buf2 = [0 ; 64];
assert!(buf.len() <= buf2.len());
buf2[..buf.len()].copy_from_slice(buf);
// Buffer for received I2C data
let mut rbuf = [0 ; 1];
// Compose I2C Transfer
let msg = [
// First I2C Message: Send Register ID and I2C Data as I2C Sub Address
i2c_msg_s {
frequency: self.frequency, // I2C Frequency
addr: addr as u16, // I2C Address
buffer: buf2.as_mut_ptr(), // Buffer to be sent
length: buf.len() as ssize_t, // Number of bytes to send
// For BL602: Register ID must be passed as I2C Sub Address
#[cfg(target_arch = "riscv32")] // If architecture is RISC-V 32-bit...
flags: crate::I2C_M_NOSTOP, // I2C Flags: Send I2C Sub Address
// Otherwise pass Register ID as I2C Data
#[cfg(not(target_arch = "riscv32"))] // If architecture is not RISC-V 32-bit...
flags: 0, // I2C Flags: None
// TODO: Check for BL602 specifically (by target_abi?), not just RISC-V 32-bit
},
// Second I2C Message: Read I2C Data, because this forces BL602 to send the first message correctly
i2c_msg_s {
frequency: self.frequency, // I2C Frequency
addr: addr as u16, // I2C Address
buffer: rbuf.as_mut_ptr(), // Buffer to be received
length: rbuf.len() as ssize_t, // Number of bytes to receive
flags: I2C_M_READ, // I2C Flags: Read I2C Data
},
];
// Compose ioctl Argument to write I2C Registers
let xfer = i2c_transfer_s {
msgv: msg.as_ptr(), // Array of I2C messages for the transfer
msgc: msg.len() as size_t, // Number of messages in the array
};
// Execute I2C Transfer to write I2C Registers
let ret = unsafe {
ioctl(
self.fd, // I2C Port
I2CIOC_TRANSFER, // I2C Transfer
&xfer // I2C Messages for the transfer
)
};
assert!(ret >= 0);
Ok(())
}
}
/// NuttX Implementation of I2C WriteRead
impl i2c::WriteRead for I2c {
/// Error Type
type Error = i32;
/// Write `wbuf` to I2C Port and read `rbuf` from I2C Port.
/// We assume this is a Read I2C Register operation, with Register ID at `wbuf[0]`.
/// TODO: Handle other kinds of I2C operations
fn write_read(&mut self, addr: u8, wbuf: &[u8], rbuf: &mut [u8]) -> Result<(), Self::Error> {
// We assume this is a Read I2C Register operation, with Register ID at wbuf[0]
assert_eq!(wbuf.len(), 1);
let reg_id = wbuf[0];
// Read I2C Registers, starting at Register ID
let mut start = [reg_id ; 1];
// Compose I2C Transfer
let msg = [
// First I2C Message: Send Register ID
i2c_msg_s {
frequency: self.frequency, // I2C Frequency
addr: addr as u16, // I2C Address
buffer: start.as_mut_ptr(), // Buffer to be sent
length: start.len() as ssize_t, // Number of bytes to send
// For BL602: Register ID must be passed as I2C Sub Address
#[cfg(target_arch = "riscv32")] // If architecture is RISC-V 32-bit...
flags: crate::I2C_M_NOSTOP, // I2C Flags: Send I2C Sub Address
// Otherwise pass Register ID as I2C Data
#[cfg(not(target_arch = "riscv32"))] // If architecture is not RISC-V 32-bit...
flags: 0, // I2C Flags: None
// TODO: Check for BL602 specifically (by target_abi?), not just RISC-V 32-bit
},
// Second I2C Message: Receive Register Values
i2c_msg_s {
frequency: self.frequency, // I2C Frequency
addr: addr as u16, // I2C Address
buffer: rbuf.as_mut_ptr(), // Buffer to be received
length: rbuf.len() as ssize_t, // Number of bytes to receive
flags: I2C_M_READ, // I2C Flags: Read I2C Data
},
];
// Compose ioctl Argument
let xfer = i2c_transfer_s {
msgv: msg.as_ptr(), // Array of I2C messages for the transfer
msgc: msg.len() as size_t, // Number of messages in the array
};
// Execute I2C Transfer
let ret = unsafe {
ioctl(
self.fd,
I2CIOC_TRANSFER,
&xfer
)
};
assert!(ret >= 0);
Ok(())
}
}
/// NuttX Implementation of SPI Transfer
impl spi::Transfer<u8> 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)
}
}
/// NuttX Implementation of SPI Write
impl spi::Write<u8> 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 GPIO Output
impl v2::OutputPin for OutputPin {
/// Error Type
type Error = i32;
/// Set the GPIO Output to High
fn set_high(&mut self) -> Result<(), Self::Error> {
let ret = unsafe {
ioctl(self.fd, GPIOC_WRITE, 1)
};
assert!(ret >= 0);
Ok(())
}
/// Set the GPIO Output to low
fn set_low(&mut self) -> Result<(), Self::Error> {
let ret = unsafe {
ioctl(self.fd, GPIOC_WRITE, 0)
};
assert!(ret >= 0);
Ok(())
}
}
/// NuttX Implementation of GPIO Input
impl v2::InputPin for InputPin {
/// Error Type
type Error = i32;
/// Return true if GPIO Input is high
fn is_high(&self) -> Result<bool, Self::Error> {
let mut invalue: i32 = 0;
let addr: *mut i32 = &mut invalue;
let ret = unsafe {
ioctl(self.fd, crate::GPIOC_READ, addr)
};
assert!(ret >= 0);
match invalue {
0 => Ok(false),
_ => Ok(true),
}
}
/// Return true if GPIO Input is low
fn is_low(&self) -> Result<bool, Self::Error> {
Ok(!self.is_high()?)
}
}
/// NuttX Implementation of GPIO Interrupt
impl v2::InputPin for InterruptPin {
/// Error Type
type Error = i32;
/// Return true if GPIO Input is high
fn is_high(&self) -> Result<bool, Self::Error> {
let mut invalue: i32 = 0;
let addr: *mut i32 = &mut invalue;
let ret = unsafe {
ioctl(self.fd, crate::GPIOC_READ, addr)
};
assert!(ret >= 0);
match invalue {
0 => Ok(false),
_ => Ok(true),
}
}
/// Return true if GPIO Input is low
fn is_low(&self) -> Result<bool, Self::Error> {
Ok(!self.is_high()?)
}
}
/// NuttX Implementation of GPIO Unused
impl v2::OutputPin for UnusedPin {
/// Error Type
type Error = i32;
/// Set the pin to high
fn set_high(&mut self) -> Result<(), Self::Error> {
Ok(())
}
/// Set the pin to low
fn set_low(&mut self) -> Result<(), Self::Error> {
Ok(())
}
}
/// NuttX Implementation of Delay in Microseconds
impl DelayUs<u8> for Delay {
/// Sleep for us microseconds
fn delay_us(&mut self, us: u8) {
unsafe { usleep(us as u32); }
}
}
/// NuttX Implementation of Delay in Microseconds
impl DelayUs<u16> for Delay {
/// Sleep for us microseconds
fn delay_us(&mut self, us: u16) {
unsafe { usleep(us as u32); }
}
}
/// NuttX Implementation of Delay in Microseconds
impl DelayUs<u32> for Delay {
/// Sleep for us microseconds
fn delay_us(&mut self, us: u32) {
unsafe { usleep(us); }
}
}
/// NuttX Implementation of Delay in Milliseconds
impl DelayMs<u8> for Delay {
/// Sleep for ms milliseconds
fn delay_ms(&mut self, ms: u8) {
unsafe { usleep(ms as u32 * 1000); }
}
}
/// NuttX Implementation of Delay in Milliseconds
impl DelayMs<u16> for Delay {
/// Sleep for ms milliseconds
fn delay_ms(&mut self, ms: u16) {
unsafe { usleep(ms as u32 * 1000); }
}
}
/// NuttX Implementation of Delay in Milliseconds
impl DelayMs<u32> for Delay {
/// Sleep for ms milliseconds
fn delay_ms(&mut self, ms: u32) {
unsafe { usleep(ms * 1000); }
}
}
/// NuttX Implementation of I2C Bus
impl I2c {
/// Create an I2C Bus from a Device Path (e.g. "/dev/i2c0")
pub fn new(path: &str, frequency: u32) -> Result<Self, i32> {
// Open the NuttX Device Path (e.g. "/dev/i2c0") for read-write
let fd = open(path, O_RDWR);
if fd < 0 { return Err(fd) }
// Return the I2C Bus
Ok(Self { fd, frequency })
}
}
/// 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<Self, i32> {
// 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 GPIO Input
impl InputPin {
/// Create a GPIO Input Pin from a Device Path (e.g. "/dev/gpio0")
pub fn new(path: &str) -> Result<Self, i32> {
// Open the NuttX Device Path (e.g. "/dev/gpio0") for read-write
let fd = open(path, O_RDWR);
if fd < 0 { return Err(fd) }
// Return the pin
Ok(Self { fd })
}
}
/// NuttX Implementation of GPIO Output
impl OutputPin {
/// Create a GPIO Output Pin from a Device Path (e.g. "/dev/gpio1")
pub fn new(path: &str) -> Result<Self, i32> {
// Open the NuttX Device Path (e.g. "/dev/gpio1") for read-write
let fd = open(path, O_RDWR);
if fd < 0 { return Err(fd) }
// Return the pin
Ok(Self { fd })
}
}
/// NuttX Implementation of GPIO Interrupt
impl InterruptPin {
/// Create a GPIO Interrupt Pin from a Device Path (e.g. "/dev/gpio2")
pub fn new(path: &str) -> Result<Self, i32> {
// Open the NuttX Device Path (e.g. "/dev/gpio2") for read-write
let fd = open(path, O_RDWR);
if fd < 0 { return Err(fd) }
// Return the pin
Ok(Self { fd })
}
}
/// NuttX Implementation of GPIO Unused
impl UnusedPin {
/// Create a GPIO Unused Pin
pub fn new() -> Result<Self, i32> {
// Return the pin
Ok(Self {})
}
}
/// NuttX Implementation of I2C Bus
impl Drop for I2c {
/// Close the I2C Bus
fn drop(&mut self) {
unsafe { close(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 GPIO Input
impl Drop for InputPin {
/// Close the GPIO Input
fn drop(&mut self) {
unsafe { close(self.fd) };
}
}
/// NuttX Implementation of GPIO Output
impl Drop for OutputPin {
/// Close the GPIO Output
fn drop(&mut self) {
unsafe { close(self.fd) };
}
}
/// NuttX Implementation of GPIO Interrupt
impl Drop for InterruptPin {
/// Close the GPIO Interrupt
fn drop(&mut self) {
unsafe { close(self.fd) };
}
}
/// NuttX I2C Bus
pub struct I2c {
/// NuttX File Descriptor
fd: i32,
/// I2C Frequency in Hz
frequency: u32,
}
/// NuttX SPI Bus
pub struct Spi {
/// NuttX File Descriptor
fd: i32,
}
/// NuttX GPIO Input
pub struct InputPin {
/// NuttX File Descriptor
fd: i32,
}
/// NuttX GPIO Output
pub struct OutputPin {
/// NuttX File Descriptor
fd: i32,
}
/// NuttX GPIO Interrupt
pub struct InterruptPin {
/// NuttX File Descriptor
fd: i32,
}
/// NuttX GPIO Unused
pub struct UnusedPin {
}
/// NuttX Delay
pub struct Delay;
/// Open a file and return the file descriptor.
/// TODO: Auto-generate this wrapper with `bindgen` from the C declaration
fn open(path: &str, oflag: i32) -> i32 { // `&str` is a reference to a string slice, similar to `const char *` in C
use crate::open;
// Convert `str` to `String`, which similar to `char [64]` in C
let mut s_with_null = String::from_str(path) // `mut` because we will modify it
.expect("open conversion failed"); // If it exceeds 64 chars, halt with an error
// Terminate the string with null, since we will be passing to C
s_with_null.push('\0')
.expect("open overflow"); // If we exceed 64 chars, halt with an error
// Convert the null-terminated string to a pointer
let p = s_with_null.as_str().as_ptr();
// Call the C function
unsafe { // Flag this code as unsafe because we're calling a C function
open(p, oflag)
}
// No semicolon `;` here, so the value returned by the C function will be passed to our caller
}