Implement low level communication with LCD+begining of the init process
This commit is contained in:
parent
e73303a757
commit
072a2b9af5
3 changed files with 149 additions and 23 deletions
4
TODO.md
Normal file
4
TODO.md
Normal file
|
@ -0,0 +1,4 @@
|
|||
- Implement the actual mutex
|
||||
- Implement gpio PIN struct
|
||||
- Implement singleton to protect hardware wrappers against concurent access
|
||||
- Implement Error
|
|
@ -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)?;
|
||||
}
|
||||
|
||||
// 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)
|
||||
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");
|
||||
}
|
||||
|
||||
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")
|
||||
}
|
||||
}
|
||||
|
||||
/// 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 data to the lcd.
|
||||
fn send_data(&self) {
|
||||
todo!()
|
||||
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 8 bits of data.
|
||||
fn send_8bit(&self) {
|
||||
todo!()
|
||||
}
|
||||
|
||||
/// 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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
Loading…
Reference in a new issue