~ruther/simple-clock

7532a6fe169d2b63baf7451bcb75ab14ad22dab1 — František Boháček 1 year, 8 months ago 69e8721
feat(src): add clock seven segment display support
A src/clock_display.rs => src/clock_display.rs +183 -0
@@ 0,0 1,183 @@
use stm32f1xx_hal::timer;

use crate::{display::Display, seven_segments::SevenSegments};

const MAIN_DISPLAY_OFFSET: usize = 2;
const MAIN_DISPLAY_SIZE: usize = 4;
const SIDE_DISPLAY_1_OFFSET: usize = 0;
const SIDE_DISPLAY_1_SIZE: usize = 2;
const SIDE_DISPLAY_2_OFFSET: usize = 6;
const SIDE_DISPLAY_2_SIZE: usize = 2;

const COLON_DIGIT_1: usize = 3;
const COLON_DIGIT_2: usize = 4;

pub struct ClockDisplay {
    display: Display<8>,
    colon: bool,
}

#[derive(Copy, Clone)]
pub enum DisplayPart {
    Whole,
    MainDisplay,
    SideDisplay1,
    SideDisplay2,
}

#[derive(Debug)]
pub enum DisplayError {
    DoesNotFit,
}

impl ClockDisplay {
    pub fn new(display: Display<8>) -> Self {
        Self {
            display,
            colon: false,
        }
    }

    pub fn display(&mut self) -> &mut Display<8> {
        &mut self.display
    }

    pub fn show_ordinal(
        &mut self,
        part: DisplayPart,
        number: u32,
        pad: bool,
    ) -> Result<(), DisplayError> {
        self.show_number(part, number, pad)?;

        let offset = Self::get_part_offset(part);
        let size = Self::get_part_size(part);
        let target_index = offset + size - 1;

        let data = self.display.data()[target_index];
        self.display.set_digit(target_index, data | 1);

        Ok(())
    }

    pub fn show_number(
        &mut self,
        part: DisplayPart,
        number: u32,
        pad: bool,
    ) -> Result<(), DisplayError> {
        let offset = Self::get_part_offset(part);
        let size = Self::get_part_size(part);
        let mut data = self.display.data();

        let mut number = number;
        for i in 1..=size {
            let digit = number % 10;
            number /= 10;

            data[offset + size - i] = if number != 0 || digit != 0 || pad {
                SevenSegments::digit_to_segments(digit as u8)
            } else {
                0
            };
        }

        if number > 0 {
            return Err(DisplayError::DoesNotFit);
        }

        self.display.set_data(data);
        self.update_colon();
        Ok(())
    }

    pub fn show_text(&mut self, part: DisplayPart, text: &str) -> Result<(), DisplayError> {
        let offset = Self::get_part_offset(part);
        let size = Self::get_part_size(part);

        if text.len() > size {
            return Err(DisplayError::DoesNotFit);
        }

        let mut data = self.display.data();
        for (i, c) in text.chars().enumerate() {
            data[offset + i] = SevenSegments::letter_to_segments(c);
        }

        self.display.set_data(data);
        self.update_colon();
        Ok(())
    }

    pub fn hide(&mut self, part: DisplayPart) {
        let offset = Self::get_part_offset(part);
        let size = Self::get_part_size(part);

        let mut data = self.display.data();
        for current_data in data.iter_mut().skip(offset).take(size) {
            *current_data = 0;
        }

        self.display.set_data(data);
    }

    pub fn brightness(&self, part: DisplayPart) -> &[u16] {
        let offset = Self::get_part_offset(part);
        let size = Self::get_part_size(part);
        &self.display.ref_brightness()[offset..offset + size]
    }

    pub fn set_brightness(&mut self, part: DisplayPart, brightness: u16) {
        let offset = Self::get_part_offset(part);
        let size = Self::get_part_size(part);
        let mut brightnesses = self.display.brightness();

        for current_brightness in brightnesses.iter_mut().skip(offset).take(size) {
            *current_brightness = brightness;
        }

        self.display.set_brightness(brightnesses);
    }

    pub fn set_colon(&mut self, colon: bool) {
        self.colon = colon;
        self.update_colon();
    }

    pub fn update(&mut self) -> nb::Result<(), timer::Error> {
        self.display.update()
    }

    fn update_colon(&mut self) {
        let data = self.display.data();
        if self.colon {
            self.display
                .set_digit(COLON_DIGIT_1, data[COLON_DIGIT_1] | 0b1);
            self.display
                .set_digit(COLON_DIGIT_2, data[COLON_DIGIT_2] | 0b1);
        } else {
            self.display
                .set_digit(COLON_DIGIT_1, data[COLON_DIGIT_1] & 0xFE);
            self.display
                .set_digit(COLON_DIGIT_2, data[COLON_DIGIT_2] & 0xFE);
        }
    }

    fn get_part_size(part: DisplayPart) -> usize {
        match part {
            DisplayPart::Whole => SIDE_DISPLAY_1_SIZE + MAIN_DISPLAY_SIZE + SIDE_DISPLAY_2_SIZE,
            DisplayPart::MainDisplay => MAIN_DISPLAY_SIZE,
            DisplayPart::SideDisplay1 => SIDE_DISPLAY_1_SIZE,
            DisplayPart::SideDisplay2 => SIDE_DISPLAY_2_SIZE,
        }
    }

    fn get_part_offset(part: DisplayPart) -> usize {
        match part {
            DisplayPart::Whole => 0,
            DisplayPart::MainDisplay => MAIN_DISPLAY_OFFSET,
            DisplayPart::SideDisplay1 => SIDE_DISPLAY_1_OFFSET,
            DisplayPart::SideDisplay2 => SIDE_DISPLAY_2_OFFSET,
        }
    }
}

A src/clock_display_viewer.rs => src/clock_display_viewer.rs +57 -0
@@ 0,0 1,57 @@
use crate::{
    clock_display::ClockDisplay,
    clock_state::ClockState,
    display_view::{
        clock_display_view::ClockDisplayView, date_display_view::DateDisplayView, DisplayView,
        DisplayViews,
    },
};
use alloc::{boxed::Box, vec, vec::Vec};
use stm32f1xx_hal::timer;

pub struct ClockDisplayViewer {
    clock_display: ClockDisplay,
    views: Vec<Box<dyn DisplayView + Send>>,
    current_view: Option<DisplayViews>,
}

impl ClockDisplayViewer {
    pub fn new(clock_display: ClockDisplay) -> Self {
        Self {
            clock_display,
            views: vec![
                Box::new(ClockDisplayView::new()),
                Box::new(ClockDisplayView::with_seconds()),
                Box::new(ClockDisplayView::with_date()),
                Box::new(DateDisplayView::new()),
            ],
            current_view: None,
        }
    }

    pub fn clock_display<'a>(&'a mut self) -> &'a mut ClockDisplay {
        &mut self.clock_display
    }

    pub fn current_view(&self) -> Option<DisplayViews> {
        self.current_view
    }

    pub fn set_current_view(&mut self, view: DisplayViews) {
        self.current_view = Some(view);
    }

    pub fn clear_current_view(&mut self) {
        self.current_view = None;
    }

    pub fn update(&mut self, state: &ClockState) -> nb::Result<(), timer::Error> {
        self.clock_display.update()?;

        if let Some(view) = self.current_view {
            let view = &mut self.views[view as usize];
            view.update_display(state, &mut self.clock_display).unwrap(); // TODO: get rid of the unwrap
        }
        Ok(())
    }
}

A src/display.rs => src/display.rs +134 -0
@@ 0,0 1,134 @@
use core::convert::Infallible;

use alloc::boxed::Box;
use embedded_hal::PwmPin;
use fugit::MicrosDurationU32;
use stm32f1xx_hal::timer;

use crate::count_down::CountDown;

type OutputPin = dyn embedded_hal::digital::v2::OutputPin<Error = Infallible> + Send;

// How long to turn on a digit to show a number
// (every digit will be turned on for this time, so this number shouldn't bee to large - so it's not flickering,
// and it shouldn't be too small to allow the transistor and LED operate)
const DIGIT_ON_TIME: MicrosDurationU32 = MicrosDurationU32::micros(1490);

// How long to turn off the digits when moving from one digit to another
// This is important to close off the previous transistor before next digit
// is lit up.
const DIGITS_OFF_TIME: MicrosDurationU32 = MicrosDurationU32::micros(500);

struct DisplayState<const DIGITS: usize> {
    digit_index: usize,
    next_show: bool, // if 1, next timer step is to show, if 0, next timer step is to hide
}

impl<const DIGITS: usize> DisplayState<DIGITS> {
    fn empty() -> Self {
        Self {
            digit_index: 0,
            next_show: true,
        }
    }

