-
-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathlib.rs
More file actions
277 lines (227 loc) · 9.1 KB
/
lib.rs
File metadata and controls
277 lines (227 loc) · 9.1 KB
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
#![no_std] // Use the Rust Core Library instead of the Rust Standard Library, which is not compatible with embedded systems
// Import Module sx1262
mod sx1262;
// Import Libraries
use core::{ // Rust Core Library
fmt, // String Formatting
panic::PanicInfo, // Panic Handler
str::FromStr, // For converting `str` to `String`
};
use embedded_hal::{ // Rust Embedded HAL
digital::v2::OutputPin, // GPIO Output
blocking::{ // Blocking I/O
delay::DelayMs, // Delay Interface
spi::Transfer, // SPI Transfer
},
};
use nuttx_embedded_hal::{ // NuttX Embedded HAL
println,
};
#[no_mangle] // Don't mangle the function name
extern "C" fn rust_main() { // Declare `extern "C"` because it will be called by NuttX
// Print a message to the serial console
println!("Hello from Rust!");
// Print a message the unsafe way
test_puts();
// Test the SPI Port by reading SX1262 Register 8
test_spi();
// Test the NuttX Embedded HAL by reading SX1262 Register 8
test_hal();
// Test the SX1262 Driver by reading a register and sending a LoRa message
sx1262::test_sx1262();
}
/// Print a message the unsafe way
fn test_puts() {
extern "C" { // Import C Function
/// Print a message to the serial console (from C stdio library)
fn puts(s: *const u8) -> i32;
}
unsafe { // Mark as unsafe because we are calling C
// Print a message to the serial console
puts(
b"Hello World!\0" // Byte String terminated with null
.as_ptr() // Convert to pointer
);
}
}
/// Test the SPI Port by reading SX1262 Register 8
fn test_spi() {
println!("test_spi");
// Open GPIO Input for SX1262 Busy Pin
let busy = unsafe {
open(b"/dev/gpio0\0".as_ptr(), O_RDWR)
};
assert!(busy > 0);
// Open GPIO Output for SX1262 Chip Select
let cs = unsafe {
open(b"/dev/gpio1\0".as_ptr(), O_RDWR)
};
assert!(cs > 0);
// Open GPIO Interrupt for SX1262 DIO1 Pin
let dio1 = unsafe {
open(b"/dev/gpio2\0".as_ptr(), O_RDWR)
};
assert!(dio1 > 0);
// Open SPI Bus for SX1262
let spi = unsafe {
open(b"/dev/spitest0\0".as_ptr(), O_RDWR)
};
assert!(spi >= 0);
// Read SX1262 Register twice
for _i in 0..2 {
// Set SX1262 Chip Select to Low
let ret = unsafe {
ioctl(cs, GPIOC_WRITE, 0)
};
assert!(ret >= 0);
// Transmit command to SX1262: Read Register 8
const READ_REG: &[u8] = &[ 0x1d, 0x00, 0x08, 0x00, 0x00 ];
let bytes_written = unsafe {
write(spi, READ_REG.as_ptr(), READ_REG.len() as u32)
};
assert!(bytes_written == READ_REG.len() as i32);
// Read response from SX1262
let mut rx_data: [ u8; 16 ] = [ 0; 16 ];
let bytes_read = unsafe {
read(spi, rx_data.as_mut_ptr(), rx_data.len() as u32)
};
assert!(bytes_read == READ_REG.len() as i32);
// Set SX1262 Chip Select to High
let ret = unsafe {
ioctl(cs, GPIOC_WRITE, 1)
};
assert!(ret >= 0);
// Show the received register value
println!("test_spi: received");
for i in 0..bytes_read {
println!(" {:02x}", rx_data[i as usize])
}
println!("test_spi: SX1262 Register 8 is 0x{:02x}", rx_data[4]);
// Sleep 5 seconds
unsafe { sleep(5); }
}
// Close the GPIO and SPI ports
unsafe {
close(busy);
close(cs);
close(dio1);
close(spi);
}
}
/// Test the NuttX Embedded HAL by reading SX1262 Register 8
fn test_hal() {
println!("test_hal");
// Open GPIO Output for SX1262 Chip Select
let mut cs = nuttx_embedded_hal::OutputPin
::new("/dev/gpio1")
.expect("open gpio failed");
// Open SPI Bus for SX1262
let mut spi = nuttx_embedded_hal::Spi
::new("/dev/spitest0")
.expect("open spi failed");
// Get a Delay Interface
let mut delay = nuttx_embedded_hal::Delay;
// Set SX1262 Chip Select to Low
cs.set_low()
.expect("cs failed");
// Transfer command to SX1262: Read Register 8
let mut data: [ u8; 5 ] = [ 0x1d, 0x00, 0x08, 0x00, 0x00 ];
spi.transfer(&mut data)
.expect("spi failed");
// Show the received register value
println!("test_hal: received");
for i in 0..data.len() {
println!(" {:02x}", data[i as usize]);
}
println!("test_hal: SX1262 Register 8 is 0x{:02x}", data[4]);
// Set SX1262 Chip Select to High
cs.set_high()
.expect("cs failed");
// Wait 5 seconds
delay.delay_ms(5000_u32);
}
/// Print a message to the serial console.
/// TODO: Auto-generate this wrapper with `bindgen` from the C declaration
pub fn puts(s: &str) -> i32 { // `&str` is a reference to a string slice, similar to `const char *` in C
extern "C" { // Import C Function
/// Print a message to the serial console (from C stdio library)
fn puts(s: *const u8) -> i32;
}
// Convert `str` to `String`, which similar to `char [64]` in C
// TODO: Increase the buffer size if we're sure we won't overflow the stack
let mut s_with_null = String::from_str(s) // `mut` because we will modify it
.expect("puts 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("puts 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
puts(p)
}
// No semicolon `;` here, so the value returned by the C function will be passed to our caller
}
/// Print a formatted message to the serial console. Called by println! macro.
pub fn puts_format(args: fmt::Arguments<'_>) {
// Allocate a 64-byte buffer.
// TODO: Increase the buffer size if we're sure we won't overflow the stack
let mut buf = String::new();
// Format the message into the buffer
fmt::write(&mut buf, args)
.expect("puts_format overflow");
// Print the buffer
puts(&buf);
}
/// This function is called on panic, like an assertion failure
#[panic_handler]
fn panic(info: &PanicInfo) -> ! { // `!` means that panic handler will never return
// Display the filename and line number
println!("*** Rust Panic:");
if let Some(location) = info.location() {
println!("File: {}", location.file());
println!("Line: {}", location.line());
} else {
println!("Unknown location");
}
// Set to true if we are already in the panic handler
static mut IN_PANIC: bool = false;
// Display the payload
if unsafe { !IN_PANIC } { // Prevent panic loop while displaying the payload
unsafe { IN_PANIC = true };
if let Some(payload) = info.payload().downcast_ref::<&str>() {
println!("Payload: {}", payload);
}
}
// Terminate the app
unsafe { exit(1); }
}
/// Limit Strings to 64 chars, similar to `char[64]` in C
pub type String = heapless::String::<64>;
extern "C" { // Import POSIX Functions. TODO: Import with bindgen
pub fn open(path: *const u8, oflag: i32, ...) -> i32;
pub fn read(fd: i32, buf: *mut u8, count: u32) -> i32;
pub fn write(fd: i32, buf: *const u8, count: u32) -> i32;
pub fn close(fd: i32) -> i32;
pub fn ioctl(fd: i32, request: i32, ...) -> i32; // On NuttX: request is i32, not u64 like Linux
pub fn sleep(secs: u32) -> u32;
pub fn usleep(usec: u32) -> u32;
pub fn exit(status: u32) -> !;
}
/// TODO: Import with bindgen from https://github.com/lupyuen/incubator-nuttx/blob/rust/include/nuttx/ioexpander/gpio.h
pub const GPIOC_WRITE: i32 = _GPIOBASE | 1; // _GPIOC(1)
pub const GPIOC_READ: i32 = _GPIOBASE | 2; // _GPIOC(2)
pub const GPIOC_PINTYPE: i32 = _GPIOBASE | 3; // _GPIOC(3)
pub const GPIOC_REGISTER: i32 = _GPIOBASE | 4; // _GPIOC(4)
pub const GPIOC_UNREGISTER: i32 = _GPIOBASE | 5; // _GPIOC(5)
pub const GPIOC_SETPINTYPE: i32 = _GPIOBASE | 6; // _GPIOC(6)
/// TODO: Import with bindgen from https://github.com/lupyuen/incubator-nuttx/blob/rust/include/fcntl.h
pub const _GPIOBASE: i32 = 0x2300; /* GPIO driver commands */
// #define _GPIOC(nr) _IOC(_GPIOBASE,nr)
// #define _IOC(type,nr) ((type)|(nr))
/// TODO: Import with bindgen from https://github.com/lupyuen/incubator-nuttx/blob/rust/include/fcntl.h
pub const O_RDONLY: i32 = 1 << 0; /* Open for read access (only) */
pub const O_RDOK: i32 = O_RDONLY; /* Read access is permitted (non-standard) */
pub const O_WRONLY: i32 = 1 << 1; /* Open for write access (only) */
pub const O_WROK: i32 = O_WRONLY; /* Write access is permitted (non-standard) */
pub const O_RDWR: i32 = O_RDOK|O_WROK; /* Open for both read & write access */