fw-rust: Make Si5351 useable
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
In order to make the Si5351 configurable add some needed assets, create the ability to print the frequency with selected digits and add a channel screen.
This commit is contained in:
parent
7f14974146
commit
c36581e703
5 changed files with 430 additions and 119 deletions
|
@ -146,6 +146,12 @@ const SYM_E: [&[u8]; 2] = [
|
||||||
&[0x3F, 0x3F, 0x31, 0x31, 0x30, 0x30],
|
&[0x3F, 0x3F, 0x31, 0x31, 0x30, 0x30],
|
||||||
];
|
];
|
||||||
|
|
||||||
|
// TODO: Use https://github.com/rust-lang/rust/issues/85077 when stabilized
|
||||||
|
const SYM_F: [&[u8]; 2] = [
|
||||||
|
&[0xFC, 0xFC, 0x8C, 0x8C, 0x0C, 0x0C],
|
||||||
|
&[0x3F, 0x3F, 0x01, 0x01, 0x00, 0x00],
|
||||||
|
];
|
||||||
|
|
||||||
// TODO: Use https://github.com/rust-lang/rust/issues/85077 when stabilized
|
// TODO: Use https://github.com/rust-lang/rust/issues/85077 when stabilized
|
||||||
const SYM_G: [&[u8]; 2] = [
|
const SYM_G: [&[u8]; 2] = [
|
||||||
&[0xF8, 0xFC, 0x0C, 0x0C, 0x3C, 0x38],
|
&[0xF8, 0xFC, 0x0C, 0x0C, 0x3C, 0x38],
|
||||||
|
@ -191,6 +197,12 @@ const SYM_P: [&[u8]; 2] = [
|
||||||
&[0x3F, 0x3F, 0x01, 0x01, 0x01, 0x00],
|
&[0x3F, 0x3F, 0x01, 0x01, 0x01, 0x00],
|
||||||
];
|
];
|
||||||
|
|
||||||
|
// TODO: Use https://github.com/rust-lang/rust/issues/85077 when stabilized
|
||||||
|
const SYM_Q: [&[u8]; 2] = [
|
||||||
|
&[0xF8, 0xFC, 0x0C, 0x0C, 0xFC, 0xF8],
|
||||||
|
&[0x1F, 0x3F, 0x30, 0x38, 0x7F, 0x6F],
|
||||||
|
];
|
||||||
|
|
||||||
// TODO: Use https://github.com/rust-lang/rust/issues/85077 when stabilized
|
// TODO: Use https://github.com/rust-lang/rust/issues/85077 when stabilized
|
||||||
const SYM_R: [&[u8]; 2] = [
|
const SYM_R: [&[u8]; 2] = [
|
||||||
&[0xFC, 0xFC, 0x8C, 0x8C, 0xFC, 0xF8],
|
&[0xFC, 0xFC, 0x8C, 0x8C, 0xFC, 0xF8],
|
||||||
|
@ -215,6 +227,12 @@ const SYM_U: [&[u8]; 2] = [
|
||||||
&[0x1F, 0x3F, 0x30, 0x30, 0x3F, 0x1F],
|
&[0x1F, 0x3F, 0x30, 0x30, 0x3F, 0x1F],
|
||||||
];
|
];
|
||||||
|
|
||||||
|
// TODO: Use https://github.com/rust-lang/rust/issues/85077 when stabilized
|
||||||
|
const SYM_UNDERSCORE: [&[u8]; 2] = [
|
||||||
|
&[0x00, 0x00, 0x00, 0x00, 0x00],
|
||||||
|
&[0x30, 0x30, 0x30, 0x30, 0x30],
|
||||||
|
];
|
||||||
|
|
||||||
// TODO: Use https://github.com/rust-lang/rust/issues/85077 when stabilized
|
// TODO: Use https://github.com/rust-lang/rust/issues/85077 when stabilized
|
||||||
const SYM_INVALID: [&[u8]; 2] = [
|
const SYM_INVALID: [&[u8]; 2] = [
|
||||||
&[0x80, 0xE0, 0x98, 0xCC, 0x4C, 0x18, 0xE0, 0x80],
|
&[0x80, 0xE0, 0x98, 0xCC, 0x4C, 0x18, 0xE0, 0x80],
|
||||||
|
@ -223,54 +241,54 @@ const SYM_INVALID: [&[u8]; 2] = [
|
||||||
|
|
||||||
// TODO: Use https://github.com/rust-lang/rust/issues/85077 when stabilized
|
// TODO: Use https://github.com/rust-lang/rust/issues/85077 when stabilized
|
||||||
pub const SYMBOL_TABLE: [&[&[u8]; 2]; 50] = [
|
pub const SYMBOL_TABLE: [&[&[u8]; 2]; 50] = [
|
||||||
&SYM_DOT, // '.'
|
&SYM_DOT, // '.'
|
||||||
&SYM_INVALID, // '/'
|
&SYM_INVALID, // '/'
|
||||||
&SYM_0, // '0'
|
&SYM_0, // '0'
|
||||||
&SYM_1, // '1'
|
&SYM_1, // '1'
|
||||||
&SYM_2, // '2'
|
&SYM_2, // '2'
|
||||||
&SYM_3, // '3'
|
&SYM_3, // '3'
|
||||||
&SYM_4, // '4'
|
&SYM_4, // '4'
|
||||||
&SYM_5, // '5'
|
&SYM_5, // '5'
|
||||||
&SYM_6, // '6'
|
&SYM_6, // '6'
|
||||||
&SYM_7, // '7'
|
&SYM_7, // '7'
|
||||||
&SYM_8, // '8'
|
&SYM_8, // '8'
|
||||||
&SYM_9, // '9'
|
&SYM_9, // '9'
|
||||||
&SYM_COLON, // ':'
|
&SYM_COLON, // ':'
|
||||||
&SYM_INVALID, // ';'
|
&SYM_INVALID, // ';'
|
||||||
&SYM_INVALID, // '<'
|
&SYM_INVALID, // '<'
|
||||||
&SYM_INVALID, // '='
|
&SYM_INVALID, // '='
|
||||||
&SYM_INVALID, // '>'
|
&SYM_INVALID, // '>'
|
||||||
&SYM_INVALID, // '?'
|
&SYM_INVALID, // '?'
|
||||||
&SYM_INVALID, // '@'
|
&SYM_INVALID, // '@'
|
||||||
&SYM_A, // 'A'
|
&SYM_A, // 'A'
|
||||||
&SYM_B, // 'B'
|
&SYM_B, // 'B'
|
||||||
&SYM_C, // 'C'
|
&SYM_C, // 'C'
|
||||||
&SYM_INVALID, // 'D'
|
&SYM_INVALID, // 'D'
|
||||||
&SYM_E, // 'E'
|
&SYM_E, // 'E'
|
||||||
&SYM_INVALID, // 'F'
|
&SYM_F, // 'F'
|
||||||
&SYM_G, // 'G'
|
&SYM_G, // 'G'
|
||||||
&SYM_H, // 'H'
|
&SYM_H, // 'H'
|
||||||
&SYM_I, // 'I'
|
&SYM_I, // 'I'
|
||||||
&SYM_INVALID, // 'J'
|
&SYM_INVALID, // 'J'
|
||||||
&SYM_K, // 'K'
|
&SYM_K, // 'K'
|
||||||
&SYM_L, // 'L'
|
&SYM_L, // 'L'
|
||||||
&SYM_INVALID, // 'M'
|
&SYM_INVALID, // 'M'
|
||||||
&SYM_N, // 'N'
|
&SYM_N, // 'N'
|
||||||
&SYM_O, // 'O'
|
&SYM_O, // 'O'
|
||||||
&SYM_P, // 'P'
|
&SYM_P, // 'P'
|
||||||
&SYM_INVALID, // 'Q'
|
&SYM_Q, // 'Q'
|
||||||
&SYM_R, // 'R'
|
&SYM_R, // 'R'
|
||||||
&SYM_S, // 'S'
|
&SYM_S, // 'S'
|
||||||
&SYM_T, // 'T'
|
&SYM_T, // 'T'
|
||||||
&SYM_U, // 'U'
|
&SYM_U, // 'U'
|
||||||
&SYM_INVALID, // 'V'
|
&SYM_INVALID, // 'V'
|
||||||
&SYM_INVALID, // 'W'
|
&SYM_INVALID, // 'W'
|
||||||
&SYM_INVALID, // 'X'
|
&SYM_INVALID, // 'X'
|
||||||
&SYM_INVALID, // 'Y'
|
&SYM_INVALID, // 'Y'
|
||||||
&SYM_INVALID, // 'Z'
|
&SYM_INVALID, // 'Z'
|
||||||
&SYM_INVALID, // '['
|
&SYM_INVALID, // '['
|
||||||
&SYM_INVALID, // '\'
|
&SYM_INVALID, // '\'
|
||||||
&SYM_INVALID, // ']'
|
&SYM_INVALID, // ']'
|
||||||
&SYM_INVALID, // '^'
|
&SYM_INVALID, // '^'
|
||||||
&SYM_INVALID, // '_'
|
&SYM_UNDERSCORE, // '_'
|
||||||
];
|
];
|
||||||
|
|
|
@ -268,6 +268,79 @@ impl Lcd {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn print_freq_digit(&mut self, segment: u8, page: u8, data: u32, digit: u8) {
|
||||||
|
let mut delay = Delay::<MHz8>::new();
|
||||||
|
|
||||||
|
let mut array = [0usize; 9];
|
||||||
|
for (i, item) in array.iter_mut().enumerate() {
|
||||||
|
*item = (((data / 10_u32.pow(i.try_into().unwrap())) % 10) + 2)
|
||||||
|
.try_into()
|
||||||
|
.unwrap();
|
||||||
|
}
|
||||||
|
array.reverse();
|
||||||
|
let digit = 8 - digit;
|
||||||
|
|
||||||
|
for i in 0..2 {
|
||||||
|
self.move_cursor(segment, page + i);
|
||||||
|
|
||||||
|
// TODO: This delay fixes issues, try find a better solution
|
||||||
|
delay.delay_ms(1_u8);
|
||||||
|
self.cd.set_high();
|
||||||
|
|
||||||
|
block!(self.spi.send(0x00)).unwrap();
|
||||||
|
block!(self.spi.send(0x00)).unwrap();
|
||||||
|
for j in 0..3 {
|
||||||
|
if j == digit {
|
||||||
|
for segment in SYMBOL_TABLE[array[j as usize]][i as usize] {
|
||||||
|
block!(self.spi.send(!*segment)).unwrap();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for segment in SYMBOL_TABLE[array[j as usize]][i as usize] {
|
||||||
|
block!(self.spi.send(*segment)).unwrap();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
block!(self.spi.send(0x00)).unwrap();
|
||||||
|
}
|
||||||
|
for segment in SYMBOL_TABLE[0][i as usize] {
|
||||||
|
block!(self.spi.send(*segment)).unwrap();
|
||||||
|
}
|
||||||
|
block!(self.spi.send(0x00)).unwrap();
|
||||||
|
for j in 3..6 {
|
||||||
|
if j == digit {
|
||||||
|
for segment in SYMBOL_TABLE[array[j as usize]][i as usize] {
|
||||||
|
block!(self.spi.send(!*segment)).unwrap();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for segment in SYMBOL_TABLE[array[j as usize]][i as usize] {
|
||||||
|
block!(self.spi.send(*segment)).unwrap();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
block!(self.spi.send(0x00)).unwrap();
|
||||||
|
}
|
||||||
|
for segment in SYMBOL_TABLE[0][i as usize] {
|
||||||
|
block!(self.spi.send(*segment)).unwrap();
|
||||||
|
}
|
||||||
|
block!(self.spi.send(0x00)).unwrap();
|
||||||
|
for j in 6..9 {
|
||||||
|
if j == digit {
|
||||||
|
for segment in SYMBOL_TABLE[array[j as usize]][i as usize] {
|
||||||
|
block!(self.spi.send(!*segment)).unwrap();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for segment in SYMBOL_TABLE[array[j as usize]][i as usize] {
|
||||||
|
block!(self.spi.send(*segment)).unwrap();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
block!(self.spi.send(0x00)).unwrap();
|
||||||
|
}
|
||||||
|
block!(self.spi.send(0x00)).unwrap();
|
||||||
|
|
||||||
|
// TODO: This delay fixes issues, try find a better solution
|
||||||
|
delay.delay_ms(1_u8);
|
||||||
|
self.cd.set_low();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn print_icon(&mut self, segment: u8, page: u8, symbol: &[u8]) {
|
pub fn print_icon(&mut self, segment: u8, page: u8, symbol: &[u8]) {
|
||||||
let mut delay = Delay::<MHz8>::new();
|
let mut delay = Delay::<MHz8>::new();
|
||||||
self.move_cursor(segment, page);
|
self.move_cursor(segment, page);
|
||||||
|
|
191
firmware/rust/src/screen/channel.rs
Normal file
191
firmware/rust/src/screen/channel.rs
Normal file
|
@ -0,0 +1,191 @@
|
||||||
|
use super::{ClockChannel, Event, Home, Screens};
|
||||||
|
use crate::{lcd::Lcd, Input};
|
||||||
|
use si5351::{ClockOutput, PLL};
|
||||||
|
|
||||||
|
enum Selection {
|
||||||
|
Frequency,
|
||||||
|
FrequencyDigit(u8),
|
||||||
|
Digit(u8),
|
||||||
|
Enabled,
|
||||||
|
Pll,
|
||||||
|
Back,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Channel {
|
||||||
|
active: Selection,
|
||||||
|
channel: ClockChannel,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Channel {
|
||||||
|
pub fn new(channel: ClockChannel) -> Self {
|
||||||
|
Self {
|
||||||
|
active: Selection::Frequency,
|
||||||
|
channel,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn input(&mut self, input: &Input) -> Event {
|
||||||
|
self.active = match self.active {
|
||||||
|
Selection::Frequency => match input {
|
||||||
|
Input::Next => Selection::Enabled,
|
||||||
|
Input::Previous => Selection::Back,
|
||||||
|
Input::Select => Selection::FrequencyDigit(8),
|
||||||
|
Input::Back => return Event::Screen(Screens::Home(Home::new())),
|
||||||
|
},
|
||||||
|
Selection::FrequencyDigit(digit) => match input {
|
||||||
|
Input::Next => {
|
||||||
|
if digit == u8::MIN {
|
||||||
|
Selection::FrequencyDigit(8)
|
||||||
|
} else {
|
||||||
|
Selection::FrequencyDigit(digit - 1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Input::Previous => {
|
||||||
|
if digit >= 8 {
|
||||||
|
Selection::FrequencyDigit(u8::MIN)
|
||||||
|
} else {
|
||||||
|
Selection::FrequencyDigit(digit + 1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Input::Select => Selection::Digit(digit),
|
||||||
|
Input::Back => Selection::Frequency,
|
||||||
|
},
|
||||||
|
Selection::Digit(digit) => match input {
|
||||||
|
Input::Next => {
|
||||||
|
let new_freq = self.channel.freq + 10_u32.pow(digit.into());
|
||||||
|
if new_freq < 162_000_000 {
|
||||||
|
self.channel.freq = new_freq;
|
||||||
|
} else {
|
||||||
|
self.channel.freq = 162_000_000
|
||||||
|
}
|
||||||
|
return Event::Channel(self.channel);
|
||||||
|
}
|
||||||
|
Input::Previous => {
|
||||||
|
let difference = 10_u32.pow(digit.into());
|
||||||
|
if self.channel.freq > difference {
|
||||||
|
let new_freq = self.channel.freq - difference;
|
||||||
|
if new_freq > 15_000 {
|
||||||
|
self.channel.freq = new_freq;
|
||||||
|
} else {
|
||||||
|
self.channel.freq = 15_000;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
self.channel.freq = 15_000;
|
||||||
|
}
|
||||||
|
return Event::Channel(self.channel);
|
||||||
|
}
|
||||||
|
Input::Select => Selection::Frequency,
|
||||||
|
Input::Back => Selection::FrequencyDigit(digit),
|
||||||
|
},
|
||||||
|
Selection::Enabled => match input {
|
||||||
|
Input::Next => Selection::Pll,
|
||||||
|
Input::Previous => Selection::Frequency,
|
||||||
|
Input::Select => {
|
||||||
|
self.channel.enabled = !self.channel.enabled;
|
||||||
|
return Event::Channel(self.channel);
|
||||||
|
}
|
||||||
|
Input::Back => return Event::Screen(Screens::Home(Home::new())),
|
||||||
|
},
|
||||||
|
Selection::Pll => match input {
|
||||||
|
Input::Next => Selection::Back,
|
||||||
|
Input::Previous => Selection::Enabled,
|
||||||
|
Input::Select => {
|
||||||
|
self.channel.pll = match self.channel.pll {
|
||||||
|
PLL::A => PLL::B,
|
||||||
|
PLL::B => PLL::A,
|
||||||
|
};
|
||||||
|
return Event::Channel(self.channel);
|
||||||
|
}
|
||||||
|
Input::Back => return Event::Screen(Screens::Home(Home::new())),
|
||||||
|
},
|
||||||
|
Selection::Back => match input {
|
||||||
|
Input::Next => Selection::Frequency,
|
||||||
|
Input::Previous => Selection::Pll,
|
||||||
|
_ => return Event::Screen(Screens::Home(Home::new())),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
Event::None
|
||||||
|
}
|
||||||
|
|
||||||
|
fn draw_enabled(&self, lcd: &mut Lcd, inverted: bool) {
|
||||||
|
if inverted {
|
||||||
|
match self.channel.enabled {
|
||||||
|
false => lcd.print_inverted(13, 4, "OFF"),
|
||||||
|
true => lcd.print_inverted(16, 4, "ON"),
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
match self.channel.enabled {
|
||||||
|
false => lcd.print(13, 4, "OFF"),
|
||||||
|
true => lcd.print(16, 4, "ON"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn draw(&self, lcd: &mut Lcd) {
|
||||||
|
lcd.fill_area(0, 0, 19, 2, 0xFF);
|
||||||
|
match self.channel.output {
|
||||||
|
ClockOutput::Clk0 => lcd.print_inverted(19, 0, "CHANNEL_1"),
|
||||||
|
ClockOutput::Clk1 => lcd.print_inverted(19, 0, "CHANNEL_2"),
|
||||||
|
ClockOutput::Clk2 => lcd.print_inverted(19, 0, "CHANNEL_3"),
|
||||||
|
_ => unimplemented!(),
|
||||||
|
}
|
||||||
|
lcd.fill_area(83, 0, 19, 2, 0xFF);
|
||||||
|
|
||||||
|
match &self.active {
|
||||||
|
Selection::Frequency => {
|
||||||
|
lcd.print_inverted(0, 2, "FREQ:");
|
||||||
|
lcd.print_freq(39, 2, self.channel.freq);
|
||||||
|
self.draw_enabled(lcd, false);
|
||||||
|
match self.channel.pll {
|
||||||
|
PLL::A => lcd.print(59, 4, "PLL_A"),
|
||||||
|
PLL::B => lcd.print(59, 4, "PLL_B"),
|
||||||
|
}
|
||||||
|
lcd.print(36, 6, "BACK");
|
||||||
|
}
|
||||||
|
Selection::FrequencyDigit(digit) | Selection::Digit(digit) => {
|
||||||
|
lcd.print(0, 2, "FREQ:");
|
||||||
|
lcd.print_freq_digit(39, 2, self.channel.freq, *digit);
|
||||||
|
self.draw_enabled(lcd, false);
|
||||||
|
match self.channel.pll {
|
||||||
|
PLL::A => lcd.print(59, 4, "PLL_A"),
|
||||||
|
PLL::B => lcd.print(59, 4, "PLL_B"),
|
||||||
|
}
|
||||||
|
lcd.print(36, 6, "BACK");
|
||||||
|
}
|
||||||
|
Selection::Enabled => {
|
||||||
|
lcd.print(0, 2, "FREQ:");
|
||||||
|
lcd.print_freq(39, 2, self.channel.freq);
|
||||||
|
self.draw_enabled(lcd, true);
|
||||||
|
match self.channel.pll {
|
||||||
|
PLL::A => lcd.print(59, 4, "PLL_A"),
|
||||||
|
PLL::B => lcd.print(59, 4, "PLL_B"),
|
||||||
|
}
|
||||||
|
lcd.print(36, 6, "BACK");
|
||||||
|
}
|
||||||
|
Selection::Pll => {
|
||||||
|
lcd.print(0, 2, "FREQ:");
|
||||||
|
lcd.print_freq(39, 2, self.channel.freq);
|
||||||
|
self.draw_enabled(lcd, false);
|
||||||
|
match self.channel.pll {
|
||||||
|
PLL::A => lcd.print_inverted(59, 4, "PLL_A"),
|
||||||
|
PLL::B => lcd.print_inverted(59, 4, "PLL_B"),
|
||||||
|
}
|
||||||
|
lcd.print(36, 6, "BACK");
|
||||||
|
}
|
||||||
|
Selection::Back => {
|
||||||
|
lcd.print(0, 2, "FREQ:");
|
||||||
|
lcd.print_freq(39, 2, self.channel.freq);
|
||||||
|
match self.channel.enabled {
|
||||||
|
false => lcd.print(13, 4, "OFF"),
|
||||||
|
true => lcd.print(16, 4, "ON"),
|
||||||
|
}
|
||||||
|
match self.channel.pll {
|
||||||
|
PLL::A => lcd.print(59, 4, "PLL_A"),
|
||||||
|
PLL::B => lcd.print(59, 4, "PLL_B"),
|
||||||
|
}
|
||||||
|
lcd.print_inverted(36, 6, "BACK");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,10 +1,5 @@
|
||||||
use super::{Event, Screens, Setup, Splash};
|
use super::{Channel, Event, Screens, Setup};
|
||||||
use crate::{
|
use crate::{lcd::Lcd, screen::ClockChannel, Input};
|
||||||
assets::{OFF, ON, PLL_A, PLL_B},
|
|
||||||
lcd::Lcd,
|
|
||||||
Input,
|
|
||||||
};
|
|
||||||
use si5351::PLL;
|
|
||||||
|
|
||||||
enum Selection {
|
enum Selection {
|
||||||
Ch1,
|
Ch1,
|
||||||
|
@ -13,74 +8,35 @@ enum Selection {
|
||||||
Setup,
|
Setup,
|
||||||
}
|
}
|
||||||
|
|
||||||
struct Channel {
|
|
||||||
freq: u32,
|
|
||||||
enabled: bool,
|
|
||||||
pll: PLL,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Channel {
|
|
||||||
fn print(&self, lcd: &mut Lcd, page: u8) {
|
|
||||||
lcd.print_freq(25, page, self.freq);
|
|
||||||
lcd.print_icon(91, page, if self.enabled { &ON } else { &OFF });
|
|
||||||
lcd.print_icon(
|
|
||||||
94,
|
|
||||||
page + 1,
|
|
||||||
match self.pll {
|
|
||||||
PLL::A => &PLL_A,
|
|
||||||
PLL::B => &PLL_B,
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct Home {
|
pub struct Home {
|
||||||
active: Selection,
|
active: Selection,
|
||||||
channels: [Channel; 3],
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Home {
|
impl Home {
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
Self {
|
Self {
|
||||||
active: Selection::Ch1,
|
active: Selection::Ch1,
|
||||||
channels: [
|
|
||||||
Channel {
|
|
||||||
freq: 0,
|
|
||||||
enabled: false,
|
|
||||||
pll: PLL::A,
|
|
||||||
},
|
|
||||||
Channel {
|
|
||||||
freq: 0,
|
|
||||||
enabled: false,
|
|
||||||
pll: PLL::A,
|
|
||||||
},
|
|
||||||
Channel {
|
|
||||||
freq: 0,
|
|
||||||
enabled: false,
|
|
||||||
pll: PLL::A,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn input(&mut self, input: &Input) -> Event {
|
pub fn input(&mut self, input: &Input, channels: [ClockChannel; 3]) -> Event {
|
||||||
self.active = match self.active {
|
self.active = match self.active {
|
||||||
Selection::Ch1 => match input {
|
Selection::Ch1 => match input {
|
||||||
Input::Next => Selection::Ch2,
|
Input::Next => Selection::Ch2,
|
||||||
Input::Previous => Selection::Setup,
|
Input::Previous => Selection::Setup,
|
||||||
Input::Select => return Event::Screen(Screens::Splash(Splash)),
|
Input::Select => return Event::Screen(Screens::Channel(Channel::new(channels[0]))),
|
||||||
Input::Back => Selection::Ch1,
|
Input::Back => Selection::Ch1,
|
||||||
},
|
},
|
||||||
Selection::Ch2 => match input {
|
Selection::Ch2 => match input {
|
||||||
Input::Next => Selection::Ch3,
|
Input::Next => Selection::Ch3,
|
||||||
Input::Previous => Selection::Ch1,
|
Input::Previous => Selection::Ch1,
|
||||||
Input::Select => return Event::Screen(Screens::Splash(Splash)),
|
Input::Select => return Event::Screen(Screens::Channel(Channel::new(channels[1]))),
|
||||||
Input::Back => Selection::Ch2,
|
Input::Back => Selection::Ch2,
|
||||||
},
|
},
|
||||||
Selection::Ch3 => match input {
|
Selection::Ch3 => match input {
|
||||||
Input::Next => Selection::Setup,
|
Input::Next => Selection::Setup,
|
||||||
Input::Previous => Selection::Ch2,
|
Input::Previous => Selection::Ch2,
|
||||||
Input::Select => return Event::Screen(Screens::Splash(Splash)),
|
Input::Select => return Event::Screen(Screens::Channel(Channel::new(channels[2]))),
|
||||||
Input::Back => Selection::Ch3,
|
Input::Back => Selection::Ch3,
|
||||||
},
|
},
|
||||||
Selection::Setup => match input {
|
Selection::Setup => match input {
|
||||||
|
@ -94,42 +50,42 @@ impl Home {
|
||||||
Event::None
|
Event::None
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn draw(&self, lcd: &mut Lcd) {
|
pub fn draw(&self, lcd: &mut Lcd, channels: [ClockChannel; 3]) {
|
||||||
match &self.active {
|
match &self.active {
|
||||||
Selection::Ch1 => {
|
Selection::Ch1 => {
|
||||||
lcd.print_inverted(0, 0, "CH1");
|
lcd.print_inverted(0, 0, "CH1");
|
||||||
self.channels[0].print(lcd, 0);
|
channels[0].print(lcd, 0);
|
||||||
lcd.print(0, 2, "CH2");
|
lcd.print(0, 2, "CH2");
|
||||||
self.channels[1].print(lcd, 2);
|
channels[1].print(lcd, 2);
|
||||||
lcd.print(0, 4, "CH3");
|
lcd.print(0, 4, "CH3");
|
||||||
self.channels[2].print(lcd, 4);
|
channels[2].print(lcd, 4);
|
||||||
lcd.print(33, 6, "SETUP");
|
lcd.print(33, 6, "SETUP");
|
||||||
}
|
}
|
||||||
Selection::Ch2 => {
|
Selection::Ch2 => {
|
||||||
lcd.print(0, 0, "CH1");
|
lcd.print(0, 0, "CH1");
|
||||||
self.channels[0].print(lcd, 0);
|
channels[0].print(lcd, 0);
|
||||||
lcd.print_inverted(0, 2, "CH2");
|
lcd.print_inverted(0, 2, "CH2");
|
||||||
self.channels[1].print(lcd, 2);
|
channels[1].print(lcd, 2);
|
||||||
lcd.print(0, 4, "CH3");
|
lcd.print(0, 4, "CH3");
|
||||||
self.channels[2].print(lcd, 4);
|
channels[2].print(lcd, 4);
|
||||||
lcd.print(33, 6, "SETUP");
|
lcd.print(33, 6, "SETUP");
|
||||||
}
|
}
|
||||||
Selection::Ch3 => {
|
Selection::Ch3 => {
|
||||||
lcd.print(0, 0, "CH1");
|
lcd.print(0, 0, "CH1");
|
||||||
self.channels[0].print(lcd, 0);
|
channels[0].print(lcd, 0);
|
||||||
lcd.print(0, 2, "CH2");
|
lcd.print(0, 2, "CH2");
|
||||||
self.channels[1].print(lcd, 2);
|
channels[1].print(lcd, 2);
|
||||||
lcd.print_inverted(0, 4, "CH3");
|
lcd.print_inverted(0, 4, "CH3");
|
||||||
self.channels[2].print(lcd, 4);
|
channels[2].print(lcd, 4);
|
||||||
lcd.print(33, 6, "SETUP");
|
lcd.print(33, 6, "SETUP");
|
||||||
}
|
}
|
||||||
Selection::Setup => {
|
Selection::Setup => {
|
||||||
lcd.print(0, 0, "CH1");
|
lcd.print(0, 0, "CH1");
|
||||||
self.channels[0].print(lcd, 0);
|
channels[0].print(lcd, 0);
|
||||||
lcd.print(0, 2, "CH2");
|
lcd.print(0, 2, "CH2");
|
||||||
self.channels[1].print(lcd, 2);
|
channels[1].print(lcd, 2);
|
||||||
lcd.print(0, 4, "CH3");
|
lcd.print(0, 4, "CH3");
|
||||||
self.channels[2].print(lcd, 4);
|
channels[2].print(lcd, 4);
|
||||||
lcd.print_inverted(33, 6, "SETUP");
|
lcd.print_inverted(33, 6, "SETUP");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,13 +3,20 @@ use atmega_hal::{
|
||||||
port::{mode::Output, Pin, PB0, PB1, PD5},
|
port::{mode::Output, Pin, PB0, PB1, PD5},
|
||||||
Spi,
|
Spi,
|
||||||
};
|
};
|
||||||
use si5351::{Si5351, Si5351Device};
|
use si5351::{ClockOutput, Si5351, Si5351Device, PLL};
|
||||||
|
|
||||||
|
mod channel;
|
||||||
mod home;
|
mod home;
|
||||||
mod setup;
|
mod setup;
|
||||||
mod splash;
|
mod splash;
|
||||||
|
|
||||||
use crate::{eeprom, lcd::Lcd, I2c, Input, BACKLIGHT};
|
use crate::{
|
||||||
|
assets::{OFF, ON, PLL_A, PLL_B},
|
||||||
|
eeprom,
|
||||||
|
lcd::Lcd,
|
||||||
|
I2c, Input, BACKLIGHT,
|
||||||
|
};
|
||||||
|
pub use channel::Channel;
|
||||||
pub use home::Home;
|
pub use home::Home;
|
||||||
pub use setup::Setup;
|
pub use setup::Setup;
|
||||||
pub use splash::Splash;
|
pub use splash::Splash;
|
||||||
|
@ -20,21 +27,55 @@ pub enum Event {
|
||||||
Screen(Screens),
|
Screen(Screens),
|
||||||
Backlight(u8),
|
Backlight(u8),
|
||||||
Contrast(u8),
|
Contrast(u8),
|
||||||
|
Channel(ClockChannel),
|
||||||
None,
|
None,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy)]
|
||||||
|
pub struct ClockChannel {
|
||||||
|
output: ClockOutput,
|
||||||
|
freq: u32,
|
||||||
|
enabled: bool,
|
||||||
|
pll: PLL,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ClockChannel {
|
||||||
|
fn print(&self, lcd: &mut Lcd, page: u8) {
|
||||||
|
lcd.print_freq(25, page, self.freq);
|
||||||
|
lcd.print_icon(91, page, if self.enabled { &ON } else { &OFF });
|
||||||
|
lcd.print_icon(
|
||||||
|
94,
|
||||||
|
page + 1,
|
||||||
|
match self.pll {
|
||||||
|
PLL::A => &PLL_A,
|
||||||
|
PLL::B => &PLL_B,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn update(&self, si5351: &mut Si5351Device<I2c>) {
|
||||||
|
si5351
|
||||||
|
.set_frequency(self.pll, self.output, self.freq)
|
||||||
|
.unwrap();
|
||||||
|
si5351.set_clock_enabled(self.output, self.enabled);
|
||||||
|
si5351.flush_output_enabled().unwrap();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub enum Screens {
|
pub enum Screens {
|
||||||
Splash(Splash),
|
Splash(Splash),
|
||||||
Home(Home),
|
Home(Home),
|
||||||
Setup(Setup),
|
Setup(Setup),
|
||||||
|
Channel(Channel),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Screens {
|
impl Screens {
|
||||||
pub fn input(&mut self, input: &Input) -> Event {
|
pub fn input(&mut self, input: &Input, channels: [ClockChannel; 3]) -> Event {
|
||||||
match self {
|
match self {
|
||||||
Screens::Splash(_) => Event::None,
|
Screens::Splash(_) => Event::None,
|
||||||
Screens::Home(home) => home.input(input),
|
Screens::Home(home) => home.input(input, channels),
|
||||||
Screens::Setup(setup) => setup.input(input),
|
Screens::Setup(setup) => setup.input(input),
|
||||||
|
Screens::Channel(channel) => channel.input(input),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -45,6 +86,7 @@ pub struct Screen {
|
||||||
pwm: Pin<Output, PD5>,
|
pwm: Pin<Output, PD5>,
|
||||||
screen: Screens,
|
screen: Screens,
|
||||||
si5351: Si5351Device<I2c>,
|
si5351: Si5351Device<I2c>,
|
||||||
|
channels: [ClockChannel; 3],
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Screen {
|
impl Screen {
|
||||||
|
@ -62,6 +104,26 @@ impl Screen {
|
||||||
pwm,
|
pwm,
|
||||||
screen: Screens::Splash(Splash),
|
screen: Screens::Splash(Splash),
|
||||||
si5351: Si5351Device::new_adafruit_module(i2c),
|
si5351: Si5351Device::new_adafruit_module(i2c),
|
||||||
|
channels: [
|
||||||
|
ClockChannel {
|
||||||
|
output: ClockOutput::Clk0,
|
||||||
|
freq: 1_000_000,
|
||||||
|
enabled: false,
|
||||||
|
pll: PLL::A,
|
||||||
|
},
|
||||||
|
ClockChannel {
|
||||||
|
output: ClockOutput::Clk1,
|
||||||
|
freq: 1_000_000,
|
||||||
|
enabled: false,
|
||||||
|
pll: PLL::A,
|
||||||
|
},
|
||||||
|
ClockChannel {
|
||||||
|
output: ClockOutput::Clk2,
|
||||||
|
freq: 1_000_000,
|
||||||
|
enabled: false,
|
||||||
|
pll: PLL::A,
|
||||||
|
},
|
||||||
|
],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -110,16 +172,27 @@ impl Screen {
|
||||||
|
|
||||||
match &self.screen {
|
match &self.screen {
|
||||||
Screens::Splash(splash) => splash.draw(&mut self.lcd),
|
Screens::Splash(splash) => splash.draw(&mut self.lcd),
|
||||||
Screens::Home(home) => home.draw(&mut self.lcd),
|
Screens::Home(home) => home.draw(&mut self.lcd, self.channels),
|
||||||
Screens::Setup(setup) => setup.draw(&mut self.lcd),
|
Screens::Setup(setup) => setup.draw(&mut self.lcd),
|
||||||
|
Screens::Channel(channel) => channel.draw(&mut self.lcd),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn input(&mut self, input: &Input) {
|
pub fn input(&mut self, input: &Input) {
|
||||||
match self.screen.input(input) {
|
match self.screen.input(input, self.channels) {
|
||||||
Event::Screen(screen) => self.screen = screen,
|
Event::Screen(screen) => self.screen = screen,
|
||||||
Event::Backlight(backlight) => self.set_backlight(backlight),
|
Event::Backlight(backlight) => self.set_backlight(backlight),
|
||||||
Event::Contrast(contrast) => self.lcd.set_contrast(contrast),
|
Event::Contrast(contrast) => self.lcd.set_contrast(contrast),
|
||||||
|
Event::Channel(channel) => {
|
||||||
|
match channel.output {
|
||||||
|
ClockOutput::Clk0 => self.channels[0] = channel,
|
||||||
|
ClockOutput::Clk1 => self.channels[1] = channel,
|
||||||
|
ClockOutput::Clk2 => self.channels[2] = channel,
|
||||||
|
_ => unimplemented!(),
|
||||||
|
}
|
||||||
|
|
||||||
|
channel.update(&mut self.si5351);
|
||||||
|
}
|
||||||
Event::None => {}
|
Event::None => {}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue