project; add rust

This commit is contained in:
David Westgate 2024-12-11 05:59:20 -08:00
parent 3c4ddaee2a
commit 7af983b289
5 changed files with 269 additions and 1 deletions

9
.gitignore vendored
View File

@ -1,2 +1,9 @@
target/ target/
code/project/*/ code/project/audio/
code/project/log/
code/project/machine/
code/project/microbit/
code/project/music/
code/project/neopixel/
code/project/radio/
code/project/speech/

45
code/project/Cargo.toml Normal file
View File

@ -0,0 +1,45 @@
[package]
name = "project"
version = "0.1.0"
edition = "2021"
[dependencies]
cortex-m-rt = "0.7.5"
embedded-hal = "1.0.0"
libm = "0.2.11"
microbit-v2 = "0.15.1"
rtt-target = "0.4"
[dependencies.num-traits]
version = "0.2.15"
default-features = false
features = ["libm"]
[dependencies.ordered-float]
version = "3.0.0"
default_features = false
[dependencies.num-complex]
version = "0.4.2"
default-features = false
features = ["libm"]
[dependencies.microfft]
version = "0.5.0"
default-features = false
features = ["size-64"]
[dependencies.panic-rtt-target]
version = "0.1"
features = ["cortex-m"]
# This works around old versions in the `microbit-v2`
# crate. You don't have to use this crate, just linking
# against it is sufficient.
[dependencies.cortex-m]
version = "0.7"
features = ["inline-asm", "critical-section-single-core"]
[dependencies.critical-section-lock-mut]
git = "https://github.com/pdx-cs-rust-embedded/critical-section-lock-mut"

11
code/project/Embed.toml Normal file
View File

@ -0,0 +1,11 @@
[default.general]
chip = "nrf52833_xxAA"
[default.reset]
halt_afterwards = false
[default.rtt]
enabled = true
[default.gdb]
enabled = false

8
code/project/SETUP.md Normal file
View File

@ -0,0 +1,8 @@
```
rustup target add thumbv7em-none-eabihf
rustup component add llvm-tools
cargo install cargo-binutils
cargo install probe-run
cargo install cargo-embed
```

197
code/project/src/main.rs Normal file
View File

@ -0,0 +1,197 @@
#![no_std]
#![no_main]
// Several lines of code are adapted or copied from Microbit-spectrum, by Bart Massey. These will be noted
// https://github.com/BartMassey/microbit-spectrum/blob/main/src/main.rs
use core::f32::consts::PI;
use cortex_m_rt::entry;
use critical_section_lock_mut::LockMut;
use embedded_hal::digital::OutputPin;
use libm::{floorf, log2f};
use microbit::{
board::Board,
display::nonblocking::{BitImage, Display},
hal::{
gpio::{p0::P0_05, Floating, Input},
saadc::SaadcConfig,
timer::Timer,
Saadc,
},
pac::TIMER1,
};
use microfft::real::rfft_64 as rfft;
use num_complex::Complex;
use num_traits::Float;
use ordered_float::NotNan;
use panic_rtt_target as _;
use rtt_target::{rprintln, rtt_init_print};
// Microbit-spectrum
const FFT_WIDTH: usize = 64;
/// ADC is signed; it is configured for a sample precision of 10 bits. Must match below.
const ADC_MAX: usize = 512;
const NOTES: [&str; 12] = [
"A", "B♭", "B", "C", "D♭", "D", "E♭", "E", "F", "G♭", "G", "A♭",
];
static DISPLAY: LockMut<Display<TIMER1>> = LockMut::new();
const DEFAULT_BITMAP: [[u8; 5]; 5] = [
[9, 0, 0, 0, 9],
[0, 9, 0, 9, 0],
[0, 0, 9, 0, 0],
[0, 9, 0, 9, 0],
[9, 0, 0, 9, 9],
];
// Bit representations of the 7 note, A to G in order
const NOTE_IMAGE_BITMAPS: [[[u8; 5]; 5]; 7] = [
[
[0, 9, 9, 0, 0],
[9, 0, 0, 9, 0],
[9, 9, 9, 9, 0],
[9, 0, 0, 9, 0],
[9, 0, 0, 9, 0],
],
[
[9, 9, 9, 0, 0],
[9, 0, 0, 9, 0],
[9, 9, 9, 0, 0],
[9, 0, 0, 9, 0],
[9, 9, 9, 0, 0],
],
[
[0, 9, 9, 9, 0],
[9, 0, 0, 0, 0],
[9, 0, 0, 0, 0],
[9, 0, 0, 0, 0],
[0, 9, 9, 9, 0],
],
[
[9, 9, 9, 0, 0],
[9, 0, 0, 9, 0],
[9, 0, 0, 9, 0],
[9, 0, 0, 9, 0],
[9, 9, 9, 0, 0],
],
[
[9, 9, 9, 9, 0],
[9, 0, 0, 0, 0],
[9, 9, 9, 0, 0],
[9, 0, 0, 0, 0],
[9, 9, 9, 9, 0],
],
[
[9, 9, 9, 9, 0],
[9, 0, 0, 0, 0],
[9, 9, 9, 0, 0],
[9, 0, 0, 0, 0],
[9, 0, 0, 0, 0],
],
[
[0, 9, 9, 9, 0],
[9, 0, 0, 0, 0],
[9, 0, 9, 9, 0],
[9, 0, 0, 9, 0],
[0, 9, 9, 9, 0],
],
];
fn read_samples(saadc: &mut Saadc, mic_in: &mut P0_05<Input<Floating>>) -> [i16; FFT_WIDTH] {
let mut result = [0; FFT_WIDTH];
for r in &mut result {
*r = saadc.read_channel(mic_in).unwrap();
}
result
}
// Microbit-spectrum - read function
fn samples_to_freq(
samples: [i16; FFT_WIDTH],
window: [f32; FFT_WIDTH],
) -> [Complex<f32>; FFT_WIDTH / 2] {
const fn nn(f: f32) -> NotNan<f32> {
unsafe { NotNan::new_unchecked(f) }
}
let mut sample_buf = [0.0f32; FFT_WIDTH];
for (i, s) in sample_buf.iter_mut().enumerate() {
*s = samples[i].abs() as f32 / ADC_MAX as f32
}
let dc = sample_buf.iter().copied().sum::<f32>() / sample_buf.len() as f32;
let peak = sample_buf
.iter()
.copied()
.map(nn)
.max()
.unwrap()
.into_inner();
for (i, s) in sample_buf.iter_mut().enumerate() {
*s = window[i] * (*s - dc) / peak;
}
// Take the FFT and process the result.
let freqs: &mut [Complex<f32>; FFT_WIDTH / 2] = rfft(&mut sample_buf);
*freqs
}
fn get_dominant_freq(_freqs: [Complex<f32>; FFT_WIDTH / 2]) -> f32 {
// todo!();
0.0
}
fn get_note(freq: f32) -> (usize, f32) {
if freq > 0.0 {
let note_num = log2f(freq / 440.0) * 12.0;
let octave = floorf(4.0 + note_num / 12.0);
let rel_note = (note_num % 12.0) as usize;
return (rel_note, octave);
}
(0, 0.0)
}
fn get_image(noctive: (usize, f32)) -> [[u8; 5]; 5] {
let (note, _octive) = noctive; // TODO - Add octive and flat modifier to display
if note > 0 {
NOTE_IMAGE_BITMAPS[note]
} else {
DEFAULT_BITMAP
}
}
#[entry]
fn init() -> ! {
rtt_init_print!();
let board: Board = Board::take().unwrap();
let mut timer = Timer::new(board.TIMER0);
let display: Display<TIMER1> = Display::new(board.TIMER1, board.display_pins);
DISPLAY.init(display);
let mut mic_in = board.microphone_pins.mic_in;
let mut mic_run = board.microphone_pins.mic_run;
let _ = mic_run.set_high();
let saadc_config: SaadcConfig = SaadcConfig::default();
let mut saadc: Saadc = Saadc::new(board.ADC, saadc_config);
let mut window = [0.0f32; FFT_WIDTH];
// Use a Hann window, which is easy to compute and reasonably good. Source: microbit-spectrum
for (n, w) in window.iter_mut().enumerate() {
let s: f32 = (PI * n as f32 / (FFT_WIDTH - 1) as f32).sin();
*w = s * s;
}
loop {
let samples: [i16; FFT_WIDTH] = read_samples(&mut saadc, &mut mic_in);
let freqs: [Complex<f32>; FFT_WIDTH / 2] = samples_to_freq(samples, window);
let dominant_freq: f32 = get_dominant_freq(freqs);
let noctive: (usize, f32) = get_note(dominant_freq); //note + octive tuple
let bitmap: [[u8; 5]; 5] = get_image(noctive);
DISPLAY.with_lock(|display: &mut Display<TIMER1>| {
display.show(&BitImage::new(&bitmap)); // XXX Not the most memory efficient - Improve
});
rprintln!("{}", NOTES[noctive.0]);
timer.delay(400);
}
}