    fn step(&mut self) {
        self.next_show = !self.next_show;
        if self.next_show {
            self.digit_index = (self.digit_index + 1) % DIGITS;
        }
    }
}

pub struct Display<const DIGITS: usize> {
    segments: [Box<OutputPin>; 8],
    digits: [Box<dyn PwmPin<Duty = u16> + Send>; DIGITS],
    timer: Box<dyn CountDown<Time = MicrosDurationU32> + Send>,
    data: [u8; DIGITS],
    brightness: [u16; DIGITS],

    state: DisplayState<DIGITS>,
}

impl<const DIGITS: usize> Display<DIGITS> {
    pub fn new(
        segments: [Box<OutputPin>; 8],
        mut digits: [Box<dyn PwmPin<Duty = u16> + Send>; DIGITS],
        timer: Box<dyn CountDown<Time = MicrosDurationU32> + Send>,
    ) -> Self {
        for digit in digits.iter_mut() {
            digit.enable();
        }

        Self {
            segments,
            digits,
            timer,
            data: [0; DIGITS],
            brightness: [0xFFFF; DIGITS],
            state: DisplayState::<DIGITS>::empty(),
        }
    }

    pub fn data(&self) -> [u8; DIGITS] {
        self.data
    }

    pub fn set_data(&mut self, digits: [u8; DIGITS]) {
        self.data = digits;
    }

    pub fn set_digit(&mut self, digit: usize, set: u8) {
        self.data[digit] = set;
    }

    pub fn brightness(&self) -> [u16; DIGITS] {
        self.brightness
    }

    pub fn ref_brightness(&self) -> &[u16] {
        &self.brightness
    }

    pub fn set_brightness(&mut self, brightness: [u16; DIGITS]) {
        self.brightness = brightness;
    }

    pub fn set_digit_brightness(&mut self, digit: usize, brightness: u16) {
        self.brightness[digit] = brightness;
    }

    pub fn update(&mut self) -> nb::Result<(), timer::Error> {
        self.timer.wait()?;
        let now_show = self.state.next_show;
        let digit_index = self.state.digit_index;

        // turn every digit off
        for digit in self.digits.iter_mut() {
            digit.set_duty(0xFFFF);
        }

        if now_show {
            let digit = &mut self.digits[digit_index];
            let data = self.data[digit_index];
            let brightness = self.brightness[digit_index];

            for (i, segment) in self.segments.iter_mut().enumerate() {
                segment
                    .set_state((!(data & (1 << (7 - i)) > 0)).into())
                    .unwrap();
            }

            digit.set_duty(0xFFFF - brightness);
        }

        self.timer.start(if now_show {
            DIGIT_ON_TIME
        } else {
            DIGITS_OFF_TIME
        });
        self.state.step();

        Ok(())
    }
}

A src/display_view.rs => src/display_view.rs +54 -0
@@ 0,0 1,54 @@
use crate::{
    clock_display::{ClockDisplay, DisplayError},
    clock_state::ClockState,
};

pub mod clock_display_view;
pub mod date_display_view;

#[derive(Copy, Clone, PartialEq, Eq)]
#[repr(usize)]
pub enum DisplayViews {
    ClockView = 0,
    ClockSecondsView = 1,
    ClockDateView = 2,
    DateView = 3,
}

impl DisplayViews {
    pub fn count() -> usize {
        4
    }
}

impl From<usize> for DisplayViews {
    fn from(value: usize) -> Self {
        match value {
            0 => Self::ClockView,
            1 => Self::ClockSecondsView,
            2 => Self::ClockDateView,
            3 => Self::DateView,
            _ => panic!(),
        }
    }
}

#[derive(Debug)]
pub enum DisplayViewError {
    Unknown,
    DisplayError(DisplayError),
}

impl From<DisplayError> for DisplayViewError {
    fn from(value: DisplayError) -> Self {
        Self::DisplayError(value)
    }
}

pub trait DisplayView {
    fn update_display(
        &mut self,
        state: &ClockState,
        display: &mut ClockDisplay,
    ) -> Result<(), DisplayViewError>;
}

A src/display_view/clock_display_view.rs => src/display_view/clock_display_view.rs +69 -0
@@ 0,0 1,69 @@
use crate::{
    clock_display::{ClockDisplay, DisplayPart},
    clock_state::ClockState,
};

