Start implementing LCD driver
This commit is contained in:
parent
adc45f6aa2
commit
e73303a757
14 changed files with 284 additions and 154 deletions
|
@ -3,8 +3,8 @@
|
|||
use core::arch::asm;
|
||||
use core::time::Duration;
|
||||
|
||||
use crate::traits::time::TimeManager;
|
||||
use crate::println;
|
||||
use crate::traits::time::TimeManager;
|
||||
|
||||
const NS_PER_S: u64 = 1_000_000_000;
|
||||
const CNTP_CTL_EL0_ENABLE_MASK: u64 = 0b001;
|
||||
|
|
141
src/bsp/generic_gpio_drivers/lcd.rs
Normal file
141
src/bsp/generic_gpio_drivers/lcd.rs
Normal file
|
@ -0,0 +1,141 @@
|
|||
//! Driver for LiquidCrystal Displays plugs to the GPIO of the board.
|
||||
//! The LCD need to be an Hitachi HD44780 chipset compatible.
|
||||
//!
|
||||
//! 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)
|
||||
|
||||
use crate::gpio;
|
||||
|
||||
// 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.
|
||||
rs_pin: usize,
|
||||
/// The Read/Write pinout.
|
||||
///
|
||||
/// If set to None, assumes the rw pin of the lcd is connected to the ground (write state)
|
||||
rw_pin: Option<usize>,
|
||||
/// The Enable pinout.
|
||||
///
|
||||
/// TODO: wiki says falling-edge triggered, arduino comment says activated by a HIGH pulse.
|
||||
e_pin: usize,
|
||||
/// First 4 data bits pins.
|
||||
///
|
||||
/// Set to None if the lcd is in 4-bit mode.
|
||||
first_data_bits_pins: Option<[usize; 4]>,
|
||||
/// Last 4 data bits pins.
|
||||
last_data_bits_pins: [usize; 4],
|
||||
/// The number of line of the LCD.
|
||||
nb_lines: usize,
|
||||
/// The number of row of the LCD.
|
||||
nb_rows: usize,
|
||||
/// Weither or not the LCD has been initialized in the rigth state.
|
||||
initialized: bool,
|
||||
}
|
||||
|
||||
impl Lcd {
|
||||
/// Instanciate a [`Lcd`] object in 4 bits mode.
|
||||
pub const fn new_4bits_mode(
|
||||
rs_pin: usize,
|
||||
rw_pin: Option<usize>,
|
||||
e_pin: usize,
|
||||
db4: usize,
|
||||
db5: usize,
|
||||
db6: usize,
|
||||
db7: usize,
|
||||
nb_lines: usize,
|
||||
nb_rows: usize,
|
||||
) -> Self {
|
||||
Self {
|
||||
rs_pin,
|
||||
rw_pin,
|
||||
e_pin,
|
||||
first_data_bits_pins: None,
|
||||
last_data_bits_pins: [db4, db5, db6, db7],
|
||||
nb_lines,
|
||||
nb_rows,
|
||||
initialized: false,
|
||||
}
|
||||
}
|
||||
|
||||
/// Instanciate a [`Lcd`] object in 8 bits mode.
|
||||
pub const fn new_8bits_mode(
|
||||
rs_pin: usize,
|
||||
rw_pin: Option<usize>,
|
||||
e_pin: usize,
|
||||
db0: usize,
|
||||
db1: usize,
|
||||
db2: usize,
|
||||
db3: usize,
|
||||
db4: usize,
|
||||
db5: usize,
|
||||
db6: usize,
|
||||
db7: usize,
|
||||
nb_lines: usize,
|
||||
nb_rows: usize,
|
||||
) -> Self {
|
||||
Self {
|
||||
rs_pin,
|
||||
rw_pin,
|
||||
e_pin,
|
||||
first_data_bits_pins: Some([db0, db1, db2, db3]),
|
||||
last_data_bits_pins: [db4, db5, db6, db7],
|
||||
nb_lines,
|
||||
nb_rows,
|
||||
initialized: false,
|
||||
}
|
||||
}
|
||||
|
||||
/// Initialize the LCD.
|
||||
pub fn init(&mut self) -> Result::<(), &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 {
|
||||
gpio::set_pin_fonction(rw_pin, gpio::PinFunction::Output)?;
|
||||
gpio::set_pin_output_state(rw_pin, gpio::PinOutputState::Low)?;
|
||||
}
|
||||
gpio::set_pin_fonction(self.e_pin, gpio::PinFunction::Output)?;
|
||||
gpio::set_pin_output_state(self.e_pin, gpio::PinOutputState::Low)?;
|
||||
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)?;
|
||||
}
|
||||
}
|
||||
for pin in self.last_data_bits_pins.into_iter() {
|
||||
gpio::set_pin_fonction(pin, gpio::PinFunction::Output)?;
|
||||
}
|
||||
|
||||
// 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)
|
||||
self.initialized = true;
|
||||
todo!();
|
||||
}
|
||||
|
||||
/// Send an 'enable' signal.
|
||||
fn enable(&self) {
|
||||
todo!()
|
||||
}
|
||||
|
||||
/// Send a Function Set instruction.
|
||||
fn function_set(&self) {
|
||||
todo!()
|
||||
}
|
||||
|
||||
/// Send data to the lcd.
|
||||
fn send_data(&self) {
|
||||
todo!()
|
||||
}
|
||||
|
||||
/// Send 8 bits of data.
|
||||
fn send_8bit(&self) {
|
||||
todo!()
|
||||
}
|
||||
|
||||
/// Send 4 bits of data.
|
||||
fn send_4bits(&self) {
|
||||
// TODO: type?
|
||||
todo!()
|
||||
}
|
||||
}
|
3
src/bsp/generic_gpio_drivers/mod.rs
Normal file
3
src/bsp/generic_gpio_drivers/mod.rs
Normal file
|
@ -0,0 +1,3 @@
|
|||
//! Generic driver for perifierics to plug to the GPIO.
|
||||
|
||||
pub mod lcd;
|
|
@ -9,3 +9,6 @@ pub mod rpi4;
|
|||
|
||||
#[cfg(target_arch = "aarch64")]
|
||||
pub mod aarch64;
|
||||
|
||||
// TODO: only for bsp with support for GPIO
|
||||
pub mod generic_gpio_drivers;
|
||||
|
|
|
@ -71,7 +71,9 @@ pub fn set_pin_fonction(n: usize, function: PinFunction) -> Result<(), &'static
|
|||
let mut val = unsafe { core::ptr::read_volatile(address as *mut u32) };
|
||||
val &= !field.get_mask();
|
||||
val |= Into::<u32>::into(function) << field.get_offset();
|
||||
unsafe { core::ptr::write_volatile(address as *mut u32, val); }
|
||||
unsafe {
|
||||
core::ptr::write_volatile(address as *mut u32, val);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
@ -92,6 +94,8 @@ pub fn set_pin_output_state(n: usize, state: PinOutputState) -> Result<(), &'sta
|
|||
let val = field.get_mask();
|
||||
let address = field.get_address();
|
||||
// 0 has no effect on this field: no nead to read-modify-write
|
||||
unsafe { core::ptr::write_volatile(address as *mut u32, val); }
|
||||
unsafe {
|
||||
core::ptr::write_volatile(address as *mut u32, val);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
@ -2,7 +2,6 @@
|
|||
|
||||
use crate::utils::field::Field;
|
||||
|
||||
|
||||
pub const START_PHYSICAL_ADDRESS: usize = 0x3F00_0000;
|
||||
|
||||
// ** GPIO addresses **
|
||||
|
@ -300,7 +299,6 @@ pub mod uart {
|
|||
/// Field located inside the Data Register [`dr`].
|
||||
pub dr_data: Field,
|
||||
|
||||
|
||||
/// Receive Status Register/Error Clear Register, 32 bits long.
|
||||
///
|
||||
/// # Bits distribution:
|
||||
|
@ -658,7 +656,6 @@ pub mod uart {
|
|||
pub cr_uarten: Field,
|
||||
|
||||
// TODO: Finish detailling the registers
|
||||
|
||||
/// Interrupt FIFO Level Select register, 32 bits long.
|
||||
///
|
||||
/// # Bits distribution:
|
||||
|
@ -809,7 +806,6 @@ pub mod uart {
|
|||
/// - 31-11: reserved. Write as 0, value read don't have meaning, R.
|
||||
/// - 10-0: TDR10_0. When ITCRI1 is set to 1, data read an write directly from the FIFOs, R/W.
|
||||
pub tdr: Field,
|
||||
|
||||
}
|
||||
|
||||
impl UartMemoryMap {
|
||||
|
@ -878,5 +874,4 @@ pub mod uart {
|
|||
}
|
||||
|
||||
pub const UART: UartMemoryMap = UartMemoryMap::new(UART_START);
|
||||
|
||||
}
|
||||
|
|
|
@ -20,7 +20,10 @@ pub struct UartInner<'a> {
|
|||
impl<'a> UartInner<'a> {
|
||||
/// Constructor for [`Uart`].
|
||||
const fn new(memory_map: &'a mm::UartMemoryMap) -> Self {
|
||||
Self { initialized: false, memory_map }
|
||||
Self {
|
||||
initialized: false,
|
||||
memory_map,
|
||||
}
|
||||
}
|
||||
|
||||
/// Initialise the UART.
|
||||
|
@ -153,14 +156,11 @@ impl Read for Uart<'_> {
|
|||
}
|
||||
|
||||
impl<'a> Uart<'a> {
|
||||
|
||||
// TODO: not sure this should be public? public in bsp only?
|
||||
/// Create a new UART object
|
||||
const fn new(memory_map: &'a mm::UartMemoryMap) -> Self {
|
||||
Self {
|
||||
inner: DummyMutex::new(
|
||||
UartInner::new(memory_map)
|
||||
)
|
||||
inner: DummyMutex::new(UartInner::new(memory_map)),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -12,4 +12,3 @@ pub const UART2_START: usize = START_LOW_PERIPHERAL_MODE + 0x0020_1400;
|
|||
pub const UART3_START: usize = START_LOW_PERIPHERAL_MODE + 0x0020_1600;
|
||||
pub const UART4_START: usize = START_LOW_PERIPHERAL_MODE + 0x0020_1800;
|
||||
pub const UART5_START: usize = START_LOW_PERIPHERAL_MODE + 0x0020_1A00;
|
||||
|
||||
|
|
24
src/main.rs
24
src/main.rs
|
@ -9,35 +9,35 @@
|
|||
#![cfg_attr(not(test), no_std)]
|
||||
#![feature(format_args_nl)]
|
||||
#![feature(panic_info_message)]
|
||||
|
||||
#![allow(dead_code)]
|
||||
|
||||
mod traits;
|
||||
mod bsp;
|
||||
mod traits;
|
||||
mod utils;
|
||||
|
||||
#[cfg(not(test))]
|
||||
mod log;
|
||||
#[cfg(not(test))]
|
||||
mod panic;
|
||||
#[cfg(not(test))]
|
||||
mod print;
|
||||
#[cfg(not(test))]
|
||||
mod log;
|
||||
|
||||
#[cfg(not(test))]
|
||||
use core::arch::global_asm;
|
||||
#[cfg(not(test))]
|
||||
use core::arch::asm;
|
||||
#[cfg(not(test))]
|
||||
use core::arch::global_asm;
|
||||
|
||||
use core::time::Duration;
|
||||
|
||||
// TODO: handle this with features
|
||||
#[cfg(not(test))]
|
||||
use crate::bsp::rpi3::uart::console;
|
||||
use crate::bsp::aarch64::time::time_manager;
|
||||
#[cfg(not(test))]
|
||||
use crate::bsp::rpi3::gpio;
|
||||
#[cfg(not(test))]
|
||||
use crate::bsp::aarch64::time::time_manager;
|
||||
use crate::bsp::rpi3::uart::console;
|
||||
|
||||
use crate::bsp::generic_gpio_drivers::lcd::Lcd;
|
||||
use crate::traits::time::TimeManager;
|
||||
|
||||
// TODO: move this to BSP
|
||||
|
@ -46,7 +46,9 @@ use crate::traits::time::TimeManager;
|
|||
#[inline(always)]
|
||||
pub fn wait_forever() -> ! {
|
||||
loop {
|
||||
unsafe { asm!("wfe"); }
|
||||
unsafe {
|
||||
asm!("wfe");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -58,7 +60,6 @@ global_asm!(include_str!("boot.s"));
|
|||
#[cfg(not(test))]
|
||||
#[no_mangle]
|
||||
pub unsafe fn _start_rust() -> ! {
|
||||
|
||||
// Set TX and RX function for pin 14 and 15
|
||||
match gpio::set_pin_fonction(14, gpio::PinFunction::AltFunc0) {
|
||||
_ => (),
|
||||
|
@ -82,6 +83,9 @@ pub unsafe fn _start_rust() -> ! {
|
|||
error!("error");
|
||||
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 mut buffer = ['X'; 200];
|
||||
let mut i = 0;
|
||||
let mut c = console().read_char();
|
||||
|
|
|
@ -36,7 +36,9 @@ fn panic(info: &PanicInfo) -> ! {
|
|||
"Kernel panic!\n\n\
|
||||
Panic location:\n File: '{}', line {}, column {}\n\n\
|
||||
{}",
|
||||
location, line, column,
|
||||
location,
|
||||
line,
|
||||
column,
|
||||
info.message().unwrap_or(&format_args!(""))
|
||||
);
|
||||
wait_forever()
|
||||
|
|
|
@ -27,7 +27,6 @@ pub trait Read {
|
|||
fn flush_input(&self);
|
||||
}
|
||||
|
||||
|
||||
/// A Dummy Console.
|
||||
///
|
||||
/// The DummyConsole implement the [`Console`] trait, and do nothing.
|
||||
|
@ -51,5 +50,4 @@ impl Read for DummyConsole {
|
|||
fn flush_input(&self) {}
|
||||
}
|
||||
|
||||
|
||||
impl Console for DummyConsole {}
|
||||
|
|
|
@ -3,7 +3,6 @@
|
|||
use core::time::Duration;
|
||||
|
||||
pub trait TimeManager {
|
||||
|
||||
/// Return the clock frequency, meaning the resolution of the
|
||||
/// time mesurement.
|
||||
fn frequency(&self) -> u64;
|
||||
|
|
|
@ -24,7 +24,7 @@ pub struct Field {
|
|||
/// The size of the field in bit.
|
||||
size: usize,
|
||||
/// The mask for the field in the `u32` at `address`.
|
||||
mask: u32
|
||||
mask: u32,
|
||||
}
|
||||
|
||||
impl Field {
|
||||
|
@ -45,7 +45,12 @@ impl Field {
|
|||
|
||||
let mask = Self::compute_mask(offset, size);
|
||||
|
||||
Self { address, offset, size, mask }
|
||||
Self {
|
||||
address,
|
||||
offset,
|
||||
size,
|
||||
mask,
|
||||
}
|
||||
}
|
||||
|
||||
/// Compute mask for the field.
|
||||
|
@ -86,7 +91,11 @@ impl Field {
|
|||
/// Set the value of the field in the provided u32.
|
||||
#[inline]
|
||||
pub fn read_and_write_to_u32(&self, val: u32, register: u32) -> u32 {
|
||||
assert_eq!(val & !(self.mask >> self.offset), 0, "Value greater than the field");
|
||||
assert_eq!(
|
||||
val & !(self.mask >> self.offset),
|
||||
0,
|
||||
"Value greater than the field"
|
||||
);
|
||||
register & !self.mask | (val << self.offset)
|
||||
}
|
||||
|
||||
|
@ -124,26 +133,17 @@ mod test {
|
|||
#[test]
|
||||
fn test_get_address() {
|
||||
// Test when the address is alligned
|
||||
assert_eq!(
|
||||
Field::new(0x1234_5678, 0, 1).get_address(),
|
||||
0x1234_5678,
|
||||
);
|
||||
assert_eq!(Field::new(0x1234_5678, 0, 1).get_address(), 0x1234_5678,);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_get_offset() {
|
||||
assert_eq!(
|
||||
Field::new(0x1234_5678, 18, 1).get_offset(),
|
||||
18,
|
||||
);
|
||||
assert_eq!(Field::new(0x1234_5678, 18, 1).get_offset(), 18,);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_get_size() {
|
||||
assert_eq!(
|
||||
Field::new(0x1234_5678, 18, 1).get_size(),
|
||||
1,
|
||||
);
|
||||
assert_eq!(Field::new(0x1234_5678, 18, 1).get_size(), 1,);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -188,32 +188,17 @@ mod test {
|
|||
|
||||
#[test]
|
||||
fn test_get_mask() {
|
||||
assert_eq!(
|
||||
Field::new(0, 0, 1).get_mask(),
|
||||
0b0000_0001,
|
||||
);
|
||||
assert_eq!(
|
||||
Field::new(0, 0, 3).get_mask(),
|
||||
0b0000_0111,
|
||||
);
|
||||
assert_eq!(
|
||||
Field::new(0, 4, 1).get_mask(),
|
||||
0b0001_0000,
|
||||
);
|
||||
assert_eq!(
|
||||
Field::new(0, 4, 3).get_mask(),
|
||||
0b0111_0000,
|
||||
);
|
||||
assert_eq!(Field::new(0, 0, 1).get_mask(), 0b0000_0001,);
|
||||
assert_eq!(Field::new(0, 0, 3).get_mask(), 0b0000_0111,);
|
||||
assert_eq!(Field::new(0, 4, 1).get_mask(), 0b0001_0000,);
|
||||
assert_eq!(Field::new(0, 4, 3).get_mask(), 0b0111_0000,);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_read_and_write_to_u32() {
|
||||
let field = Field::new(0, 3, 4);
|
||||
let val = 0b0101_0101;
|
||||
assert_eq!(
|
||||
field.read_and_write_to_u32(0b0101, val),
|
||||
0b0010_1101,
|
||||
);
|
||||
assert_eq!(field.read_and_write_to_u32(0b0101, val), 0b0010_1101,);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -227,9 +212,6 @@ mod test {
|
|||
fn test_read_from_u32() {
|
||||
let field = Field::new(0, 3, 4);
|
||||
let val = 0b1100_1111;
|
||||
assert_eq!(
|
||||
field.read_from_u32(val),
|
||||
0b1001,
|
||||
);
|
||||
assert_eq!(field.read_from_u32(val), 0b1001,);
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue