#embedded-hal-driver #waveshare #display #driver #spi-driver #no-alloc

no-std lcd-ili9341-spi

Embedded_hal driver for ILI9341 SPI LCD, compatible with Waveshare 2,4 inch board

2 releases

0.1.1 Aug 9, 2024
0.1.0 Aug 8, 2024

#471 in Embedded development

Download history 185/week @ 2024-08-05 21/week @ 2024-08-12

206 downloads per month

MIT license

22KB
380 lines

ILI9341 LCD Spi driver

crates.io Documentation

Device agnostic LCD driver for Waveshare 2,4" board, based on ILI9341.

no_std and embedded_hal=1.0 compatibile.

Currently supports:

  • Rect / line drawing
  • Sprite buffer rendering
  • Basic text rendering (via a text feature)
  • Backlight level setting (via a PWM pin)

Text rendering

As the text rendering requires an embedded font, it has been hidden behind a feature flag in order to save memory when not needed. The font supports basic ASCII range: codes 32 - 128 (each character takes 8 bytes).

Required hardware connections:

  • SPI: impl SpiBus (from the embedded_hal)
  • DC pin: Data / Command selector - Digital Pin
  • RST pin: Device reset - Digital Pin
  • BL pin: backlight level - PWM pin

Arduino Uno Example

#![no_std]
#![no_main]
#![feature(abi_avr_interrupt)]

use arduino_hal::prelude::*;
use arduino_hal::simple_pwm::*;
use arduino_hal::spi;
use core::panic::PanicInfo;
use lcd_ili9341_spi::{
    Lcd, rgb_to_u16, rgb_to_u8, LcdOrientation
};

#[panic_handler]
fn panic(_info: &PanicInfo) -> ! {
    loop {}
}

#[arduino_hal::entry]
fn main() -> ! {
    let dp = arduino_hal::Peripherals::take().unwrap();
    let pins = arduino_hal::pins!(dp);

    let spi_settings = spi::Settings {
        data_order: spi::DataOrder::MostSignificantFirst,
        clock: spi::SerialClockRate::OscfOver2,
        mode: embedded_hal::spi::MODE_0,
    };

    let (mut arduino_spi, _) = arduino_hal::Spi::new(
        dp.SPI,
        pins.d13.into_output(),        
        pins.d11.into_output(),
        pins.d12.into_pull_up_input(),
        pins.d10.into_output(),
        spi_settings
    );
    
    let timer1 = Timer1Pwm::new(dp.TC1, Prescaler::Direct);
    let mut bl_pin = pins.d9.into_output().into_pwm(&timer1);
    bl_pin.enable();

    let mut lcd = Lcd::new(
            arduino_spi,
            pins.d7.into_output(),
            pins.d8.into_output(),
            bl_pin
        )
        .with_orientation(LcdOrientation::Rotate90);

    let mut delay = arduino_hal::Delay::new();
    let _ = lcd.init(&mut delay);

    let _ = lcd.set_backlight(140);

    // Make the screen black
    let _ = lcd.clear(0x0000);
    // Draw magenta rect
    lcd.fill_rect(10, 10, 20, 30, rgb_to_u16(255, 0, 255));
    // Draw blue-ish horizontal line
    lcd.fill_rect(0, 50, 320, 1, rgb_to_u16(0, 128, 255));

    // Draw 4x4 px red-black checker
    let (red_h, red_l) = rgb_to_u8(255, 0, 0);
    let sprite = [
        red_h, red_l, red_h, red_l, 0, 0, 0, 0,
        red_h, red_l, red_h, red_l, 0, 0, 0, 0,
        0, 0, 0, 0, red_h, red_l, red_h, red_l,
        0, 0, 0, 0, red_h, red_l, red_h, red_l,
    ];

    lcd.draw_sprite(50, 70, 4, 4, &sprite);

    loop {
        arduino_hal::delay_ms(100);
    }
}

Raspberry Pi Example

use lcd_ili9341_spi::{rgb_to_u16, rgb_to_u8, Lcd, LcdOrientation};
use rppal::gpio::Gpio;
use rppal::hal::Delay;
use rppal::pwm::{Channel, Polarity, Pwm};
use rppal::spi::{Bus, Mode, Segment, SlaveSelect, Spi};

fn main() {
    let dc_pin = Gpio::new().unwrap().get(25).unwrap().into_output();
    let rst_pin = Gpio::new().unwrap().get(27).unwrap().into_output();
    let bl_pin = PwmHal(Pwm::with_frequency(Channel::Pwm0, 512.0, 0.5, Polarity::Normal, true).unwrap()); 

    let mut pi_spi = Spi::new(
        Bus::Spi0,
        SlaveSelect::Ss0,
        8_000_000,
        rppal::spi::Mode::Mode0,
    )
    .unwrap();

    let mut lcd = Lcd::new(pi_spi, dc_pin, rst_pin, bl_pin);
    let mut delay = Delay::new();
    let _ = lcd.init(&mut delay);

    let _ = lcd.set_backlight(140);

    // Make the screen black
    let _ = lcd.clear(0x0000);
    // Draw magenta colored rect
    lcd.fill_rect(10, 10, 20, 30, rgb_to_u16(255, 0, 255));
    // Render text
    lcd.draw_text(10, 120, "RustberryPi", rgb_to_u16(0, 128, 255), 0x0000, 2);

    std::thread::sleep(std::time::Duration::from_secs(15));
}

// SetDutyCycle impl is missing in the rppal crate?
struct PwmHal(Pwm);
impl embedded_hal::pwm::SetDutyCycle for PwmHal {
    fn max_duty_cycle(&self) -> u16 {
        255
    }
    fn set_duty_cycle(&mut self, duty: u16) -> Result<(), Self::Error> {
        self.0.set_duty_cycle(duty as f64 / 255.0);
        Ok(())
    }
}
impl embedded_hal::pwm::ErrorType for PwmHal {
    type Error = PwmError;
}

#[derive(Debug)]
struct PwmError;
impl embedded_hal::pwm::Error for PwmError {
    fn kind(&self) -> embedded_hal::pwm::ErrorKind {
        embedded_hal::pwm::ErrorKind::Other
    }
}

Dependencies

~56KB