use super::{DisplayView, DisplayViewError};

pub struct ClockDisplayView {
    show_seconds: bool,
    show_date: bool,
}

impl Default for ClockDisplayView {
    fn default() -> Self {
        Self::new()
    }
}

impl ClockDisplayView {
    pub fn new() -> Self {
        Self {
            show_date: false,
            show_seconds: false,
        }
    }

    pub fn with_seconds() -> Self {
        Self {
            show_date: false,
            show_seconds: true,
        }
    }

    pub fn with_date() -> Self {
        Self {
            show_date: true,
            show_seconds: false,
        }
    }
}

impl DisplayView for ClockDisplayView {
    fn update_display(
        &mut self,
        state: &ClockState,
        display: &mut ClockDisplay,
    ) -> Result<(), DisplayViewError> {
        let calendar = state.calendar();

        if self.show_seconds {
            display.hide(DisplayPart::SideDisplay1);
            display.show_number(DisplayPart::SideDisplay2, calendar.seconds() as u32, true)?;
        } else if self.show_date {
            display.show_ordinal(DisplayPart::SideDisplay1, calendar.day() as u32, true)?;
            display.show_ordinal(DisplayPart::SideDisplay2, calendar.month() as u32, true)?;
        } else {
            display.hide(DisplayPart::SideDisplay1);
            display.hide(DisplayPart::SideDisplay2);
        }

        display.set_colon(calendar.seconds() % 2 == 0);
        display.show_number(
            DisplayPart::MainDisplay,
            (calendar.hours() as u32) * 100 + (calendar.minutes() as u32),
            true,
        )?;
        Ok(())
    }
}

A src/display_view/date_display_view.rs => src/display_view/date_display_view.rs +33 -0
@@ 0,0 1,33 @@
use crate::clock_display::DisplayPart;

use super::DisplayView;

pub struct DateDisplayView;

impl DateDisplayView {
    pub fn new() -> Self {
        Self
    }
}

impl Default for DateDisplayView {
    fn default() -> Self {
        Self::new()
    }
}

impl DisplayView for DateDisplayView {
    fn update_display(
        &mut self,
        state: &crate::clock_state::ClockState,
        display: &mut crate::clock_display::ClockDisplay,
    ) -> Result<(), super::DisplayViewError> {
        let calendar = state.calendar();

        display.show_ordinal(DisplayPart::SideDisplay1, calendar.day() as u32, true)?;
        display.show_ordinal(DisplayPart::SideDisplay2, calendar.month() as u32, true)?;
        display.show_number(DisplayPart::MainDisplay, calendar.year() as u32, false)?;
        display.set_colon(false);
        Ok(())
    }
}

A src/number_digits.rs => src/number_digits.rs +33 -0
@@ 0,0 1,33 @@
pub trait NumberDigits {
    fn get_digit(self, digit_index: u8) -> u8;
}

impl NumberDigits for u8 {
    fn get_digit(self, digit_index: u8) -> u8 {
        let mut number = self;
        for _ in 0..digit_index {
            number /= 10;
        }
        (number % 10) as u8
    }
}

impl NumberDigits for u16 {
    fn get_digit(self, digit_index: u8) -> u8 {
        let mut number = self;
        for _ in 0..digit_index {
            number /= 10;
        }
        (number % 10) as u8
    }
}

impl NumberDigits for u32 {
    fn get_digit(self, digit_index: u8) -> u8 {
        let mut number = self;
        for _ in 0..digit_index {
            number /= 10;
        }
        (number % 10) as u8
    }
}

A src/seven_segments.rs => src/seven_segments.rs +28 -0
@@ 0,0 1,28 @@
pub struct SevenSegments;

impl SevenSegments {
    pub fn digit_to_segments(digit: u8) -> u8 {
        (match digit {
            0 => 0b1111110,
            1 => 0b0110000,
            2 => 0b1101101,
            3 => 0b1111001,
            4 => 0b0110011,
            5 => 0b1011011,
            6 => 0b1011111,
            7 => 0b1110000,
            8 => 0b1111111,
            9 => 0b1111011,
            _ => 0b0000001,
        }) << 1
    }

    pub fn letter_to_segments(letter: char) -> u8 {
        (match letter {
            'E' => 0b1001111,
            'r' => 0b0000101,
            'o' => 0b0011101,
            _ => 0b0000001,
        }) << 1
    }
}

Do not follow this link