### Build an example Source: https://github.com/tock/libtock-rs/blob/master/README.md Compiles a specific example for a target platform. ```shell make nrf52 EXAMPLE=console # Builds the console example for the nrf52 ``` -------------------------------- ### Build and install a generic TAB Source: https://github.com/tock/libtock-rs/blob/master/README.md Creates a Tock Application Bundle for multiple boards and installs it using tockloader. ```shell make tab EXAMPLE= ``` ```shell tockloader install target/tab/ ``` -------------------------------- ### Install dependencies Source: https://github.com/tock/libtock-rs/blob/master/README.md Configures the build environment for libtock-rs. ```shell make setup ``` -------------------------------- ### Implement Hello World app with static_component! Source: https://github.com/tock/libtock-rs/blob/master/doc/PlatformDesignStory.md Example of using the static_component! macro to instantiate an App struct without using unsafe blocks. ```rust #![no_std] static GREETING: [u8; 7] = *b"Hello, "; static NOUN: [u8; 7] = *b"World!\n"; fn main() { libtock_console::set_write_callback::<_, AppLink>(0); libtock_console::set_write_buffer(&GREETING); libtock_console::start_write(GREETING.len()); loop { libtock_runtime::yieldk(); } } struct App { done: core::cell::Cell } impl App { pub const fn new() -> App { App { done: core::cell::Cell::new(false) } } } impl MethodCallback for App { fn call(&self, _response: WriteCompleted) { if self.done.get() { return; } self.done.set(true); set_write_buffer(TockSyscalls, &NOUN ); start_write(TockSyscalls, NOUN.len() ); } } libtock_runtime::static_component![AppLink, APP: App = App::new()]; ``` -------------------------------- ### Tock OS Kernel Output Example Source: https://github.com/tock/libtock-rs/blob/master/doc/FaultDebuggingExample.md This is an example of kernel output indicating a Memop syscall, which is used in the address checking logic within `asm_riscv32.S`. ```text Last Syscall: Some(Memop { operand: 0, arg0: 2147494144 }) ``` -------------------------------- ### Build with custom memory addresses Source: https://github.com/tock/libtock-rs/blob/master/build_scripts/README.md Define specific flash and RAM starting addresses using environment variables for custom memory layouts. ```bash $ LIBTOCK_LINKER_FLASH=0x00040000 LIBTOCK_LINKER_RAM=0x20008000 cargo build --target thumbv7em-none-eabi --release ``` -------------------------------- ### Asynchronous Temperature Read Example Source: https://context7.com/tock/libtock-rs/llms.txt Initiates an asynchronous temperature sensor read. A callback is executed upon completion, providing the temperature in hundredths of degrees Celsius. Requires registering a listener and calling the read function within a `share::scope`. ```rust fn read_temp_async() { let listener = TemperatureListener(|temp_val| { writeln!(Console::writer(), "Async temp: {} (hundredths °C)", temp_val).unwrap(); }); share::scope(|subscribe| { Temperature::register_listener(&listener, subscribe).unwrap(); Temperature::read_temperature().unwrap(); // Callback will be invoked when reading completes }); } ``` -------------------------------- ### Set Write Buffer and Start Write Functions Source: https://github.com/tock/libtock-rs/blob/master/doc/PlatformDesignStory.md Provides `set_write_buffer` and `start_write` functions for the `libtock_console` crate. `set_write_buffer` uses `const_allow` to set the buffer, and `start_write` uses `command` to initiate the write operation. ```rust const WRITE_BUFFER: usize = 1; const START_WRITE: usize = 1; pub fn set_write_buffer(buffer: &'static [u8]) { libtock_runtime::const_allow(DRIVER, WRITE_BUFFER, buffer); } pub fn start_write(bytes: usize) { libtock_runtime::command(DRIVER, START_WRITE, bytes, 0); } ``` -------------------------------- ### TBF start section analysis Source: https://github.com/tock/libtock-rs/blob/master/doc/FaultDebuggingExample.md Examination of the 'start' section of the TBF file, which typically contains entry point information and initial code. This is part of the byte-by-byte decoding process. ```text start: 20040068 17 04 00 00 aa 87 84 43 20040070 63 0c 94 00 21 45 85 45 09 46 09 47 73 00 00 00 20040080 01 45 19 47 73 00 00 00 01 45 cc 43 15 47 73 00 20040090 00 00 03 a1 87 00 c8 47 09 c9 8c 4b d0 4b 94 41 200400a0 14 c2 71 15 91 05 11 06 7d f9 88 4f 19 c5 cc 4f 200400b0 23 80 05 00 7d 15 85 05 65 fd ef 00 c0 03 ``` -------------------------------- ### RISC-V Assembly: Start Address Verification Source: https://github.com/tock/libtock-rs/blob/master/doc/FaultDebuggingExample.md This RISC-V assembly code verifies that the program binary was loaded at the correct address by comparing the current program counter (pc) with the expected start address stored in rt_header. If the addresses do not match, it reports an error via LowLevelDebug and exits. ```assembly .section .start, "ax" .globl start start: /* First, verify the process binary was loaded at the correct address. The * check is performed by comparing the program counter at the start to the * address of `start`, which is stored in rt_header. */ auipc s0, 0 /* s0 = pc */ mv a5, a0 /* Save rt_header so syscalls don't overwrite it */ lw s1, 0(a5) /* s1 = rt_header.start */ beq s0, s1, .Lset_brk /* Skip error handling code if pc is correct */ /* If the beq on the previous line did not jump, then the binary is not at * the correct location. Report the error via LowLevelDebug then exit. */ li a0, 8 /* LowLevelDebug driver number */ li a1, 1 /* Command: Print alert code */ li a2, 2 /* Alert code 2 (incorrect location) */ li a4, 2 /* `command` class */ ecall li a0, 0 /* exit-terminate */ /* TODO: Set a completion code, once completion codes are decided */ li a4, 6 /* `exit` class */ ecall .Lset_brk: /* memop(): set brk to rt_header's initial break value */ li a0, 0 /* operation: set break */ lw a1, 4(a5) /* rt_header's initial process break */ li a4, 5 /* `memop` class */ ecall ``` -------------------------------- ### Asynchronous ADC Read Example Source: https://context7.com/tock/libtock-rs/llms.txt Initiates an asynchronous analog-to-digital converter read. A callback function is invoked upon completion. Requires registering a listener and then calling the read function within a `share::scope`. ```rust fn read_adc_async() { let listener = ADCListener(|adc_val| { writeln!(Console::writer(), "Async ADC: {}", adc_val).unwrap(); }); share::scope(|subscribe| { Adc::register_listener(&listener, subscribe).unwrap(); Adc::read_single_sample().unwrap(); // Process continues in callback }); } ``` -------------------------------- ### Synchronous ADC Read Example Source: https://context7.com/tock/libtock-rs/llms.txt Reads a single analog-to-digital converter sample synchronously. This function blocks until a sample is available or an error occurs. Ensure the ADC driver is available before use. ```rust #![no_main] #![no_std] use core::fmt::Write; use libtock::adc::{Adc, ADCListener}; use libtock::alarm::{Alarm, Milliseconds}; use libtock::console::Console; use libtock::runtime::{set_main, stack_size}; use libtock_platform::share; set_main! {main} stack_size! {0x300} fn main() { // Check if ADC driver is available if Adc::exists().is_err() { writeln!(Console::writer(), "ADC driver unavailable").unwrap(); return; } // Get ADC resolution and reference voltage if let Ok(bits) = Adc::get_resolution_bits() { writeln!(Console::writer(), "ADC resolution: {} bits", bits).unwrap(); } if let Ok(ref_mv) = Adc::get_reference_voltage_mv() { writeln!(Console::writer(), "Reference voltage: {} mV", ref_mv).unwrap(); } loop { // Synchronous ADC read (blocking) match Adc::read_single_sample_sync() { Ok(adc_val) => { writeln!(Console::writer(), "ADC sample: {}", adc_val).unwrap(); } Err(e) => { writeln!(Console::writer(), "ADC error: {:?}", e).unwrap(); } } Alarm::sleep_for(Milliseconds(2000)).unwrap(); } } // Asynchronous ADC read example fn read_adc_async() { let listener = ADCListener(|adc_val| { writeln!(Console::writer(), "Async ADC: {}", adc_val).unwrap(); }); share::scope(|subscribe| { Adc::register_listener(&listener, subscribe).unwrap(); Adc::read_single_sample().unwrap(); // Process continues in callback }); } ``` -------------------------------- ### Key-Value Storage Operations Source: https://context7.com/tock/libtock-rs/llms.txt Demonstrates persistent key-value storage using the KeyValue driver. Supports getting, setting, adding, updating, and deleting key-value pairs. Check for driver availability before use. ```rust #![no_main] #![no_std] use core::fmt::Write; use core::str; use libtock::console::Console; use libtock::key_value::KeyValue; use libtock::runtime::{set_main, stack_size}; set_main! {main} stack_size! {0x200} fn main() { // Check if key-value storage is available if !KeyValue::exists() { writeln!(Console::writer(), "Key-value storage unavailable").unwrap(); return; } let key = b"my_app_config"; let mut value_buf: [u8; 64] = [0; 64]; // Try to get existing value match KeyValue::get(key, &mut value_buf) { Ok(len) => { let len = len as usize; if let Ok(val_str) = str::from_utf8(&value_buf[0..len]) { writeln!(Console::writer(), "Got value: {}", val_str).unwrap(); } else { writeln!(Console::writer(), "Got {} bytes (binary)", len).unwrap(); } } Err(_) => writeln!(Console::writer(), "Key not found").unwrap(), } // Set a new value let new_value = b"configuration_data_v1"; match KeyValue::set(key, new_value) { Ok(()) => writeln!(Console::writer(), "Value stored successfully").unwrap(), Err(e) => writeln!(Console::writer(), "Store error: {:?}", e).unwrap(), } // Add a new key (fails if key exists) match KeyValue::add(b"new_key", b"new_value") { Ok(()) => writeln!(Console::writer(), "New key added").unwrap(), Err(e) => writeln!(Console::writer(), "Add failed (may exist): {:?}", e).unwrap(), } // Update existing key (fails if key doesn't exist) match KeyValue::update(key, b"updated_value") { Ok(()) => writeln!(Console::writer(), "Value updated").unwrap(), Err(e) => writeln!(Console::writer(), "Update error: {:?}", e).unwrap(), } // Delete a key match KeyValue::delete(key) { Ok(()) => writeln!(Console::writer(), "Key deleted").unwrap(), Err(e) => writeln!(Console::writer(), "Delete error: {:?}", e).unwrap(), } } ``` -------------------------------- ### RISC-V Disassembly of .start Section Source: https://github.com/tock/libtock-rs/blob/master/doc/FaultDebuggingExample.md This is the disassembly of the .start section, including the rt_header and start labels. It shows the low-level RISC-V instructions executed during the early stages of program execution. ```assembly target/riscv32imac-unknown-none-elf/release/examples/low_level_debug: file format elf32-littleriscv Disassembly of section .start: 20040048 : 20040048: 0068 addi a0,sp,12 2004004a: 2004 fld fs1,0(s0) 2004004c: 2900 fld fs0,16(a0) 2004004e: 8000 0x8000 20040050: 2900 fld fs0,16(a0) 20040052: 8000 0x8000 20040054: 0000 unimp 20040056: 0000 unimp 20040058: 2900 fld fs0,16(a0) 2004005a: 8000 0x8000 2004005c: 2900 fld fs0,16(a0) 2004005e: 8000 0x8000 20040060: 0000 unimp 20040062: 0000 unimp 20040064: 2900 fld fs0,16(a0) 20040066: 8000 0x8000 20040068 : 20040068: 00000417 auipc s0,0x0 2004006c: 87aa mv a5,a0 2004006e: 4384 lw s1,0(a5) 20040070: 00940c63 beq s0,s1,20040088 20040074: 4521 li a0,8 20040076: 4585 li a1,1 20040078: 4609 li a2,2 2004007a: 4709 li a4,2 2004007c: 00000073 ecall 20040080: 4501 li a0,0 20040082: 4719 li a4,6 20040084: 00000073 ecall 20040088: 4501 li a0,0 2004008a: 43cc lw a1,4(a5) 2004008c: 4715 li a4,5 2004008e: 00000073 ecall 20040092: 0087a103 lw sp,8(a5) 20040096: 47c8 lw a0,12(a5) 20040098: c909 beqz a0,200400aa 2004009a: 4b8c lw a1,16(a5) 2004009c: 4bd0 lw a2,20(a5) 2004009e: 4194 lw a3,0(a1) 200400a0: c214 sw a3,0(a2) 200400a2: 1571 addi a0,a0,-4 200400a4: 0591 addi a1,a1,4 200400a6: 0611 addi a2,a2,4 200400a8: f97d bnez a0,2004009e 200400aa: 4f88 lw a0,24(a5) 200400ac: c519 beqz a0,200400ba 200400ae: 4fcc lw a1,28(a5) 200400b0: 00058023 sb zero,0(a1) 200400b4: 157d addi a0,a0,-1 200400b6: 0585 addi a1,a1,1 200400b8: fd65 bnez a0,200400b0 200400ba: 03c000ef jal ra,200400f6 ``` -------------------------------- ### Synchronous Temperature Read Example Source: https://context7.com/tock/libtock-rs/llms.txt Reads the temperature sensor value synchronously. The value is returned in hundredths of degrees Celsius. This function blocks until the temperature reading is complete. Handles potential read errors. ```rust #![no_main] #![no_std] use core::fmt::Write; use libtock::alarm::{Alarm, Milliseconds}; use libtock::console::Console; use libtock::runtime::{set_main, stack_size}; use libtock::temperature::{Temperature, TemperatureListener}; use libtock_platform::share; set_main! {main} stack_size! {0x200} fn main() { // Check if temperature driver is available match Temperature::exists() { Ok(()) => writeln!(Console::writer(), "Temperature sensor available").unwrap(), Err(_) => { writeln!(Console::writer(), "Temperature sensor unavailable").unwrap(); return; } } loop { // Synchronous temperature read (value in hundredths of degrees C) match Temperature::read_temperature_sync() { Ok(temp_val) => { // Convert from hundredths to displayable format let sign = if temp_val >= 0 { "" } else { "-" }; let whole = i32::abs(temp_val) / 100; let frac = i32::abs(temp_val) % 100; writeln!(Console::writer(), "Temperature: {}{}.{:02}°C", sign, whole, frac).unwrap(); } Err(e) => { writeln!(Console::writer(), "Read error: {:?}", e).unwrap(); } } Alarm::sleep_for(Milliseconds(2000)).unwrap(); } } // Asynchronous temperature reading fn read_temp_async() { let listener = TemperatureListener(|temp_val| { writeln!(Console::writer(), "Async temp: {} (hundredths °C)", temp_val).unwrap(); }); share::scope(|subscribe| { Temperature::register_listener(&listener, subscribe).unwrap(); Temperature::read_temperature().unwrap(); // Callback will be invoked when reading completes }); } ``` -------------------------------- ### Build and flash for a specific board Source: https://github.com/tock/libtock-rs/blob/master/README.md Commands to compile and deploy an application to a specific hardware platform. ```shell make EXAMPLE= ``` ```shell make flash- EXAMPLE= ``` -------------------------------- ### Clone the repository Source: https://github.com/tock/libtock-rs/blob/master/README.md Initializes the local environment by cloning the repository and its submodules. ```shell git clone --recursive https://github.com/tock/libtock-rs cd libtock-rs ``` -------------------------------- ### Asynchronous Hello World Application Source: https://github.com/tock/libtock-rs/blob/master/doc/PlatformDesignStory.md A basic asynchronous application demonstrating system calls for writing to a console in Tock. ```rust #![no_std] static GREETING: [u8; 7] = *b"Hello, "; static NOUN: [u8; 7] = *b"World!\n"; static mut DONE: bool = false; // Driver, command, allow, and subscription numbers. const DRIVER: usize = 1; const WRITE_DONE: usize = 1; const WRITE_BUFFER: usize = 1; const START_WRITE: usize = 1; fn main() { libtock_runtime::subscribe(DRIVER, WRITE_DONE, write_complete, 0); libtock_runtime::const_allow(DRIVER, WRITE_BUFFER, &GREETING); libtock_runtime::command(DRIVER, START_WRITE, GREETING.len(), 0); loop { libtock_runtime::yieldk(); } } extern "C" fn write_complete(bytes: usize, _: usize, _: usize, _data: usize) { // Detect if this write completion is a result of writing NOUN (the // second write). If so, return immediately to avoid an infinite loop. unsafe { if DONE { return; } DONE = true; } // At this point, we just finished writing GREETING and need to write NOUN. libtock_runtime::const_allow(DRIVER, WRITE_BUFFER, &NOUN); libtock_runtime::command(DRIVER, START_WRITE, NOUN.len(), 0); } ``` -------------------------------- ### Console Output and Input in Rust Source: https://context7.com/tock/libtock-rs/llms.txt Demonstrates formatted output using the fmt::Write trait, raw byte writing, and reading input into a buffer. ```rust #![no_main] #![no_std] use core::fmt::Write; use libtock::console::Console; use libtock::runtime::{set_main, stack_size}; set_main! {main} stack_size! {0x100} fn main() { // Check if console driver is available if Console::exists() { // Write formatted output to the console writeln!(Console::writer(), "Hello world!").unwrap(); // Write raw bytes directly Console::write(b"Raw byte output\n").unwrap(); // Read input into a buffer let mut buf = [0u8; 64]; let (bytes_read, result) = Console::read(&mut buf); if result.is_ok() { writeln!(Console::writer(), "Read {} bytes", bytes_read).unwrap(); } } } ``` -------------------------------- ### Control Screen Display with Screen API Source: https://context7.com/tock/libtock-rs/llms.txt Demonstrates initializing, configuring, and writing to the screen using the Screen driver. Supports setting resolution, brightness, rotation, and writing pixel data. ```rust #![no_main] #![no_std] use core::fmt::Write; use libtock::console::Console; use libtock::runtime::{set_main, stack_size}; use libtock::screen::Screen; set_main! {main} stack_size! {0x1000} fn main() { // Check if screen driver is available if Screen::exists().is_err() { writeln!(Console::writer(), "Screen unavailable").unwrap(); return; } // Initialize screen Screen::screen_setup().unwrap(); Screen::set_power(1).unwrap(); // Get and set resolution if let Ok((width, height)) = Screen::get_resolution() { writeln!(Console::writer(), "Screen resolution: {}x{}", width, height).unwrap(); } // Set brightness (0-100 typical range) Screen::set_brightness(80).unwrap(); // Set rotation (0, 90, 180, 270 degrees) Screen::set_rotation(0).unwrap(); // Enable/disable color inversion Screen::set_invert_on().unwrap(); Screen::set_invert_off().unwrap(); // Define a write region and write pixel data let x = 0; let y = 0; let width = 100; let height = 100; Screen::set_write_frame(x, y, width, height).unwrap(); // Prepare pixel buffer (RGB565 format example) let mut pixel_buffer: [u8; 200] = [0; 200]; // Fill with red pixels (RGB565: 0xF800) for i in (0..pixel_buffer.len()).step_by(2) { pixel_buffer[i] = 0xF8; // High byte pixel_buffer[i + 1] = 0x00; // Low byte } // Write pixel data to screen Screen::write(&pixel_buffer).unwrap(); // Fill entire frame with a solid color (RGB565) let mut fill_buf: [u8; 2] = [0; 2]; let blue: u16 = 0x001F; // RGB565 blue Screen::fill(&mut fill_buf, blue).unwrap(); } ``` -------------------------------- ### LED Control and Timing Source: https://context7.com/tock/libtock-rs/llms.txt Shows how to iterate through available LEDs, toggle states, and use the Alarm driver for non-blocking delays. ```rust #![no_main] #![no_std] use libtock::alarm::{Alarm, Milliseconds}; use libtock::leds::Leds; use libtock::runtime::{set_main, stack_size}; set_main! {main} stack_size! {0x200} fn main() { // Get number of available LEDs on the board if let Ok(leds_count) = Leds::count() { let mut counter = 0u32; loop { for led_index in 0..leds_count { // Turn LED on if corresponding bit is set if counter & (1 << led_index) > 0 { let _ = Leds::on(led_index); } else { let _ = Leds::off(led_index); } } // Toggle a specific LED let _ = Leds::toggle(0); // Sleep for 250ms before next update Alarm::sleep_for(Milliseconds(250)).unwrap(); counter += 1; } } } ``` -------------------------------- ### Build with platform environment variable Source: https://github.com/tock/libtock-rs/blob/master/build_scripts/README.md Specify the target platform using the LIBTOCK_PLATFORM environment variable during the build process. ```bash $ LIBTOCK_PLATFORM=microbit_v2 cargo build --target thumbv7em-none-eabi --release ``` -------------------------------- ### Use GPIO API for pin control and interrupts Source: https://context7.com/tock/libtock-rs/llms.txt Shows configuring pins as input or output, reading pin states, and setting up interrupt listeners. ```rust #![no_main] #![no_std] use core::fmt::Write; use libtock::console::Console; use libtock::gpio::{Gpio, GpioInterruptListener, GpioState, Pin, PinInterruptEdge, PullDown, PullNone, PullUp}; use libtock::runtime::{set_main, stack_size}; use libtock_platform::{share, Syscalls}; use libtock_runtime::TockSyscalls; set_main! {main} stack_size! {0x1000} fn main() { // Check GPIO availability if Gpio::exists().is_err() || !Gpio::count().is_ok_and(|c| c > 0) { writeln!(Console::writer(), "No GPIO pins available").unwrap(); return; } // Configure pin 0 as output and control it let mut pin0 = Gpio::get_pin(0).unwrap(); { let mut output = pin0.make_output().unwrap(); output.set().unwrap(); // Set pin high output.clear().unwrap(); // Set pin low output.toggle().unwrap(); // Toggle pin state } // Pin is disabled when OutputPin is dropped // Configure pin 1 as input with pull-up resistor and read state let pin1 = Gpio::get_pin(1).unwrap(); let input = pin1.make_input::().unwrap(); match input.read() { Ok(GpioState::High) => writeln!(Console::writer(), "Pin 1 is HIGH").unwrap(), Ok(GpioState::Low) => writeln!(Console::writer(), "Pin 1 is LOW").unwrap(), Err(_) => writeln!(Console::writer(), "Error reading pin").unwrap(), } // Configure pin 2 for interrupt-driven input let pin2 = Gpio::get_pin(2).unwrap(); let input2 = pin2.make_input::().unwrap(); input2.enable_interrupts(PinInterruptEdge::Rising).unwrap(); // Set up interrupt listener let listener = GpioInterruptListener(|gpio_index, state| { writeln!(Console::writer(), "GPIO[{}]: {:?}", gpio_index, state).unwrap(); }); share::scope(|subscribe| { Gpio::register_listener(&listener, subscribe).unwrap(); loop { TockSyscalls::yield_wait(); } }); } ``` -------------------------------- ### Application Usage of libtock_console Source: https://github.com/tock/libtock-rs/blob/master/doc/PlatformDesignStory.md Demonstrates how an application can utilize the `libtock_console` crate to send messages. It sets a write callback, writes an initial greeting, and then writes a noun upon completion of the first write. ```rust #![no_std] static GREETING: [u8; 7] = *b"Hello, "; static NOUN: [u8; 7] = *b"World!\n"; static mut DONE: bool = false; fn main() { libtock_console::set_write_callback::(0); libtock_console::set_write_buffer(&GREETING); libtock_console::start_write(GREETING.len()); loop { libtock_runtime::yieldk(); } } struct App; impl libtock_platform::FreeCallback for App { fn call(_response: WriteCompleted) { unsafe { if DONE { return; } DONE = true; } libtock_console::set_write_buffer(&NOUN); libtock_console::start_write(NOUN.len()); } } ``` -------------------------------- ### Use Alarm API for timing services Source: https://context7.com/tock/libtock-rs/llms.txt Demonstrates checking driver availability, reading frequency and ticks, and performing sleep operations. ```rust #![no_main] #![no_std] use core::fmt::Write; use libtock::alarm::{Alarm, Hz, Milliseconds, Ticks}; use libtock::console::Console; use libtock::runtime::{set_main, stack_size}; set_main! {main} stack_size! {0x200} fn main() { // Check if alarm driver exists if Alarm::exists().is_ok() { // Get the timer frequency if let Ok(freq) = Alarm::get_frequency() { writeln!(Console::writer(), "Alarm frequency: {} Hz", freq.0).unwrap(); } // Get current tick count if let Ok(ticks) = Alarm::get_ticks() { writeln!(Console::writer(), "Current ticks: {}", ticks).unwrap(); } // Get time in milliseconds if let Ok(ms) = Alarm::get_milliseconds() { writeln!(Console::writer(), "Time: {} ms", ms).unwrap(); } loop { // Sleep for 2 seconds using Milliseconds Alarm::sleep_for(Milliseconds(2000)).unwrap(); writeln!(Console::writer(), "Woke up after 2 seconds").unwrap(); // Sleep using raw ticks (useful for precise timing) // Alarm::sleep_for(Ticks(1000)).unwrap(); } } } ``` -------------------------------- ### Formatting structs with ufmt Source: https://github.com/tock/libtock-rs/blob/master/ufmt/README.md Demonstrates using the uwrite! macro to format various data structures, including custom structs and tuples, within an exception handler. ```rust // .. #[derive(Clone, Copy, uDebug)] struct Pair { x: i32, y: i32, } static X: AtomicI32 = AtomicI32::new(0); static Y: AtomicI32 = AtomicI32::new(0); #[exception] fn PendSV() { let x = X.load(Ordering::Relaxed); let y = Y.load(Ordering::Relaxed); uwrite!(&mut W, "{:?}", Braces {}).unwrap(); uwrite!(&mut W, "{:#?}", Braces {}).unwrap(); uwrite!(&mut W, "{:?}", Parens()).unwrap(); uwrite!(&mut W, "{:#?}", Parens()).unwrap(); uwrite!(&mut W, "{:?}", I32(x)).unwrap(); uwrite!(&mut W, "{:#?}", I32(x)).unwrap(); uwrite!(&mut W, "{:?}", Tuple(x, y)).unwrap(); uwrite!(&mut W, "{:#?}", Tuple(x, y)).unwrap(); let pair = Pair { x, y }; uwrite!(&mut W, "{:?}", pair).unwrap(); uwrite!(&mut W, "{:#?}", pair).unwrap(); let first = pair; let second = pair; uwrite!(&mut W, "{:?}", Nested { first, second }).unwrap(); uwrite!(&mut W, "{:#?}", Nested { first, second }).unwrap(); } // .. ``` -------------------------------- ### Configure Cargo.toml build dependencies Source: https://github.com/tock/libtock-rs/blob/master/build_scripts/README.md Add the libtock_build_scripts crate as a build dependency in your application's Cargo.toml file. ```toml # Cargo.toml ... [build-dependencies] libtock_build_scripts = { git = "https://github.com/tock/libtock-rs"} ``` -------------------------------- ### Play Tones and Melodies with Buzzer API Source: https://context7.com/tock/libtock-rs/llms.txt Demonstrates playing single tones and melodies using the Buzzer driver. Ensure the buzzer is available before use. Tones are played synchronously. ```rust #![no_main] #![no_std] use core::fmt::Write; use core::time::Duration; use libtock::buzzer::{Buzzer, Note}; use libtock::console::Console; use libtock::runtime::{set_main, stack_size}; set_main! {main} stack_size! {0x800} fn main() { // Check if buzzer is available if Buzzer::exists().is_err() { writeln!(Console::writer(), "Buzzer unavailable").unwrap(); return; } // Play a simple tone (frequency in Hz, duration) Buzzer::tone_sync(440, Duration::from_millis(500)).unwrap(); // A4 note for 500ms // Play a melody using predefined musical notes let melody = [ (Note::C4, 250), (Note::D4, 250), (Note::E4, 250), (Note::F4, 250), (Note::G4, 500), ]; for (note, duration_ms) in melody { Buzzer::tone_sync(note as u32, Duration::from_millis(duration_ms)).unwrap(); } // Play Ode to Joy excerpt writeln!(Console::writer(), "Playing melody...").unwrap(); let ode_to_joy = [ (Note::E4, 4), (Note::E4, 4), (Note::F4, 4), (Note::G4, 4), (Note::G4, 4), (Note::F4, 4), (Note::E4, 4), (Note::D4, 4), (Note::C4, 4), (Note::C4, 4), (Note::D4, 4), (Note::E4, 4), ]; const TEMPO: u32 = 120; const WHOLE_NOTE_MS: u32 = (60000 * 4) / TEMPO; for (note, divider) in ode_to_joy { let duration = Duration::from_millis((WHOLE_NOTE_MS / divider as u32) as u64); Buzzer::tone_sync(note as u32, duration).unwrap(); } } ``` -------------------------------- ### Enable rust-embedded support Source: https://github.com/tock/libtock-rs/blob/master/README.md Enables compatibility with the embedded_hal crate during the build process. ```shell FEATURES=rust_embedded ``` -------------------------------- ### Implement SyncAdapter Source: https://github.com/tock/libtock-rs/blob/master/doc/PlatformDesignStory.md Implements a generic `SyncAdapter` that wraps an asynchronous driver to provide a synchronous interface. It uses `core::cell::Cell` to store the response and `yieldk` to wait for it. ```rust use libtock_platform::MethodCallback; pub struct SyncAdapter { response: core::cell::Cell>, syscalls: Syscalls, } impl SyncAdapter { pub const fn new(syscalls: Syscalls) -> SyncAdapter { SyncAdapter { response: core::cell::Cell::new(None), syscalls } } } impl SyncAdapter { pub fn wait(&self) -> AsyncResponse { loop { match self.response.take() { Some(response) => return response, None => self.syscalls.yieldk(), } } } } impl MethodCallback for SyncAdapter { fn call(&self, response: AsyncResponse) { self.response.set(Some(response)); } } ``` -------------------------------- ### Adapt libtock_console to Use App-Provided Syscalls Source: https://github.com/tock/libtock-rs/blob/master/doc/PlatformDesignStory.md These functions allow the console driver to use an app-provided `Syscalls` implementation, making it compatible with both real and fake kernels. ```rust pub fn set_write_callback>( syscalls: S, data: usize) { syscalls.subscribe(1, 1, write_complete::, data); } extern "C" fn write_complete>( bytes: usize, _: usize, _: usize, data: usize) { Callback::call(WriteCompleted { bytes, data }); } pub fn set_write_buffer(syscalls: S, buffer: &'static [u8]) { syscalls.const_allow(1, 1, buffer); } pub fn start_write(syscalls: S, bytes: usize) { syscalls.command(1, 1, bytes, 0); } ``` -------------------------------- ### Button Polling and Interrupts Source: https://context7.com/tock/libtock-rs/llms.txt Illustrates both polling for button states and registering an interrupt-driven listener using the share::scope pattern. ```rust #![no_main] #![no_std] use core::fmt::Write; use libtock::buttons::{ButtonListener, ButtonState, Buttons}; use libtock::console::Console; use libtock::leds::Leds; use libtock::runtime::{set_main, stack_size}; use libtock_platform::{share, Syscalls}; use libtock_runtime::TockSyscalls; set_main! {main} stack_size! {0x1000} fn main() { // Create a listener that toggles LEDs and prints state on button events let listener = ButtonListener(|button, state| { let _ = Leds::toggle(button); writeln!(Console::writer(), "Button {}: {:?}", button, state).unwrap(); }); if let Ok(buttons_count) = Buttons::count() { writeln!(Console::writer(), "Found {} buttons", buttons_count).unwrap(); // Check individual button state (polling) for i in 0..buttons_count { if Buttons::is_pressed(i) { writeln!(Console::writer(), "Button {} is pressed", i).unwrap(); } } // Register for interrupt-driven events share::scope(|subscribe| { Buttons::register_listener(&listener, subscribe).unwrap(); // Enable interrupts for all buttons for i in 0..buttons_count { Buttons::enable_interrupts(i).unwrap(); } // Wait for button events loop { TockSyscalls::yield_wait(); } }); } } ``` -------------------------------- ### Create test_component! Macro Source: https://github.com/tock/libtock-rs/blob/master/doc/PlatformDesignStory.md A macro to create a thread-local instance of a component and provide `FreeCallback` implementations for its `MethodCallback` implementations. This simplifies unit testing. ```rust #[macro_export] macro_rules! test_component { [$link:ident, $name:ident: $comp:ty = $init:expr] => { let $name = std::boxed::Box::leak(std::boxed::Box::new($init)) as &$comp; std::thread_local!(static GLOBAL: std::cell::Cell> = const {std::cell::Cell::new(None)}) GLOBAL.with(|g| g.set(Some($name))); struct $link; impl libtock_platform::FreeCallback for $link where $comp: libtock_platform::MethodCallback { fn call(response: T) { GLOBAL.with(|g| g.get().unwrap()).call(response); } } }; } ``` -------------------------------- ### Define static_component! macro Source: https://github.com/tock/libtock-rs/blob/master/doc/PlatformDesignStory.md This macro is designed for libtock_runtime's single-threaded environment to safely manage component instantiation. ```rust #[macro_export] macro_rules! static_component { [$link:ident, $name:ident: $comp:ty = $init:expr] => { static mut COMPONENT: $comp = $init; struct $link; impl libtock_platform::FreeCallback for $link where $comp: libtock_platform::MethodCallback { fn call(response: T) { unsafe { &COMPONENT }.call(response); } } }; } ``` -------------------------------- ### Unit Test for Console Write Operation Source: https://github.com/tock/libtock-rs/blob/master/doc/PlatformDesignStory.md A unit test demonstrating the usage of `SyncAdapter` and `test_component!` to test the `libtock_console` write functionality. It verifies buffer content and response data. ```rust @#[cfg(test)] mod tests { #[test] fn write() { extern crate std; use libtock_platform::MethodCallback; use libtock_sync::SyncAdapter; use libtock_unittest::FakeSyscalls; use std::boxed::Box; use std::thread_local; use super::{set_write_buffer, set_write_callback, start_write, WriteCompleted}; let syscalls: &'_ = Box::leak(Box::new(FakeSyscalls::new())); libtock_unittest::test_component![SyncAdapterLink, sync_adapter: SyncAdapter = SyncAdapter::new(syscalls)]; set_write_callback::<_, SyncAdapterLink>(syscalls, 1234); set_write_buffer(syscalls, b"Hello"); start_write(syscalls, 5); let response = sync_adapter.wait(); assert_eq!(response.bytes, 5); assert_eq!(response.data, 1234); assert_eq!(syscalls.read_buffer(), b"Hello"); } } ``` -------------------------------- ### Invoke auto_layout in build.rs Source: https://github.com/tock/libtock-rs/blob/master/build_scripts/README.md Call the auto_layout function within your application's build.rs file to configure linker scripts and paths. ```rs // build.rs fn main() { libtock_build_scripts::auto_layout(); } ``` -------------------------------- ### Generate Random Bytes Asynchronously with RNG Source: https://context7.com/tock/libtock-rs/llms.txt Use Rng::get_bytes_async for asynchronous random byte generation. This involves registering a listener and allowing a buffer. The callback is invoked when random bytes are ready. ```rust // Asynchronous RNG example fn get_random_async() { let listener = RngListener(|bytes_filled| { writeln!(Console::writer(), "Got {} random bytes", bytes_filled).unwrap(); }); let mut buffer: [u8; 16] = [0; 16]; share::scope(|handle| { let (allow_rw, subscribe) = handle.split(); Rng::allow_buffer(&mut buffer, allow_rw).unwrap(); Rng::register_listener(&listener, subscribe).unwrap(); Rng::get_bytes_async(16).unwrap(); // Callback will be invoked when random bytes are ready }); } ``` -------------------------------- ### Define FakeSyscalls Structure for Unit Testing Source: https://github.com/tock/libtock-rs/blob/master/doc/PlatformDesignStory.md This structure simulates the Tock kernel's system call functionality for unit testing. It uses `core::cell::Cell` to manage mutable state. ```rust type RawCallback = extern "C" fn(usize, usize, usize, usize); pub struct FakeSyscalls { callback_pending: core::cell::Cell>, output: core::cell::Cell>, write_buffer: core::cell::Cell>, write_callback: core::cell::Cell>, write_data: core::cell::Cell, } impl FakeSyscalls { pub fn new() -> Self { FakeSyscalls { callback_pending: Cell::new(None), output: Cell::new(Vec::new()), write_buffer: Cell::new(None), write_callback: Cell::new(None), write_data: Cell::new(0), } } pub fn read_buffer(&self) -> &'static [u8] { self.write_buffer.take().unwrap_or(&[]) } } ``` -------------------------------- ### Set Write Callback in libtock_console Source: https://github.com/tock/libtock-rs/blob/master/doc/PlatformDesignStory.md Sets a write callback using `subscribe` for the write done event. This initial version highlights the need to store the callback for later use. ```rust #![no_std] const DRIVER: usize = 1; const WRITE_DONE: usize = 1; fn set_write_callback(callback: fn(bytes: usize, data: usize), data: usize) { libtock_runtime::subscribe(DRIVER, WRITE_DONE, write_complete, data); // We need to store `callback` somewhere -- where to do so? } extern "C" fn write_complete(bytes: usize, _: usize, _: usize, data: usize) { // Hmm, how do we access the callback? } ```