Implement low level communication with LCD+begining of the init process

This commit is contained in:
histausse 2022-11-22 23:51:10 +01:00
parent e73303a757
commit 072a2b9af5
Signed by: histausse
GPG key ID: 67486F107F62E9E9
3 changed files with 149 additions and 23 deletions

4
TODO.md Normal file
View file

@ -0,0 +1,4 @@
- Implement the actual mutex
- Implement gpio PIN struct
- Implement singleton to protect hardware wrappers against concurent access
- Implement Error

View file

@ -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
}
}
}

View file

@ -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;