diff --git a/TODO.md b/TODO.md new file mode 100644 index 0000000..d794cdc --- /dev/null +++ b/TODO.md @@ -0,0 +1,4 @@ +- Implement the actual mutex +- Implement gpio PIN struct +- Implement singleton to protect hardware wrappers against concurent access +- Implement Error diff --git a/src/bsp/generic_gpio_drivers/lcd.rs b/src/bsp/generic_gpio_drivers/lcd.rs index 678b08f..8ad611f 100644 --- a/src/bsp/generic_gpio_drivers/lcd.rs +++ b/src/bsp/generic_gpio_drivers/lcd.rs @@ -4,10 +4,28 @@ //! This driver inspired from the one from the arduino libriaries //! (https://github.com/arduino-libraries/LiquidCrystal) and the //! documentation from wikipedia (https://en.wikipedia.org/wiki/Hitachi_HD44780_LCD_controller) +//! and the actual doc (https://www.sparkfun.com/datasheets/LCD/HD44780.pdf) +use crate::bsp::aarch64::time::time_manager; use crate::gpio; +use crate::traits::time::TimeManager; +use core::time::Duration; + +/// The font size +pub enum FontSize { + Font5x8Dots, + Font5x10Dots, +} + +impl FontSize { + fn function_code(&self) -> u8 { + match self { + Self::Font5x8Dots => Lcd::FUNCTION_SET_5X8_DOTS, + Self::Font5x10Dots => Lcd::FUNCTION_SET_5X10_DOTS, + } + } +} -// TODO: change USIZE to a Pin struct (and implement the Pin struct :'( ) /// The structure handling the LCD periferic pub struct Lcd { /// The Register Select pinout. @@ -35,6 +53,23 @@ pub struct Lcd { } impl Lcd { + + /// The instruction to turn off the display + const INSTRUCTION_DISPLAY_OFF: u8 = 0b0000_1000; + /// The instruction to clear the display + const INSTRUCTION_DISPLAY_CLEAR: u8 = 0b0000_0001; + + /// The bit indicating a function set instruction + const FUNCTION_SET_INDICATOR: u8 = 0b0010_0000; + /// The bit indicating the use of only one line + const FUNCTION_SET_ONE_LINE_INDICATOR: u8 = 0b0000_0000; + /// The bit indicating the use of only two lines + const FUNCTION_SET_TWO_LINES_INDICATOR: u8 = 0b0000_1000; + /// The bit indicating the use of the 5x8 font + const FUNCTION_SET_5X8_DOTS: u8 = 0b0000_0000; + /// The bit indicating the use of the 5x10 font + const FUNCTION_SET_5X10_DOTS: u8 = 0b0000_0100; + /// Instanciate a [`Lcd`] object in 4 bits mode. pub const fn new_4bits_mode( rs_pin: usize, @@ -88,7 +123,7 @@ impl Lcd { } /// Initialize the LCD. - pub fn init(&mut self) -> Result::<(), &str> { + pub fn init(&mut self, font_size: FontSize) -> Result<(), &'static str> { gpio::set_pin_fonction(self.rs_pin, gpio::PinFunction::Output)?; gpio::set_pin_output_state(self.rs_pin, gpio::PinOutputState::Low)?; if let Some(rw_pin) = self.rw_pin { @@ -100,42 +135,128 @@ impl Lcd { if let Some(first_data_bits_pins) = self.first_data_bits_pins { for pin in first_data_bits_pins.into_iter() { gpio::set_pin_fonction(pin, gpio::PinFunction::Output)?; + gpio::set_pin_output_state(pin, gpio::PinOutputState::Low)?; } } for pin in self.last_data_bits_pins.into_iter() { gpio::set_pin_fonction(pin, gpio::PinFunction::Output)?; + gpio::set_pin_output_state(pin, gpio::PinOutputState::Low)?; + } + + let db4 = self.last_data_bits_pins[0]; + let db5 = self.last_data_bits_pins[1]; + + // The initialization process as described in figure 23/24 of the doc + // First, wait 15 ms after poweron. To be sure, wait 30 ms + time_manager().sleep(Duration::from_millis(30)); + + // Currently the state of the lcd is undefined. We must make sur + // to be in 8bits mode by sendy 0b0011_0000 3 times. + gpio::set_pin_output_state(db4, gpio::PinOutputState::High)?; + gpio::set_pin_output_state(db5, gpio::PinOutputState::High)?; + self.pulse()?; + time_manager().sleep(Duration::from_micros(4100)); + self.pulse()?; + time_manager().sleep(Duration::from_micros(100)); + self.pulse()?; + time_manager().sleep(Duration::from_micros(100)); + gpio::set_pin_output_state(db4, gpio::PinOutputState::Low)?; + gpio::set_pin_output_state(db5, gpio::PinOutputState::Low)?; + + // Now we are in 8bits mode, if we only have 4 data pins, we + // needs to set the mode to 4 bits. + if self.first_data_bits_pins.is_none() { + gpio::set_pin_output_state(db5, gpio::PinOutputState::High)?; + self.pulse()?; + } + + let mut function = Self::FUNCTION_SET_INDICATOR; + if self.first_data_bits_pins.is_some() { + function |= 0b0001_0000; + } + + if self.nb_lines == 1 { + function |= Self::FUNCTION_SET_ONE_LINE_INDICATOR; + } else if self.nb_lines == 2 { + function |= Self::FUNCTION_SET_TWO_LINES_INDICATOR; + } else { + return Err("Only 1 and 2 lines modes are supported"); } - // TODO: send 0b0011 3 times to make sure to be in 8bits mode 5x8 characters - // TODO: set the new mode with Function set (2 times if 4 bits modes, 1 to set DL=0, - // then the actual one) + function |= font_size.function_code(); + self.send_data(function)?; + self.initialized = true; + + self.display_off()?; + self.display_clear()?; + // TODO: self.set_entry_mode(stuff)?; + todo!(); } - /// Send an 'enable' signal. - fn enable(&self) { - todo!() + /// Turn the display off. + pub fn display_off(&self) -> Result<(), &'static str> { + if self.initialized { + self.send_data(Self::INSTRUCTION_DISPLAY_OFF) + } else { + Err("Cannot control the LCD without initializing it first") + } } - /// Send a Function Set instruction. - fn function_set(&self) { - todo!() + /// Clear the display + pub fn display_clear(&self) -> Result<(), &'static str> { + if self.initialized { + self.send_data(Self::INSTRUCTION_DISPLAY_CLEAR) + } else { + Err("Cannot control the LCD without initializing it first") + } } - /// Send data to the lcd. - fn send_data(&self) { - todo!() + /// Tell the LCD that the data is ready to be read. + fn pulse(&self) -> Result<(), &'static str> { + gpio::set_pin_output_state(self.e_pin, gpio::PinOutputState::Low)?; + time_manager().sleep(Duration::from_nanos(500)); + gpio::set_pin_output_state(self.e_pin, gpio::PinOutputState::High)?; + time_manager().sleep(Duration::from_nanos(500)); + gpio::set_pin_output_state(self.e_pin, gpio::PinOutputState::Low)?; + time_manager().sleep(Duration::from_micros(50)); + Ok(()) } - /// Send 8 bits of data. - fn send_8bit(&self) { - todo!() + /// Send data to the lcd. + fn send_data(&self, data: u8) -> Result<(), &'static str> { + if let Some(first_data_bits_pins) = self.first_data_bits_pins { + for (i, pin) in first_data_bits_pins.iter().enumerate() { + let value = Self::get_bit_value(data, i); + gpio::set_pin_output_state(*pin, value)?; + } + for (i, pin) in self.last_data_bits_pins.iter().enumerate() { + let value = Self::get_bit_value(data, i+4); + gpio::set_pin_output_state(*pin, value)?; + } + self.pulse() + } else { + for (i, pin) in self.last_data_bits_pins.iter().enumerate() { + let value = Self::get_bit_value(data, i+4); + gpio::set_pin_output_state(*pin, value)?; + } + self.pulse()?; + for (i, pin) in self.last_data_bits_pins.iter().enumerate() { + let value = Self::get_bit_value(data, i+4); + gpio::set_pin_output_state(*pin, value)?; + } + self.pulse() + } } - /// Send 4 bits of data. - fn send_4bits(&self) { - // TODO: type? - todo!() + /// Return the value of the pin correponding to the bit `i` of `data`. + #[inline] + fn get_bit_value(data: u8, i: usize) -> gpio::PinOutputState { + if (data & (1 << i)) != 0 { + gpio::PinOutputState::High + } else { + gpio::PinOutputState::Low + } } } diff --git a/src/main.rs b/src/main.rs index 66963d7..e0324f2 100644 --- a/src/main.rs +++ b/src/main.rs @@ -38,6 +38,7 @@ use crate::bsp::rpi3::gpio; use crate::bsp::rpi3::uart::console; use crate::bsp::generic_gpio_drivers::lcd::Lcd; +use crate::bsp::generic_gpio_drivers::lcd; use crate::traits::time::TimeManager; // TODO: move this to BSP @@ -84,7 +85,7 @@ pub unsafe fn _start_rust() -> ! { fatal!("fatal"); let mut lcd = Lcd::new_8bits_mode(1, Some(2), 3, 4, 5, 6, 7, 8, 9, 10, 11, 1, 80); - let _ = lcd.init(); + let _ = lcd.init(lcd::FontSize::Font5x8Dots); let mut buffer = ['X'; 200]; let mut i = 0;