wio_terminal 0.7.3

Board support crate for the Seeed Studio Wio Terminal
Documentation
#![no_std]
#![no_main]
#![allow(static_mut_refs)]

use embedded_graphics as eg;
use panic_halt as _;
use wio_terminal as wio;

use cortex_m::peripheral::NVIC;
use wio::entry;
use wio::hal::adc::{FreeRunning, InterruptAdc};
use wio::hal::clock::GenericClockController;
use wio::hal::delay::Delay;
use wio::pac::{interrupt, ADC1};
use wio::pac::{CorePeripherals, Peripherals};
use wio::prelude::*;

use core::fmt::Write;
use eg::mono_font::{ascii::FONT_6X12, MonoTextStyle};
use eg::pixelcolor::Rgb565;
use eg::prelude::*;
use eg::primitives::{PrimitiveStyleBuilder, Rectangle};
use eg::text::{Baseline, Text};
use heapless::String;

use heapless::spsc::Queue;
struct Ctx {
    adc: InterruptAdc<ADC1, ConversionMode>,
    samples: Queue<u16, 8>,
}
static mut CTX: Option<Ctx> = None;

type ConversionMode = FreeRunning;
// You also have to uncomment the line which calls start_conversion function in
// the main loop. type ConversionMode = SingleConversion;

#[entry]
fn main() -> ! {
    let mut peripherals = Peripherals::take().unwrap();
    let core = CorePeripherals::take().unwrap();

    let mut clocks = GenericClockController::with_external_32kosc(
        peripherals.GCLK,
        &mut peripherals.MCLK,
        &mut peripherals.OSC32KCTRL,
        &mut peripherals.OSCCTRL,
        &mut peripherals.NVMCTRL,
    );
    let mut delay = Delay::new(core.SYST, &mut clocks);
    let sets = wio::Pins::new(peripherals.PORT).split();

    // Set up the display so we can log our progress.
    let (display, _backlight) = sets
        .display
        .init(
            &mut clocks,
            peripherals.SERCOM7,
            &mut peripherals.MCLK,
            58.MHz(),
            &mut delay,
        )
        .unwrap();
    let mut terminal = Terminal::new(display);
    let mut textbuffer = String::<256>::new();

    let mut user_led = sets.user_led.into_push_pull_output();
    user_led.set_high().unwrap();

    // Construct an InterruptAdc with free-running mode.
    let (mut microphone_adc, mut microphone_pin) = {
        let (adc, pin) = sets
            .microphone
            .init(peripherals.ADC1, &mut clocks, &mut peripherals.MCLK);
        let interrupt_adc: InterruptAdc<_, ConversionMode> = InterruptAdc::from(adc);
        (interrupt_adc, pin)
    };

    microphone_adc.start_conversion(&mut microphone_pin);

    unsafe {
        CTX = Some(Ctx {
            adc: microphone_adc,
            samples: Queue::new(),
        });
    }
    let mut consumer = unsafe { CTX.as_mut().unwrap().samples.split().1 };

    terminal.write_str("min,max,avg\n");

    unsafe {
        // Enable ADC1 result ready interrupt.
        NVIC::unmask(interrupt::ADC1_RESRDY);
    }
    user_led.set_low().unwrap();

    loop {
        let mut min = core::f32::INFINITY;
        let mut max = core::f32::NEG_INFINITY;
        let mut sum = 0f32;
        // Though the ADC sampling rate is set to 250[kSPS] according to the comment in
        // the adc.rs, actual sampling rate seems 83.333[kSPS], which is 1/3 of
        // expected sampling rate.
        let count_max = 83333;
        for _count in 0..count_max {
            // Uncomment if you use single conversion mode.
            // unsafe { CTX.as_mut().unwrap().adc.start_conversion(&mut microphone_pin); }
            let value = loop {
                if let Some(value) = consumer.dequeue() {
                    break value as f32;
                }
            };
            if value < min {
                min = value;
            }
            if max < value {
                max = value
            }
            sum += value;
        }
        textbuffer.clear();
        writeln!(textbuffer, "{},{},{}", min, max, sum / count_max as f32).unwrap();
        terminal.write_str(textbuffer.as_str());
    }
}

#[interrupt]
fn ADC1_RESRDY() {
    unsafe {
        let ctx = CTX.as_mut().unwrap();
        let mut producer = ctx.samples.split().0;
        if let Some(sample) = ctx.adc.service_interrupt_ready() {
            producer.enqueue_unchecked(sample);
        }
    }
}

/// Handly helper for logging text to the screen.
struct Terminal<'a> {
    text_style: MonoTextStyle<'a, Rgb565>,
    cursor: Point,
    display: wio::LCD,
}

impl<'a> Terminal<'a> {
    pub fn new(mut display: wio::LCD) -> Self {
        // Clear the screen.
        let style = PrimitiveStyleBuilder::new()
            .fill_color(Rgb565::BLACK)
            .build();
        let backdrop =
            Rectangle::with_corners(Point::new(0, 0), Point::new(320, 320)).into_styled(style);
        backdrop.draw(&mut display).ok().unwrap();

        Self {
            text_style: MonoTextStyle::new(&FONT_6X12, Rgb565::WHITE),
            cursor: Point::new(0, 0),
            display,
        }
    }

    pub fn write_str(&mut self, str: &str) {
        for character in str.chars() {
            self.write_character(character);
        }
    }

    pub fn write_character(&mut self, c: char) {
        if self.cursor.x >= 320 || c == '\n' {
            self.cursor = Point::new(0, self.cursor.y + FONT_6X12.character_size.height as i32);
        }
        if self.cursor.y >= 240 {
            // Clear the screen.
            let style = PrimitiveStyleBuilder::new()
                .fill_color(Rgb565::BLACK)
                .build();
            let backdrop =
                Rectangle::with_corners(Point::new(0, 0), Point::new(320, 320)).into_styled(style);
            backdrop.draw(&mut self.display).ok().unwrap();
            self.cursor = Point::new(0, 0);
        }

        if c != '\n' {
            let mut buf = [0u8; 8];
            Text::with_baseline(
                c.encode_utf8(&mut buf),
                self.cursor,
                self.text_style,
                Baseline::Top,
            )
            .draw(&mut self.display)
            .ok()
            .unwrap();

            self.cursor.x += (FONT_6X12.character_size.width + FONT_6X12.character_spacing) as i32;
        }
    }
}