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 self.rst.set_low()?;
106 self.rst.set_high()?;
107
108 self.power_down_control = false;
110 self.entry_mode = false;
111 self.extended_instruction_set = false;
112
113 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 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 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}