Files
Embedded-Hacking/drivers/0x0b_spi_rust/src/spi.rs
T
Kevin Thomas 3604b3c921 refactor: enforce max 8 code lines, add docstrings, fix warnings across all Rust and C SDK projects
Rust (all 15 projects):
- Refactored overlength functions: format_counter, format_u8, format_f32_1,
  format_u32_minimal, gpio_drive, read_sensor, poll_sensor, format_round_trip,
  format_u32, prepare_write_buf, write_min_digits, write_temp, UartDriver::init,
  init_spi, angle_to_pulse_us, compute_servo_level
- Added 200+ docstrings to test functions, mock structs, impl blocks
- Fixed pub static comments (//) to doc comments (///) in all main.rs files
- Fixed helper function ordering (helpers above callers)
- Fixed Fn(u32) -> FnMut(u32) bound in button poll_button
- Moved OneShot trait import from main.rs to board.rs in adc project
- Added unsafe {} blocks in flash unsafe fn bodies (Rust 2024 edition)
- Removed unused hal::Clock imports from pwm/servo main.rs
- All 15 projects build with zero errors and zero warnings

C Pico SDK (all 15 projects):
- Added docstrings to all public functions, macros, and static variables
- All 15 projects rebuilt with zero errors

Cleanup:
- Removed build/ and target/ directories from git tracking
- Added target/ to .gitignore
- Deleted temporary fix_rust_docs.py script
2026-04-06 08:33:17 -04:00

159 lines
4.9 KiB
Rust

//! @file spi.rs
//! @brief Implementation of the SPI loopback driver helper logic
//! @author Kevin Thomas
//! @date 2025
//!
//! MIT License
//!
//! Copyright (c) 2025 Kevin Thomas
//!
//! Permission is hereby granted, free of charge, to any person obtaining a copy
//! of this software and associated documentation files (the "Software"), to deal
//! in the Software without restriction, including without limitation the rights
//! to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
//! copies of the Software, and to permit persons to whom the Software is
//! furnished to do so, subject to the following conditions:
//!
//! The above copyright notice and this permission notice shall be included in
//! all copies or substantial portions of the Software.
//!
//! THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
//! IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
//! FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
//! AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
//! LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
//! OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
//! SOFTWARE.
/// SPI port number used by the loopback demo.
pub const SPI_PORT: u8 = 0;
/// SPI clock frequency used by the loopback demo.
pub const SPI_BAUD_HZ: u32 = 1_000_000;
/// GPIO pin number for MISO.
pub const PIN_MISO: u8 = 16;
/// GPIO pin number for chip select.
pub const PIN_CS: u8 = 17;
/// GPIO pin number for SCK.
pub const PIN_SCK: u8 = 18;
/// GPIO pin number for MOSI.
pub const PIN_MOSI: u8 = 19;
/// Fixed transmit message used by the SPI loopback demo.
pub const TX_MESSAGE: &[u8; 16] = b"SPI loopback OK\0";
/// Return true when the selected SPI port is supported by the driver.
pub fn is_valid_port(port: u8) -> bool {
port <= 1
}
/// Clear a receive buffer to all zeros.
pub fn clear_rx_buffer(rx: &mut [u8]) {
rx.fill(0);
}
/// Copy `src` into `dst` up to the minimum length of both slices.
pub fn copy_transfer_result(src: &[u8], dst: &mut [u8]) {
let len = core::cmp::min(src.len(), dst.len());
dst[..len].copy_from_slice(&src[..len]);
}
/// Format the `TX:` line with trailing CRLF.
pub fn format_tx_line(buf: &mut [u8], tx: &[u8]) -> usize {
format_line(buf, b"TX: ", tx, false)
}
/// Format the `RX:` line with an extra blank line after it.
pub fn format_rx_line(buf: &mut [u8], rx: &[u8]) -> usize {
format_line(buf, b"RX: ", rx, true)
}
/// Format a single ASCII line for UART output.
fn format_line(buf: &mut [u8], prefix: &[u8], text: &[u8], extra_blank_line: bool) -> usize {
let mut pos = 0;
buf[pos..pos + prefix.len()].copy_from_slice(prefix);
pos += prefix.len();
pos += copy_c_string(&mut buf[pos..], text);
pos += append_crlf(&mut buf[pos..], extra_blank_line);
pos
}
/// Copy bytes from `text` up to the first NUL into `buf`.
fn copy_c_string(buf: &mut [u8], text: &[u8]) -> usize {
let len = c_string_len(text);
buf[..len].copy_from_slice(&text[..len]);
len
}
/// Append CRLF (and optionally a second blank CRLF) to `buf`.
fn append_crlf(buf: &mut [u8], extra: bool) -> usize {
buf[0] = b'\r';
buf[1] = b'\n';
if extra { buf[2] = b'\r'; buf[3] = b'\n'; 4 } else { 2 }
}
/// Return the length up to the first NUL byte or full slice length.
fn c_string_len(text: &[u8]) -> usize {
let mut len = 0usize;
while len < text.len() && text[len] != 0 {
len += 1;
}
len
}
#[cfg(test)]
mod tests {
// Import all parent module items
use super::*;
/// Valid ports are zero and one.
#[test]
fn valid_ports_are_zero_and_one() {
assert!(is_valid_port(0));
assert!(is_valid_port(1));
assert!(!is_valid_port(2));
}
/// Clear rx buffer zeroes all bytes.
#[test]
fn clear_rx_buffer_zeroes_all_bytes() {
let mut rx = [1u8, 2, 3, 4];
clear_rx_buffer(&mut rx);
assert_eq!(rx, [0u8; 4]);
}
/// Copy transfer result copies common prefix.
#[test]
fn copy_transfer_result_copies_common_prefix() {
let src = b"abcd";
let mut dst = [0u8; 3];
copy_transfer_result(src, &mut dst);
assert_eq!(&dst, b"abc");
}
/// Format tx line omits trailing nul.
#[test]
fn format_tx_line_omits_trailing_nul() {
let mut buf = [0u8; 32];
let n = format_tx_line(&mut buf, TX_MESSAGE);
assert_eq!(&buf[..n], b"TX: SPI loopback OK\r\n");
}
/// Format rx line adds blank line.
#[test]
fn format_rx_line_adds_blank_line() {
let mut buf = [0u8; 32];
let n = format_rx_line(&mut buf, TX_MESSAGE);
assert_eq!(&buf[..n], b"RX: SPI loopback OK\r\n\r\n");
}
/// C string len stops at nul.
#[test]
fn c_string_len_stops_at_nul() {
assert_eq!(c_string_len(b"abc\0xyz"), 3);
}
}