liquidcrystal_i2c_rs/
lib.rs

1#![no_std]
2
3// Port of the liquide crystall I2C lirary found for arduino in rust. 
4// Tested on raspberry pi. 
5
6// Example of use:
7
8// ```rust
9// use rppal::{gpio::Gpio, i2c::I2c};
10
11// static  LCD_ADDRESS: u8 = 0x27;
12
13// fn setup() {
14
15// }
16// fn main() {
17//     let mut i2c = I2c::new().unwrap();
18//     let mut delay = rppal::hal::Delay;
19
20//     let mut lcd = screen::Lcd::new(&mut i2c, LCD_ADDRESS, &mut delay).unwrap();
21    
22//     lcd.set_display(screen::Display::On).unwrap();
23//     lcd.set_backlight(screen::Backlight::On).unwrap();
24//     lcd.print("Hello world!").unwrap();
25// }
26
27// ```
28
29use embedded_hal::blocking::{i2c, delay::DelayMs};
30
31/// Controls the visibilty of the non-blinking cursor, which is basically an _ **after** the cursor position.
32/// The cursor position represents where the next character will show up.
33#[derive(Copy, Clone, Debug)]
34pub enum Cursor {
35    /// Display the non-blinking cursor
36    On = 0x02,
37    /// Hide the non-blinking cursor
38    Off = 0x00,
39}
40
41/// Controls the visibility of the blinking block cursor.
42#[derive(Copy, Clone, Debug)]
43pub enum Blink {
44    /// Turn the blinking block cursor on
45    On = 0x01,
46    /// Turn the blinking block cursor off
47    Off = 0x00,
48}
49
50/// Determines whether the entire LCD is on or off.
51#[derive(Copy, Clone, Debug)]
52pub enum Display {
53    /// Turn the LCD display on
54    On = 0x04,
55    /// Turn the LCD display off
56    Off = 0x00,
57}
58
59/// Determines whether the blaclight is on or off.
60#[derive(Copy, Clone, Debug)]
61pub enum Backlight {
62    /// Turn the backlight on
63    On = 0x08,
64    /// Turn the backlight off
65    Off = 0x00,
66}
67
68/// Commands
69#[derive(Copy, Clone, Debug)]
70pub enum Mode {
71    COMMAND = 0x00,
72    CLEARDISPLAY = 0x01,
73    RETURNHOME = 0x02,
74    ENTRYMODESET = 0x04,
75    DISPLAYCONTROL = 0x08,
76    CURSORSHIFT = 0x10,
77    FUNCTIONSET = 0x20,
78    SETCGRAMADDR = 0x40,
79    SETDDRAMADDR = 0x80,
80}
81
82/// flags for display entry mode
83#[derive(Copy, Clone, Debug)]
84pub enum Entries {
85    RIGHT = 0x00,
86    LEFT = 0x02,
87}
88
89/// Flag for selection the display of cursor
90#[derive(Copy, Clone, Debug)]
91pub enum MoveSelect {
92    DISPLAY = 0x08,
93    CURSOR = 0x00,
94}
95
96// flags for selection the direction to wite in.
97#[derive(Copy, Clone, Debug)]
98pub enum Direction {
99    RIGHT = 0x04,
100    LEFT = 0x00,
101}
102
103
104#[derive(Copy, Clone, Debug)]
105pub enum Shift {
106    INCREMENT = 0x01,
107    DECREMENT = 0x00,
108}
109
110#[derive(Copy, Clone, Debug)]
111pub enum BitMode {
112    Bit4 = 0x00,
113    Bit8 = 0x10,
114}
115
116#[derive(Copy, Clone, Debug)]
117pub enum Dots {
118    Dots5x8 = 0x00,
119    Dots5x10 = 0x04,
120}
121
122#[derive(Copy, Clone, Debug)]
123pub enum Lines {
124    OneLine = 0x00,
125    TwoLine = 0x08,
126}
127
128#[derive(Copy, Clone, Debug)]
129pub enum BitAction {
130    Command = 0x00,
131    Enable = 0x04,
132    ReadWrite = 0x02,
133    RegisterSelect = 0x01
134}
135
136
137pub struct DisplayControl {
138    pub cursor: Cursor,
139    pub display: Display,
140    pub blink: Blink,
141    pub backlight: Backlight,
142    pub direction: Direction,
143}
144
145impl DisplayControl {
146    pub fn new() -> Self {
147        DisplayControl {
148            cursor: Cursor::Off,
149            display: Display::Off,
150            blink: Blink::Off,
151            backlight: Backlight::On,
152            direction: Direction::LEFT,
153        }
154    }
155
156    pub fn value(&self) -> u8 {
157        self.blink as u8 | 
158        self.cursor as u8 | 
159        self.display as u8 | 
160        self.backlight as u8
161    }
162}
163
164
165
166pub struct Lcd<'a, I, D>
167where
168    I: i2c::Write,
169    D: DelayMs<u8>,
170{
171    i2c: &'a mut I,
172    control: DisplayControl,
173    address: u8,
174    delay: &'a mut D,
175}
176
177impl<'a, I, D> Lcd<'a, I, D>
178where
179    I: i2c::Write,
180    D: DelayMs<u8>,
181    {
182
183    pub fn new(i2c: &'a mut I, address: u8, delay: &'a mut D) -> Result<Self, <I as i2c::Write>::Error>  {
184        let mut display = Lcd {
185            i2c,
186            control: DisplayControl::new(),
187            address,
188            delay
189        };
190        display.init()?;
191        Ok(display)
192    }
193
194    // Initialize the display for the first time after power up
195    fn init(&mut self) -> Result<(), <I as i2c::Write>::Error>
196     {
197
198        // SEE PAGE 45/46 FOR INITIALIZATION SPECIFICATION!
199        // according to datasheet, we need at least 40ms after power rises above 2.7V
200        // before sending commands. Arduino can turn on way befer 4.5V so we'll wait 50
201        self.delay.delay_ms(50); 
202
203        self.expander_write(self.control.backlight as u8)?;
204        self.delay.delay_ms(1); 
205
206 
207        // Send the initial command sequence according to the HD44780 datasheet
208        let mode_8bit = Mode::FUNCTIONSET as u8 | BitMode::Bit8 as u8;
209        self.write4bits(mode_8bit)?;
210        self.delay.delay_ms(5);
211
212        self.write4bits(mode_8bit)?;
213        self.delay.delay_ms(5);
214
215        self.write4bits(mode_8bit)?;
216        self.delay.delay_ms(5);
217
218        let mode_4bit = Mode::FUNCTIONSET as u8 | BitMode::Bit4 as u8;
219        self.write4bits(mode_4bit)?;
220        self.delay.delay_ms(5);
221
222
223        let lines_font = Mode::FUNCTIONSET as u8 | BitMode::Bit4 as u8 | Dots::Dots5x8 as u8 | Lines::TwoLine as u8;
224        self.command(lines_font)?;
225
226        self.clear()?;
227
228        let entry_mode = Mode::ENTRYMODESET as u8 | Entries::LEFT as u8 | Shift::DECREMENT as u8;
229        self.command(entry_mode)?;
230
231        Ok(())
232    }
233
234
235    /********** high level commands, for the user! */
236    /**
237    Clear the display. The LCD display driver requires a 2ms delay after clearing, which
238    is why this method requires a `delay` object.
239
240    # Errors
241
242    Returns a `Result` that will report I2C errors, if any.
243    */
244    pub fn clear(&mut self) -> Result<(), <I as i2c::Write>::Error> {
245        self.command(Mode::CLEARDISPLAY as u8)?;
246        self.delay.delay_ms(2);
247        Ok(())
248    }
249
250    /**
251    Home
252
253    # Errors
254
255    Returns a `Result` that will report I2C errors, if any.
256    */
257    pub fn home(&mut self) -> Result<(), <I as i2c::Write>::Error> {
258        self.command(Mode::RETURNHOME as u8)?;
259        self.delay.delay_ms(2);
260        Ok(())
261    }
262
263    /**
264    Set the position of the cursor
265
266    # Errors
267
268    Returns a `Result` that will report I2C errors, if any.
269    */
270    pub fn set_cursor_position(&mut self, col: u8, row: u8) -> Result<(), <I as i2c::Write>::Error> {
271        // let row_offsets = [0x00, 0x40, 0x14, 0x54];
272        // let row = if row > 2 {
273        //     1
274        // } else {
275        //     row
276        // };
277        self.command(Mode::SETDDRAMADDR as u8 | (col + row *0x40))?;
278        Ok(())
279    }
280
281    /**
282    Control whether the display is on or off
283
284    # Errors
285
286    Returns a `Result` that will report I2C errors, if any.
287    */
288    pub fn set_display(&mut self, display: Display) -> Result<(), <I as i2c::Write>::Error> {
289        self.control.display = display;
290        self.write_display_control()
291    }
292
293    /**
294    Sets the visiblity of the cursor, which is a non-blinking _
295
296    # Errors
297
298    Returns a `Result` that will report I2C errors, if any.
299    */
300    pub fn set_cursor(&mut self, cursor: Cursor) -> Result<(), <I as i2c::Write>::Error> {
301        self.control.cursor = cursor;
302        self.write_display_control()
303    }
304
305    /**
306    Turns on the blinking block cursor
307
308    # Errors
309
310    Returns a `Result` that will report I2C errors, if any.
311    */
312    pub fn set_blink(&mut self, blink: Blink) -> Result<(), <I as i2c::Write>::Error> {
313        self.control.blink = blink;
314        self.write_display_control()
315    }
316
317
318    pub fn set_backlight(&mut self, backlight: Backlight)-> Result<(), <I as i2c::Write>::Error> {
319        self.control.backlight = backlight;
320        self.expander_write(0)
321    }
322
323
324
325
326
327    
328    /*********** mid level commands, for sending data/cmds */
329
330     /**
331    Adds a string to the current position. The cursor will advance
332    after this call to the next column
333    # Errors
334    Returns a `Result` that will report I2C errors, if any.
335    */
336    pub fn print(&mut self, s: &str) -> Result<(), <I as i2c::Write>::Error> {
337        for c in s.chars() {
338            self.write(c as u8)?;
339        }
340
341        Ok(())
342    }
343
344    // Set one of the display's control options and then send the updated set of options to the display
345    fn write_display_control(&mut self) -> Result<(), <I as i2c::Write>::Error> {
346        self.command(Mode::DISPLAYCONTROL as u8 | self.control.value())
347    }
348
349
350    // Send two bytes to the display
351    fn write(&mut self, value: u8) -> Result<(), <I as i2c::Write>::Error> {
352        self.send(value, BitAction::RegisterSelect)
353    }
354    
355    fn command(&mut self, value: u8) -> Result<(), <I as i2c::Write>::Error> {
356        self.send(value, BitAction::Command)
357    }
358
359    /************ low level data pushing commands **********/
360
361
362    fn send(&mut self, data: u8, mode: BitAction) -> Result<(), <I as i2c::Write>::Error> {
363        let high_bits: u8 = data & 0xf0;
364        let low_bits: u8 = (data << 4) & 0xf0;
365        self.write4bits(high_bits | mode as u8)?;
366        self.write4bits(low_bits | mode as u8)?;
367        Ok(())
368    }
369
370    fn write4bits(&mut self, value: u8)  -> Result<(), <I as i2c::Write>::Error> {
371        self.expander_write(value)?;
372        self.pulse_enable(value)?;
373        Ok(())
374    }
375
376    fn expander_write(&mut self, data: u8) -> Result<(), <I as i2c::Write>::Error> {                                        
377        self.i2c.write(self.address, &[data | self.control.backlight as u8])
378    }
379
380    fn pulse_enable(&mut self, data: u8) -> Result<(), <I as i2c::Write>::Error> {
381        self.expander_write(data | BitAction::Enable as u8)?;	// En high
382        self.delay.delay_ms(1);
383        
384        self.expander_write(data & !(BitAction::Enable as u8))?;	// En low
385        self.delay.delay_ms(1);
386
387        Ok(())
388    } 
389    
390
391
392
393}