262 lines
7.6 KiB
Rust
262 lines
7.6 KiB
Rust
use stm32f4xx_hal::{
|
|
gpio::{self, Alternate, Input, Output, PushPull},
|
|
i2c::I2c,
|
|
pac::{I2C1, SPI1, SYST, TIM2},
|
|
prelude::*,
|
|
qei, rcc,
|
|
spi::{self, Spi},
|
|
};
|
|
|
|
use st7735_lcd::{Orientation, ST7735};
|
|
|
|
use embedded_graphics::{
|
|
mono_font::MonoTextStyle,
|
|
pixelcolor::Rgb565,
|
|
prelude::*,
|
|
primitives::rectangle::Rectangle,
|
|
primitives::{Line, PrimitiveStyle},
|
|
text::Text,
|
|
};
|
|
use profont::PROFONT_14_POINT;
|
|
|
|
use microfft::complex::cfft_128;
|
|
use num::Complex;
|
|
use num_traits::{float::Float, Pow};
|
|
|
|
use core::fmt::Write;
|
|
|
|
use crate::si5153;
|
|
|
|
type EncoderA = gpio::Pin<'A', 0, Input>;
|
|
type EncoderB = gpio::Pin<'A', 1, Input>;
|
|
type EncoderButton = gpio::Pin<'A', 5, Input>;
|
|
|
|
type DisplaySPI = Spi<SPI1>;
|
|
type DisplayRST = gpio::Pin<'A', 11, Output>;
|
|
type DisplayDC = gpio::Pin<'A', 12, Output>;
|
|
type DisplayLed = gpio::Pin<'A', 10, Output<PushPull>>;
|
|
type DisplayCS = gpio::Pin<'A', 15, Output<PushPull>>;
|
|
type DisplaySCK = gpio::Pin<'B', 3, Alternate<5>>;
|
|
type DisplayMOSI = gpio::Pin<'B', 5, Alternate<5>>;
|
|
|
|
pub struct UI {
|
|
disp_led: DisplayLed,
|
|
disp_cs: DisplayCS,
|
|
disp: ST7735<DisplaySPI, DisplayDC, DisplayRST>,
|
|
row_pos: u16,
|
|
row_buffer: [Complex<f32>; 128],
|
|
row_buffer_count: usize,
|
|
|
|
encoder: qei::Qei<TIM2>,
|
|
encoder_button: EncoderButton,
|
|
last_encoder_button: bool,
|
|
|
|
last_encoder_pos: u32,
|
|
carrier_freq: u32,
|
|
cursor_pos: u32,
|
|
|
|
initial_render: bool,
|
|
}
|
|
|
|
impl UI {
|
|
pub fn setup(
|
|
clocks: &rcc::Clocks,
|
|
|
|
enc_a: EncoderA,
|
|
enc_b: EncoderB,
|
|
tim2: TIM2,
|
|
encoder_button: EncoderButton,
|
|
|
|
disp_rst: DisplayRST,
|
|
disp_dc: DisplayDC,
|
|
mut disp_led: DisplayLed,
|
|
mut disp_cs: DisplayCS,
|
|
disp_sck: DisplaySCK,
|
|
disp_mosi: DisplayMOSI,
|
|
spi1: SPI1,
|
|
syst: SYST,
|
|
) -> UI {
|
|
let encoder = qei::Qei::new(tim2, (enc_a, enc_b));
|
|
defmt::info!("[UI] Encoder Setup done");
|
|
|
|
disp_led.set_high();
|
|
disp_cs.set_low();
|
|
|
|
let spi1 = Spi::new(
|
|
spi1,
|
|
(disp_sck, spi::NoMiso::new(), disp_mosi),
|
|
spi::Mode {
|
|
polarity: spi::Polarity::IdleLow,
|
|
phase: spi::Phase::CaptureOnFirstTransition,
|
|
},
|
|
16.MHz(),
|
|
&clocks,
|
|
);
|
|
|
|
let mut disp = ST7735::new(spi1, disp_dc, disp_rst, true, false, 160, 128);
|
|
|
|
let mut delay = syst.delay(&clocks);
|
|
|
|
disp.init(&mut delay).unwrap();
|
|
disp.set_orientation(&Orientation::Landscape).unwrap();
|
|
disp.clear(Rgb565::BLACK).unwrap();
|
|
|
|
defmt::info!("[UI] Display setup done");
|
|
|
|
UI {
|
|
disp_led,
|
|
disp_cs,
|
|
disp,
|
|
row_pos: 0,
|
|
row_buffer: [Complex::<f32>::new(0.0, 0.0); 128],
|
|
row_buffer_count: 0,
|
|
encoder,
|
|
encoder_button: encoder_button,
|
|
last_encoder_pos: 0,
|
|
last_encoder_button: false,
|
|
|
|
carrier_freq: 7_100_000,
|
|
cursor_pos: 3,
|
|
|
|
initial_render: true,
|
|
}
|
|
}
|
|
|
|
pub fn update_display(
|
|
&mut self,
|
|
mut row: [Complex<f32>; 128],
|
|
pll: &mut si5153::Si5153<I2c<I2C1>>,
|
|
i2c: &mut I2c<I2C1>,
|
|
) {
|
|
let buffers_per_row = 4;
|
|
|
|
let row = cfft_128(&mut row);
|
|
|
|
for idx in 0..row.len() {
|
|
self.row_buffer[idx] += row[idx] / buffers_per_row as f32;
|
|
}
|
|
|
|
self.row_buffer_count += 1;
|
|
|
|
if self.row_buffer_count > buffers_per_row {
|
|
self.row_buffer_count = 0;
|
|
|
|
let gradient = colorous::TURBO;
|
|
|
|
for idx in 0..128 {
|
|
let intens: f32 = self.row_buffer[idx].re.pow(2) + self.row_buffer[idx].im.pow(2);
|
|
|
|
let log_intens = intens.log10() / 2.0 * 10.0;
|
|
let norm_intens = (log_intens + 18.0) / 18.0;
|
|
|
|
let color = gradient.eval_continuous(norm_intens as f64);
|
|
|
|
let x = if idx < 64 { 64 + idx } else { idx - 64 };
|
|
Pixel(
|
|
Point::new(32 + x as i32, 28 + self.row_pos as i32),
|
|
Rgb565::new(color.r >> 3, color.g >> 2, color.b >> 3),
|
|
)
|
|
.draw(&mut self.disp)
|
|
.unwrap();
|
|
|
|
self.row_buffer[idx] = Complex::new(0.0, 0.0);
|
|
}
|
|
|
|
self.row_pos = (self.row_pos + 1) % 100;
|
|
Line::new(
|
|
Point::new(32, 28 + self.row_pos as i32),
|
|
Point::new(159, 28 + self.row_pos as i32),
|
|
)
|
|
.into_styled(PrimitiveStyle::with_stroke(Rgb565::BLACK, 1))
|
|
.draw(&mut self.disp)
|
|
.unwrap();
|
|
|
|
defmt::info!("Position is {}", self.row_pos);
|
|
}
|
|
|
|
if self.initial_render {
|
|
Rectangle::new(Point::new(0, 0), Size::new(30, 24))
|
|
.into_styled(PrimitiveStyle::with_fill(Rgb565::GREEN))
|
|
.draw(&mut self.disp)
|
|
.unwrap();
|
|
|
|
let text_style = MonoTextStyle::new(&PROFONT_14_POINT, Rgb565::BLACK);
|
|
Text::new("RX", Point::new(6, 16), text_style)
|
|
.draw(&mut self.disp)
|
|
.unwrap();
|
|
}
|
|
|
|
let encoder_pos = self.encoder.count();
|
|
let diff = encoder_pos.wrapping_sub(self.last_encoder_pos) as i32 / 4;
|
|
|
|
if diff != 0 || self.initial_render {
|
|
if diff != 0 {
|
|
let increment = 10.pow(self.cursor_pos);
|
|
self.carrier_freq = (self.carrier_freq as i32 + diff * increment) as u32;
|
|
|
|
pll.set_pll_freq(i2c, si5153::PLL::A, self.carrier_freq * 100);
|
|
pll.set_ms_freq(i2c, si5153::Multisynth::MS0, self.carrier_freq);
|
|
pll.set_ms_freq(i2c, si5153::Multisynth::MS1, self.carrier_freq);
|
|
}
|
|
|
|
Rectangle::new(Point::new(32, 0), Size::new(160, 18))
|
|
.into_styled(PrimitiveStyle::with_fill(Rgb565::BLACK))
|
|
.draw(&mut self.disp)
|
|
.unwrap();
|
|
|
|
let freq_str = format_frequency(self.carrier_freq);
|
|
let text_style = MonoTextStyle::new(&PROFONT_14_POINT, Rgb565::WHITE);
|
|
Text::new(&freq_str, Point::new(32, 16), text_style)
|
|
.draw(&mut self.disp)
|
|
.unwrap();
|
|
|
|
self.last_encoder_pos = encoder_pos;
|
|
|
|
defmt::info!("Carrier freq is {}", self.carrier_freq);
|
|
}
|
|
|
|
let encoder_button = self.encoder_button.is_high();
|
|
if encoder_button && !self.last_encoder_button {
|
|
self.cursor_pos += 1;
|
|
if self.cursor_pos >= 6 {
|
|
self.cursor_pos = 0;
|
|
}
|
|
|
|
Rectangle::new(Point::new(32, 18), Size::new(160, 2))
|
|
.into_styled(PrimitiveStyle::with_fill(Rgb565::BLACK))
|
|
.draw(&mut self.disp)
|
|
.unwrap();
|
|
|
|
let str_pos = if self.cursor_pos >= 3 {
|
|
self.cursor_pos + 1
|
|
} else {
|
|
self.cursor_pos
|
|
};
|
|
|
|
Rectangle::new(
|
|
Point::new(32 + 90 - 10 * str_pos as i32, 18),
|
|
Size::new(8, 2),
|
|
)
|
|
.into_styled(PrimitiveStyle::with_fill(Rgb565::WHITE))
|
|
.draw(&mut self.disp)
|
|
.unwrap();
|
|
}
|
|
self.last_encoder_button = encoder_button;
|
|
|
|
self.initial_render = false;
|
|
}
|
|
}
|
|
|
|
fn format_frequency(freq: u32) -> arrayvec::ArrayString<10> {
|
|
let hz = freq % 1000;
|
|
let rest = freq / 1000;
|
|
let khz = rest % 1000;
|
|
let rest = rest / 1000;
|
|
let mhz = rest % 1000;
|
|
|
|
let mut freq_str = arrayvec::ArrayString::new();
|
|
write!(freq_str, "{:02}.{:03}.{:03}", mhz, khz, hz).unwrap();
|
|
|
|
freq_str
|
|
}
|