Skip to main content

virtual_lcd_sdk/
lib.rs

1#![forbid(unsafe_code)]
2
3#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
4pub struct Color {
5    pub r: u8,
6    pub g: u8,
7    pub b: u8,
8}
9
10impl Color {
11    pub const BLACK: Self = Self::rgb(0, 0, 0);
12    pub const WHITE: Self = Self::rgb(255, 255, 255);
13    pub const RED: Self = Self::rgb(255, 0, 0);
14    pub const GREEN: Self = Self::rgb(0, 255, 0);
15    pub const BLUE: Self = Self::rgb(0, 0, 255);
16
17    pub const fn rgb(r: u8, g: u8, b: u8) -> Self {
18        Self { r, g, b }
19    }
20
21    pub fn luminance(self) -> u8 {
22        ((self.r as u16 * 30 + self.g as u16 * 59 + self.b as u16 * 11) / 100) as u8
23    }
24
25    pub fn to_rgb565(self) -> u16 {
26        let r = (self.r as u16 >> 3) & 0x1F;
27        let g = (self.g as u16 >> 2) & 0x3F;
28        let b = (self.b as u16 >> 3) & 0x1F;
29        (r << 11) | (g << 5) | b
30    }
31
32    pub fn from_rgb565(value: u16) -> Self {
33        let r = ((value >> 11) & 0x1F) as u8;
34        let g = ((value >> 5) & 0x3F) as u8;
35        let b = (value & 0x1F) as u8;
36
37        Self {
38            r: (r << 3) | (r >> 2),
39            g: (g << 2) | (g >> 4),
40            b: (b << 3) | (b >> 2),
41        }
42    }
43
44    pub fn to_gray8(self) -> u8 {
45        self.luminance()
46    }
47}
48
49#[derive(Clone, Copy, Debug, PartialEq, Eq)]
50pub enum PinId {
51    Cs,
52    Dc,
53    Rst,
54    Wr,
55    Rd,
56    Clk,
57    Mosi,
58    Miso,
59    Bl,
60}
61
62impl PinId {
63    pub const ALL: [Self; 9] = [
64        Self::Cs,
65        Self::Dc,
66        Self::Rst,
67        Self::Wr,
68        Self::Rd,
69        Self::Clk,
70        Self::Mosi,
71        Self::Miso,
72        Self::Bl,
73    ];
74
75    pub const fn index(self) -> usize {
76        match self {
77            Self::Cs => 0,
78            Self::Dc => 1,
79            Self::Rst => 2,
80            Self::Wr => 3,
81            Self::Rd => 4,
82            Self::Clk => 5,
83            Self::Mosi => 6,
84            Self::Miso => 7,
85            Self::Bl => 8,
86        }
87    }
88}
89
90pub trait Lcd {
91    type Error;
92
93    fn init(&mut self) -> Result<(), Self::Error>;
94    fn clear(&mut self, color: Color) -> Result<(), Self::Error>;
95    fn draw_pixel(&mut self, x: u16, y: u16, color: Color) -> Result<(), Self::Error>;
96    fn fill_rect(
97        &mut self,
98        x: u16,
99        y: u16,
100        width: u16,
101        height: u16,
102        color: Color,
103    ) -> Result<(), Self::Error>;
104    fn present(&mut self) -> Result<(), Self::Error>;
105}
106
107pub trait LcdBus {
108    type Error;
109
110    fn set_pin(&mut self, pin: PinId, value: bool) -> Result<(), Self::Error>;
111    fn write_command(&mut self, cmd: u8) -> Result<(), Self::Error>;
112    fn write_data(&mut self, data: &[u8]) -> Result<(), Self::Error>;
113    fn read_data(&mut self, len: usize) -> Result<Vec<u8>, Self::Error>;
114}
115
116#[cfg(test)]
117mod tests {
118    use super::Color;
119
120    #[test]
121    fn rgb565_roundtrip_preserves_primary_signal() {
122        let red = Color::RED;
123        let encoded = red.to_rgb565();
124        let decoded = Color::from_rgb565(encoded);
125
126        assert!(decoded.r > 200);
127        assert!(decoded.g < 20);
128        assert!(decoded.b < 20);
129    }
130}