Some refactoring to move ssh1106 to its own mod
This commit is contained in:
121
src/main.rs
121
src/main.rs
@@ -4,6 +4,7 @@
|
|||||||
#![no_std]
|
#![no_std]
|
||||||
#![no_main]
|
#![no_main]
|
||||||
|
|
||||||
|
use defmt::println;
|
||||||
use embedded_hal::{digital::OutputPin, i2c::I2c};
|
use embedded_hal::{digital::OutputPin, i2c::I2c};
|
||||||
use hal::{fugit::RateExtU32, pac};
|
use hal::{fugit::RateExtU32, pac};
|
||||||
use rp2040_hal as hal;
|
use rp2040_hal as hal;
|
||||||
@@ -13,27 +14,15 @@ use defmt as _;
|
|||||||
use panic_probe as _;
|
use panic_probe as _;
|
||||||
|
|
||||||
mod sprite;
|
mod sprite;
|
||||||
|
mod ssh1106;
|
||||||
|
|
||||||
use crate::sprite::*;
|
use crate::sprite::*;
|
||||||
|
use crate::ssh1106::*;
|
||||||
|
|
||||||
#[link_section = ".boot_loader"]
|
#[link_section = ".boot_loader"]
|
||||||
#[used]
|
#[used]
|
||||||
pub static BOOT_LOADER: [u8; 256] = rp2040_boot2::BOOT_LOADER_W25Q080;
|
pub static BOOT_LOADER: [u8; 256] = rp2040_boot2::BOOT_LOADER_W25Q080;
|
||||||
|
|
||||||
#[allow(unused)]
|
|
||||||
#[repr(u8)]
|
|
||||||
#[derive(PartialEq, Copy, Clone)]
|
|
||||||
enum SH1106Cmd {
|
|
||||||
SetColumnLo = 0,
|
|
||||||
SetColumnHi = 0x10,
|
|
||||||
DisplayInvert = 0xA6,
|
|
||||||
DisplayOnOff = 0xAE,
|
|
||||||
ColDirectionNorm = 0xC0,
|
|
||||||
ColDirectionRev = 0xC8,
|
|
||||||
SetPage = 0xB0,
|
|
||||||
}
|
|
||||||
|
|
||||||
const SH1106_I2C_ADDR: u8 = 0x78u8 >> 1;
|
|
||||||
|
|
||||||
#[rp2040_hal::entry]
|
#[rp2040_hal::entry]
|
||||||
fn main() -> ! {
|
fn main() -> ! {
|
||||||
// Grab our singleton objects
|
// Grab our singleton objects
|
||||||
@@ -69,29 +58,22 @@ fn main() -> ! {
|
|||||||
&mut pac.RESETS,
|
&mut pac.RESETS,
|
||||||
);
|
);
|
||||||
|
|
||||||
// Create the I²C drive, using the two pre-configured pins.
|
// Create the I²C drive
|
||||||
let mut i2c = hal::I2C::i2c0(
|
let mut i2c = hal::I2C::i2c0(
|
||||||
pac.I2C0,
|
pac.I2C0,
|
||||||
pins.gpio8.reconfigure(),
|
pins.gpio8.reconfigure(),
|
||||||
pins.gpio9.reconfigure(), // Try `not_an_scl_pin` here
|
pins.gpio9.reconfigure(),
|
||||||
400.kHz(),
|
400.kHz(),
|
||||||
&mut pac.RESETS,
|
&mut pac.RESETS,
|
||||||
&clocks.system_clock,
|
&clocks.system_clock,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// show life on the board
|
||||||
let mut led_pin = pins.gpio25.into_push_pull_output();
|
let mut led_pin = pins.gpio25.into_push_pull_output();
|
||||||
led_pin.set_high().unwrap();
|
led_pin.set_high().unwrap();
|
||||||
|
|
||||||
send_command(&mut i2c, &SH1106Cmd::DisplayOnOff, 0x00);
|
let ssh1106_dev = SSH1106Dev::new(&mut delay, &mut i2c);
|
||||||
delay.delay_ms(100);
|
println!("SSH1106 initialized.");
|
||||||
send_command(&mut i2c, &SH1106Cmd::DisplayInvert, 0);
|
|
||||||
send_command(&mut i2c, &SH1106Cmd::ColDirectionNorm, 0);
|
|
||||||
|
|
||||||
set_col(&mut i2c, 2);
|
|
||||||
set_page(&mut i2c, 0);
|
|
||||||
clr_screen(&mut i2c);
|
|
||||||
delay.delay_ms(500);
|
|
||||||
send_command(&mut i2c, &SH1106Cmd::DisplayOnOff, 0x01);
|
|
||||||
|
|
||||||
let mut gfx_buf = [0u8; 8192];
|
let mut gfx_buf = [0u8; 8192];
|
||||||
|
|
||||||
@@ -134,12 +116,13 @@ fn main() -> ! {
|
|||||||
draw_bresenham_line(0, 63, 128, 63, &mut gfx_buf);
|
draw_bresenham_line(0, 63, 128, 63, &mut gfx_buf);
|
||||||
draw_bresenham_line(0, 0, 0, 63, &mut gfx_buf);
|
draw_bresenham_line(0, 0, 0, 63, &mut gfx_buf);
|
||||||
draw_bresenham_line(127, 0, 127, 63, &mut gfx_buf);
|
draw_bresenham_line(127, 0, 127, 63, &mut gfx_buf);
|
||||||
blit_framebuffer(&mut i2c, &gfx_buf);
|
ssh1106_dev.blit_framebuffer(&mut i2c, &gfx_buf);
|
||||||
|
|
||||||
delay.delay_ms(1);
|
delay.delay_ms(1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// should probably go into some kind of graphics module
|
||||||
fn draw_bresenham_line(x0: u8, y0: u8, x1: u8, y1: u8, gfx_buf: &mut [u8; 8192]) {
|
fn draw_bresenham_line(x0: u8, y0: u8, x1: u8, y1: u8, gfx_buf: &mut [u8; 8192]) {
|
||||||
let x0 = (x0 as i32).clamp(0, 127);
|
let x0 = (x0 as i32).clamp(0, 127);
|
||||||
let x1 = (x1 as i32).clamp(0, 127);
|
let x1 = (x1 as i32).clamp(0, 127);
|
||||||
@@ -193,85 +176,3 @@ fn draw_bresenham_line(x0: u8, y0: u8, x1: u8, y1: u8, gfx_buf: &mut [u8; 8192])
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn blit_framebuffer(i2c: &mut dyn I2c<Error = Error>, graphic: &[u8; 8192]) {
|
|
||||||
// for each row of 8x8 pixels
|
|
||||||
for r in 0..8 {
|
|
||||||
set_page(i2c, r);
|
|
||||||
set_col(i2c, 2);
|
|
||||||
// for each cel of 8x8 pixels
|
|
||||||
for c in 0..16 {
|
|
||||||
let mut cel = [0u8; 8];
|
|
||||||
|
|
||||||
// so our x,y here would be r * 128 + c * 8
|
|
||||||
|
|
||||||
// start assembling the 8x8 cel
|
|
||||||
for x in 0..8 {
|
|
||||||
let mut col_byte = 0u8;
|
|
||||||
|
|
||||||
// for each row of the cel
|
|
||||||
for y in 0..8 {
|
|
||||||
let pix_x = (c as usize) * 8 + x;
|
|
||||||
let pix_y = (r as usize) * 8 + y;
|
|
||||||
col_byte |= graphic[pix_y * 128 + pix_x] << y;
|
|
||||||
}
|
|
||||||
|
|
||||||
cel[x] = col_byte;
|
|
||||||
}
|
|
||||||
|
|
||||||
write_cel_pixels(i2c, &cel);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn set_col(i2c: &mut dyn I2c<Error = Error>, col: u8) {
|
|
||||||
let col_lo = col & 0b0000_1111;
|
|
||||||
let col_hi = (col & 0b1111_0000) >> 4;
|
|
||||||
|
|
||||||
send_command(i2c, &SH1106Cmd::SetColumnLo, col_lo);
|
|
||||||
send_command(i2c, &SH1106Cmd::SetColumnHi, col_hi);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn set_page(i2c: &mut dyn I2c<Error = Error>, page: u8) {
|
|
||||||
send_command(i2c, &SH1106Cmd::SetPage, page & 0b0000_0111);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn clr_screen(i2c: &mut dyn I2c<Error = Error>) {
|
|
||||||
let mut writebuf = [0u8; 132 * 8 + 1];
|
|
||||||
|
|
||||||
writebuf[0] = 0b0100_0000;
|
|
||||||
|
|
||||||
for i in 0..8 {
|
|
||||||
match i2c.write(SH1106_I2C_ADDR, &writebuf) {
|
|
||||||
_ => (),
|
|
||||||
};
|
|
||||||
|
|
||||||
set_page(i2c, i);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn write_cel_pixels(i2c: &mut dyn I2c<Error = Error>, data: &[u8]) {
|
|
||||||
let mut writebuf = [0b0100_0000u8; 9];
|
|
||||||
|
|
||||||
writebuf[0] = 0b0100_0000;
|
|
||||||
writebuf[1..].copy_from_slice(&data[0..8]);
|
|
||||||
|
|
||||||
match i2c.write(SH1106_I2C_ADDR, &writebuf) {
|
|
||||||
_ => (),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
fn send_command(i2c: &mut dyn I2c<Error = Error>, command: &SH1106Cmd, data: u8) {
|
|
||||||
let mut writebuf: [u8; 2] = [0; 2];
|
|
||||||
|
|
||||||
writebuf[0] = 0b1000_0000;
|
|
||||||
writebuf[1] = (command.clone() as u8) | data;
|
|
||||||
|
|
||||||
match i2c.write(SH1106_I2C_ADDR, &writebuf) {
|
|
||||||
_ => (),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
// static GRAPHIC:[u8;8192] = *include_bytes!("../image.bin");
|
|
||||||
// static TILES_COMPRESSED:[u8;12800] = *include_bytes!("../kenney_comp.bin");
|
|
||||||
// static TILES:[u8;102400] = *include_bytes!("../kenney_mono.bin");
|
|
||||||
|
|||||||
121
src/ssh1106.rs
Normal file
121
src/ssh1106.rs
Normal file
@@ -0,0 +1,121 @@
|
|||||||
|
use cortex_m::delay::Delay;
|
||||||
|
use embedded_hal::i2c::I2c;
|
||||||
|
use rp2040_hal::i2c::Error;
|
||||||
|
|
||||||
|
#[allow(unused)]
|
||||||
|
#[repr(u8)]
|
||||||
|
#[derive(PartialEq, Copy, Clone)]
|
||||||
|
pub enum SH1106Cmd {
|
||||||
|
SetColumnLo = 0,
|
||||||
|
SetColumnHi = 0x10,
|
||||||
|
DisplayInvert = 0xA6,
|
||||||
|
DisplayOnOff = 0xAE,
|
||||||
|
ColDirectionNorm = 0xC0,
|
||||||
|
ColDirectionRev = 0xC8,
|
||||||
|
SetPage = 0xB0,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub const SH1106_I2C_ADDR: u8 = 0x78u8 >> 1;
|
||||||
|
|
||||||
|
pub struct SSH1106Dev {
|
||||||
|
ready: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SSH1106Dev {
|
||||||
|
pub fn new(delay: &mut Delay, mut i2c: &mut dyn I2c<Error = Error>) -> Self {
|
||||||
|
send_command(&mut i2c, &SH1106Cmd::DisplayOnOff, 0x00);
|
||||||
|
delay.delay_ms(100);
|
||||||
|
send_command(&mut i2c, &SH1106Cmd::DisplayInvert, 0);
|
||||||
|
send_command(&mut i2c, &SH1106Cmd::ColDirectionNorm, 0);
|
||||||
|
|
||||||
|
set_col(&mut i2c, 2);
|
||||||
|
set_page(&mut i2c, 0);
|
||||||
|
clr_screen(&mut i2c);
|
||||||
|
delay.delay_ms(500);
|
||||||
|
send_command(&mut i2c, &SH1106Cmd::DisplayOnOff, 0x01);
|
||||||
|
|
||||||
|
SSH1106Dev { ready: true }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn blit_framebuffer(&self, i2c: &mut dyn I2c<Error = Error>, graphic: &[u8; 8192]) {
|
||||||
|
if !self.ready {
|
||||||
|
panic!("Attempted to use SSH1106 before initialized.");
|
||||||
|
}
|
||||||
|
|
||||||
|
// for each row of 8x8 pixels
|
||||||
|
for r in 0..8 {
|
||||||
|
set_page(i2c, r);
|
||||||
|
set_col(i2c, 2);
|
||||||
|
// for each cel of 8x8 pixels
|
||||||
|
for c in 0..16 {
|
||||||
|
let mut cel = [0u8; 8];
|
||||||
|
|
||||||
|
// so our x,y here would be r * 128 + c * 8
|
||||||
|
|
||||||
|
// start assembling the 8x8 cel
|
||||||
|
for x in 0..8 {
|
||||||
|
let mut col_byte = 0u8;
|
||||||
|
|
||||||
|
// for each row of the cel
|
||||||
|
for y in 0..8 {
|
||||||
|
let pix_x = (c as usize) * 8 + x;
|
||||||
|
let pix_y = (r as usize) * 8 + y;
|
||||||
|
col_byte |= graphic[pix_y * 128 + pix_x] << y;
|
||||||
|
}
|
||||||
|
|
||||||
|
cel[x] = col_byte;
|
||||||
|
}
|
||||||
|
|
||||||
|
write_cel_pixels(i2c, &cel);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn send_command(i2c: &mut dyn I2c<Error = Error>, command: &SH1106Cmd, data: u8) {
|
||||||
|
let mut writebuf: [u8; 2] = [0; 2];
|
||||||
|
|
||||||
|
writebuf[0] = 0b1000_0000;
|
||||||
|
writebuf[1] = (command.clone() as u8) | data;
|
||||||
|
|
||||||
|
match i2c.write(SH1106_I2C_ADDR, &writebuf) {
|
||||||
|
_ => (),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_col(i2c: &mut dyn I2c<Error = Error>, col: u8) {
|
||||||
|
let col_lo = col & 0b0000_1111;
|
||||||
|
let col_hi = (col & 0b1111_0000) >> 4;
|
||||||
|
|
||||||
|
send_command(i2c, &SH1106Cmd::SetColumnLo, col_lo);
|
||||||
|
send_command(i2c, &SH1106Cmd::SetColumnHi, col_hi);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_page(i2c: &mut dyn I2c<Error = Error>, page: u8) {
|
||||||
|
send_command(i2c, &SH1106Cmd::SetPage, page & 0b0000_0111);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn clr_screen(i2c: &mut dyn I2c<Error = Error>) {
|
||||||
|
let mut writebuf = [0u8; 132 * 8 + 1];
|
||||||
|
|
||||||
|
writebuf[0] = 0b0100_0000;
|
||||||
|
|
||||||
|
for i in 0..8 {
|
||||||
|
match i2c.write(SH1106_I2C_ADDR, &writebuf) {
|
||||||
|
_ => (),
|
||||||
|
};
|
||||||
|
|
||||||
|
set_page(i2c, i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn write_cel_pixels(i2c: &mut dyn I2c<Error = Error>, data: &[u8]) {
|
||||||
|
let mut writebuf = [0b0100_0000u8; 9];
|
||||||
|
|
||||||
|
writebuf[0] = 0b0100_0000;
|
||||||
|
writebuf[1..].copy_from_slice(&data[0..8]);
|
||||||
|
|
||||||
|
match i2c.write(SH1106_I2C_ADDR, &writebuf) {
|
||||||
|
_ => (),
|
||||||
|
};
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user