Skip to main content

pcd8544/
lib.rs

1#![no_std]
2use core::fmt::Error as FmtError;
3use core::fmt::Result as FmtResult;
4use core::fmt::Write;
5use embedded_hal::digital::v2::OutputPin;
6
7pub const WIDTH: u8 = 84;
8pub const HEIGHT: u8 = 48;
9pub const ROWS: u8 = HEIGHT / 8;
10
11#[repr(u8)]
12pub enum TemperatureCoefficient {
13    TC0 = 0,
14    TC1 = 1,
15    TC2 = 2,
16    TC3 = 3,
17}
18
19#[repr(u8)]
20pub enum BiasMode {
21    Bias1To100 = 0,
22    Bias1To80 = 1,
23    Bias1To65 = 2,
24    Bias1To48 = 3,
25    Bias1To40 = 4,
26    Bias1To24 = 5,
27    Bias1To18 = 6,
28    Bias1To10 = 7,
29}
30
31#[repr(u8)]
32pub enum DisplayMode {
33    DisplayBlank = 0b000,
34    NormalMode = 0b100,
35    AllSegmentsOn = 0b001,
36    InverseVideoMode = 0b101,
37}
38
39pub struct PCD8544<CLK, DIN, DC, CE, RST, LIGHT>
40where
41    CLK: OutputPin,
42    DIN: OutputPin,
43    DC: OutputPin,
44    CE: OutputPin,
45    RST: OutputPin,
46    LIGHT: OutputPin,
47{
48    clk: CLK,
49    din: DIN,
50    dc: DC,
51    ce: CE,
52    rst: RST,
53    light: LIGHT,
54    power_down_control: bool,
55    entry_mode: bool,
56    extended_instruction_set: bool,
57    x: u8,
58    y: u8,
59}
60
61impl<CLK, DIN, DC, CE, RST, LIGHT, ERR> PCD8544<CLK, DIN, DC, CE, RST, LIGHT>
62where
63    CLK: OutputPin<Error = ERR>,
64    DIN: OutputPin<Error = ERR>,
65    DC: OutputPin<Error = ERR>,
66    CE: OutputPin<Error = ERR>,
67    RST: OutputPin<Error = ERR>,
68    LIGHT: OutputPin<Error = ERR>,
69{
70    pub fn new(
71        mut clk: CLK,
72        din: DIN,
73        dc: DC,
74        mut ce: CE,
75        mut rst: RST,
76        light: LIGHT,
77    ) -> Result<PCD8544<CLK, DIN, DC, CE, RST, LIGHT>, ERR> {
78        clk.set_low()?;
79        rst.set_low()?;
80        ce.set_high()?;
81        Ok(PCD8544 {
82            clk,
83            din,
84            dc,
85            ce,
86            rst,
87            light,
88            power_down_control: false,
89            entry_mode: false,
90            extended_instruction_set: false,
91            x: 0,
92            y: 0,
93        })
94    }
95
96    pub fn reset(&mut self) -> Result<(), ERR> {
97        self.rst.set_low()?;
98        self.x = 0;
99        self.y = 0;
100        self.init()
101    }
102
103    pub fn init(&mut self) -> Result<(), ERR> {
104        // reset the display
105        self.rst.set_low()?;
106        self.rst.set_high()?;
107
108        // reset state variables
109        self.power_down_control = false;
110        self.entry_mode = false;
111        self.extended_instruction_set = false;
112
113        // write init configuration
114        self.enable_extended_commands(true)?;
115        self.set_contrast(56_u8)?;
116        self.set_temperature_coefficient(TemperatureCoefficient::TC3)?;
117        self.set_bias_mode(BiasMode::Bias1To40)?;
118        self.enable_extended_commands(false)?;
119        self.set_display_mode(DisplayMode::NormalMode)?;
120
121        // clear display data
122        self.clear()
123    }
124
125    pub fn clear(&mut self) -> Result<(), ERR> {
126        for _ in 0..(WIDTH as u16 * ROWS as u16) {
127            self.write_data(0x00)?;
128        }
129        self.set_x_position(0)?;
130        self.set_y_position(0)
131    }
132
133    pub fn set_power_down(&mut self, power_down: bool) -> Result<(), ERR> {
134        self.power_down_control = power_down;
135        self.write_current_function_set()
136    }
137
138    pub fn set_entry_mode(&mut self, entry_mode: bool) -> Result<(), ERR> {
139        self.entry_mode = entry_mode;
140        self.write_current_function_set()
141    }
142
143    pub fn x(&self) -> u8 {
144        self.x
145    }
146
147    pub fn y(&self) -> u8 {
148        self.y
149    }
150
151    pub fn set_x_position(&mut self, x: u8) -> Result<(), ERR> {
152        let x = x % WIDTH;
153        self.x = x;
154        self.write_command(0x80 | x)
155    }
156
157    pub fn set_y_position(&mut self, y: u8) -> Result<(), ERR> {
158        let y = y % ROWS;
159        self.y = y;
160        self.write_command(0x40 | y)
161    }
162
163    pub fn set_light(&mut self, enabled: bool) -> Result<(), ERR> {
164        if enabled {
165            self.light.set_low()
166        } else {
167            self.light.set_high()
168        }
169    }
170
171    pub fn set_display_mode(&mut self, mode: DisplayMode) -> Result<(), ERR> {
172        self.write_command(0x08 | mode as u8)
173    }
174
175    pub fn set_bias_mode(&mut self, bias: BiasMode) -> Result<(), ERR> {
176        self.write_command(0x10 | bias as u8)
177    }
178
179    pub fn set_temperature_coefficient(
180        &mut self,
181        coefficient: TemperatureCoefficient,
182    ) -> Result<(), ERR> {
183        self.write_command(0x04 | coefficient as u8)
184    }
185
186    /// contrast in range of 0..128
187    pub fn set_contrast(&mut self, contrast: u8) -> Result<(), ERR> {
188        self.write_command(0x80 | contrast)
189    }
190
191    pub fn enable_extended_commands(&mut self, enable: bool) -> Result<(), ERR> {
192        self.extended_instruction_set = enable;
193        self.write_current_function_set()
194    }
195
196    fn write_current_function_set(&mut self) -> Result<(), ERR> {
197        let power = self.power_down_control;
198        let entry = self.entry_mode;
199        let extended = self.extended_instruction_set;
200        self.write_function_set(power, entry, extended)
201    }
202
203    fn write_function_set(
204        &mut self,
205        power_down_control: bool,
206        entry_mode: bool,
207        extended_instruction_set: bool,
208    ) -> Result<(), ERR> {
209        let mut val = 0x20;
210        if power_down_control {
211            val |= 0x04;
212        }
213        if entry_mode {
214            val |= 0x02;
215        }
216        if extended_instruction_set {
217            val |= 0x01;
218        }
219        self.write_command(val)
220    }
221
222    pub fn write_command(&mut self, value: u8) -> Result<(), ERR> {
223        self.write_byte(false, value)
224    }
225
226    pub fn write_data(&mut self, value: u8) -> Result<(), ERR> {
227        self.write_byte(true, value)
228    }
229
230    fn write_byte(&mut self, data: bool, value: u8) -> Result<(), ERR> {
231        let mut value = value;
232        if data {
233            self.dc.set_high()?;
234            self.increase_position();
235        } else {
236            self.dc.set_low()?;
237        }
238        self.ce.set_low()?;
239        for _ in 0..8 {
240            self.write_bit((value & 0x80) == 0x80)?;
241            value <<= 1;
242        }
243        self.ce.set_high()
244    }
245
246    fn increase_position(&mut self) {
247        self.x = (self.x + 1) % WIDTH;
248        if self.x == 0 {
249            self.y = (self.y + 1) & ROWS;
250        }
251    }
252
253    fn write_bit(&mut self, high: bool) -> Result<(), ERR> {
254        if high {
255            self.din.set_high()?;
256        } else {
257            self.din.set_low()?;
258        }
259        self.clk.set_high()?;
260        self.clk.set_low()
261    }
262}
263
264fn char_to_bytes(char: char) -> &'static [u8] {
265    match char {
266        ' ' => &[0x00, 0x00, 0x00, 0x00, 0x00],
267        '!' => &[0x00, 0x00, 0x5f, 0x00, 0x00],
268        '"' => &[0x00, 0x07, 0x00, 0x07, 0x00],
269        '#' => &[0x14, 0x7f, 0x14, 0x7f, 0x14],
270        '$' => &[0x24, 0x2a, 0x7f, 0x2a, 0x12],
271        '%' => &[0x23, 0x13, 0x08, 0x64, 0x62],
272        '&' => &[0x36, 0x49, 0x55, 0x22, 0x50],
273        '\'' => &[0x00, 0x05, 0x03, 0x00, 0x00],
274        '(' => &[0x00, 0x1c, 0x22, 0x41, 0x00],
275        ')' => &[0x00, 0x41, 0x22, 0x1c, 0x00],
276        '*' => &[0x14, 0x08, 0x3e, 0x08, 0x14],
277        '+' => &[0x08, 0x08, 0x3e, 0x08, 0x08],
278        ',' => &[0x00, 0x50, 0x30, 0x00, 0x00],
279        '-' => &[0x08, 0x08, 0x08, 0x08, 0x08],
280        '.' => &[0x00, 0x60, 0x60, 0x00, 0x00],
281        '/' => &[0x20, 0x10, 0x08, 0x04, 0x02],
282        '0' => &[0x3e, 0x51, 0x49, 0x45, 0x3e],
283        '1' => &[0x00, 0x42, 0x7f, 0x40, 0x00],
284        '2' => &[0x42, 0x61, 0x51, 0x49, 0x46],
285        '3' => &[0x21, 0x41, 0x45, 0x4b, 0x31],
286        '4' => &[0x18, 0x14, 0x12, 0x7f, 0x10],
287        '5' => &[0x27, 0x45, 0x45, 0x45, 0x39],
288        '6' => &[0x3c, 0x4a, 0x49, 0x49, 0x30],
289        '7' => &[0x01, 0x71, 0x09, 0x05, 0x03],
290        '8' => &[0x36, 0x49, 0x49, 0x49, 0x36],
291        '9' => &[0x06, 0x49, 0x49, 0x29, 0x1e],
292        ':' => &[0x00, 0x36, 0x36, 0x00, 0x00],
293        ';' => &[0x00, 0x56, 0x36, 0x00, 0x00],
294        '<' => &[0x08, 0x14, 0x22, 0x41, 0x00],
295        '=' => &[0x14, 0x14, 0x14, 0x14, 0x14],
296        '>' => &[0x00, 0x41, 0x22, 0x14, 0x08],
297        '?' => &[0x02, 0x01, 0x51, 0x09, 0x06],
298        '@' => &[0x32, 0x49, 0x79, 0x41, 0x3e],
299        'A' => &[0x7e, 0x11, 0x11, 0x11, 0x7e],
300        'B' => &[0x7f, 0x49, 0x49, 0x49, 0x36],
301        'C' => &[0x3e, 0x41, 0x41, 0x41, 0x22],
302        'D' => &[0x7f, 0x41, 0x41, 0x22, 0x1c],
303        'E' => &[0x7f, 0x49, 0x49, 0x49, 0x41],
304        'F' => &[0x7f, 0x09, 0x09, 0x09, 0x01],
305        'G' => &[0x3e, 0x41, 0x49, 0x49, 0x7a],
306        'H' => &[0x7f, 0x08, 0x08, 0x08, 0x7f],
307        'I' => &[0x00, 0x41, 0x7f, 0x41, 0x00],
308        'J' => &[0x20, 0x40, 0x41, 0x3f, 0x01],
309        'K' => &[0x7f, 0x08, 0x14, 0x22, 0x41],
310        'L' => &[0x7f, 0x40, 0x40, 0x40, 0x40],
311        'M' => &[0x7f, 0x02, 0x0c, 0x02, 0x7f],
312        'N' => &[0x7f, 0x04, 0x08, 0x10, 0x7f],
313        'O' => &[0x3e, 0x41, 0x41, 0x41, 0x3e],
314        'P' => &[0x7f, 0x09, 0x09, 0x09, 0x06],
315        'Q' => &[0x3e, 0x41, 0x51, 0x21, 0x5e],
316        'R' => &[0x7f, 0x09, 0x19, 0x29, 0x46],
317        'S' => &[0x46, 0x49, 0x49, 0x49, 0x31],
318        'T' => &[0x01, 0x01, 0x7f, 0x01, 0x01],
319        'U' => &[0x3f, 0x40, 0x40, 0x40, 0x3f],
320        'V' => &[0x1f, 0x20, 0x40, 0x20, 0x1f],
321        'W' => &[0x3f, 0x40, 0x38, 0x40, 0x3f],
322        'X' => &[0x63, 0x14, 0x08, 0x14, 0x63],
323        'Y' => &[0x07, 0x08, 0x70, 0x08, 0x07],
324        'Z' => &[0x61, 0x51, 0x49, 0x45, 0x43],
325        '[' => &[0x00, 0x7f, 0x41, 0x41, 0x00],
326        '¥' => &[0x02, 0x04, 0x08, 0x10, 0x20],
327        ']' => &[0x00, 0x41, 0x41, 0x7f, 0x00],
328        '^' => &[0x04, 0x02, 0x01, 0x02, 0x04],
329        '_' => &[0x40, 0x40, 0x40, 0x40, 0x40],
330        '`' => &[0x00, 0x01, 0x02, 0x04, 0x00],
331        'a' => &[0x20, 0x54, 0x54, 0x54, 0x78],
332        'b' => &[0x7f, 0x48, 0x44, 0x44, 0x38],
333        'c' => &[0x38, 0x44, 0x44, 0x44, 0x20],
334        'd' => &[0x38, 0x44, 0x44, 0x48, 0x7f],
335        'e' => &[0x38, 0x54, 0x54, 0x54, 0x18],
336        'f' => &[0x08, 0x7e, 0x09, 0x01, 0x02],
337        'g' => &[0x0c, 0x52, 0x52, 0x52, 0x3e],
338        'h' => &[0x7f, 0x08, 0x04, 0x04, 0x78],
339        'i' => &[0x00, 0x44, 0x7d, 0x40, 0x00],
340        'j' => &[0x20, 0x40, 0x44, 0x3d, 0x00],
341        'k' => &[0x7f, 0x10, 0x28, 0x44, 0x00],
342        'l' => &[0x00, 0x41, 0x7f, 0x40, 0x00],
343        'm' => &[0x7c, 0x04, 0x18, 0x04, 0x78],
344        'n' => &[0x7c, 0x08, 0x04, 0x04, 0x78],
345        'o' => &[0x38, 0x44, 0x44, 0x44, 0x38],
346        'p' => &[0x7c, 0x14, 0x14, 0x14, 0x08],
347        'q' => &[0x08, 0x14, 0x14, 0x18, 0x7c],
348        'r' => &[0x7c, 0x08, 0x04, 0x04, 0x08],
349        's' => &[0x48, 0x54, 0x54, 0x54, 0x20],
350        't' => &[0x04, 0x3f, 0x44, 0x40, 0x20],
351        'u' => &[0x3c, 0x40, 0x40, 0x20, 0x7c],
352        'v' => &[0x1c, 0x20, 0x40, 0x20, 0x1c],
353        'w' => &[0x3c, 0x40, 0x30, 0x40, 0x3c],
354        'x' => &[0x44, 0x28, 0x10, 0x28, 0x44],
355        'y' => &[0x0c, 0x50, 0x50, 0x50, 0x3c],
356        'z' => &[0x44, 0x64, 0x54, 0x4c, 0x44],
357        '{' => &[0x00, 0x08, 0x36, 0x41, 0x00],
358        '|' => &[0x00, 0x00, 0x7f, 0x00, 0x00],
359        '}' => &[0x00, 0x41, 0x36, 0x08, 0x00],
360        '←' => &[0x10, 0x08, 0x08, 0x10, 0x08],
361        '→' => &[0x78, 0x46, 0x41, 0x46, 0x78],
362        '°' => &[0x00, 0x02, 0x05, 0x02, 0x00],
363        _ => &[0xFF, 0xFF, 0xFF, 0xFF, 0xFF],
364    }
365}
366
367impl<CLK, DIN, DC, CE, RST, LIGHT, ERR> Write for PCD8544<CLK, DIN, DC, CE, RST, LIGHT>
368where
369    CLK: OutputPin<Error = ERR>,
370    DIN: OutputPin<Error = ERR>,
371    DC: OutputPin<Error = ERR>,
372    CE: OutputPin<Error = ERR>,
373    RST: OutputPin<Error = ERR>,
374    LIGHT: OutputPin<Error = ERR>,
375{
376    fn write_str(&mut self, s: &str) -> FmtResult {
377        for char in s.chars() {
378            match char {
379                '\r' => {
380                    self.set_x_position(0).map_err(|_| FmtError)?;
381                }
382                '\n' => {
383                    for _ in 0..(WIDTH - self.x) {
384                        self.write_data(0x00).map_err(|_| FmtError)?;
385                    }
386                }
387                _ => {
388                    for b in char_to_bytes(char) {
389                        self.write_data(*b).map_err(|_| FmtError)?;
390                    }
391                    self.write_data(0x00).map_err(|_| FmtError)?;
392                }
393            }
394        }
395        Ok(())
396    }
397}