Making i2c more generic over any i2c pins

This commit is contained in:
JP Stringham
2025-12-26 21:21:01 -05:00
parent 941d4386a1
commit 28c012c6cb
2 changed files with 62 additions and 75 deletions

7
.vscode/settings.json vendored Normal file
View File

@@ -0,0 +1,7 @@
{
"editor.formatOnSave": true,
"editor.defaultFormatter": "rust-lang.rust-analyzer",
"workbench.editor.customLabels.patterns": {
"**/mod.rs": "mod | ${dirname}"
},
}

View File

@@ -1,23 +1,13 @@
// Intended to be used with a 1.3" OLED // Intended to be used with a 1.3" OLED
// resolution is 128*64 // resolution is 128*64
//
#![no_std] #![no_std]
#![no_main] #![no_main]
use embedded_hal::{ use embedded_hal::{digital::OutputPin, i2c::I2c};
digital::OutputPin, use hal::{fugit::RateExtU32, pac};
i2c::I2c
};
use hal::gpio::bank0::{Gpio8, Gpio9};
use rp2040_hal::gpio::PullUp;
use rp2040_hal as hal; use rp2040_hal as hal;
use hal::{ use rp2040_hal::i2c::Error;
pac as pac,
gpio::{FunctionI2C, Pin},
fugit::RateExtU32,
};
use defmt as _; use defmt as _;
use panic_probe as _; use panic_probe as _;
@@ -42,7 +32,7 @@ enum SH1106Cmd {
SetPage = 0xB0, SetPage = 0xB0,
} }
const SH1106_I2C_ADDR:u8 = 0x78u8 >> 1; const SH1106_I2C_ADDR: u8 = 0x78u8 >> 1;
#[rp2040_hal::entry] #[rp2040_hal::entry]
fn main() -> ! { fn main() -> ! {
@@ -76,10 +66,9 @@ fn main() -> ! {
pac.IO_BANK0, pac.IO_BANK0,
pac.PADS_BANK0, pac.PADS_BANK0,
sio.gpio_bank0, sio.gpio_bank0,
&mut pac.RESETS &mut pac.RESETS,
); );
// Create the I²C drive, using the two pre-configured pins. // Create the I²C drive, using the two pre-configured pins.
let mut i2c = hal::I2C::i2c0( let mut i2c = hal::I2C::i2c0(
pac.I2C0, pac.I2C0,
@@ -116,18 +105,30 @@ fn main() -> ! {
sprites[0].visible = true; sprites[0].visible = true;
loop { loop {
gfx_buf.iter_mut().for_each(|x| *x = 0); gfx_buf.iter_mut().for_each(|x| *x = 0);
sprites.iter_mut().for_each(|s| { sprites.iter_mut().for_each(|s| {
if s.visible { if s.visible {
sprite_atlas.draw_sprite(s.sprite_index, s.x as u8, s.y as u8, s.flip, &mut gfx_buf); sprite_atlas.draw_sprite(
s.sprite_index,
s.x as u8,
s.y as u8,
s.flip,
&mut gfx_buf,
);
} }
}); });
sprite_atlas.draw_string(10, 10, "Hello World!", &mut gfx_buf); sprite_atlas.draw_string(10, 10, "Hello World!", &mut gfx_buf);
sprite_atlas.draw_textfield(10, 20, 110, 10, "I think this font has a *lot* of 'character'! ;)", &mut gfx_buf); sprite_atlas.draw_textfield(
10,
20,
110,
10,
"I think this font has a *lot* of 'character'! ;)",
&mut gfx_buf,
);
draw_bresenham_line(0, 0, 127, 0, &mut gfx_buf); draw_bresenham_line(0, 0, 127, 0, &mut gfx_buf);
draw_bresenham_line(0, 63, 128, 63, &mut gfx_buf); draw_bresenham_line(0, 63, 128, 63, &mut gfx_buf);
@@ -139,12 +140,8 @@ fn main() -> ! {
} }
} }
fn draw_bresenham_line( fn draw_bresenham_line(x0: u8, y0: u8, x1: u8, y1: u8, gfx_buf: &mut [u8; 8192]) {
x0: u8, y0: u8, let x0 = (x0 as i32).clamp(0, 127);
x1: u8, y1: u8,
gfx_buf: &mut [u8; 8192],
) {
let x0 = (x0 as i32).clamp(0,127);
let x1 = (x1 as i32).clamp(0, 127); let x1 = (x1 as i32).clamp(0, 127);
let y0 = (y0 as i32).clamp(0, 63); let y0 = (y0 as i32).clamp(0, 63);
let y1 = (y1 as i32).clamp(0, 63); let y1 = (y1 as i32).clamp(0, 63);
@@ -172,7 +169,7 @@ fn draw_bresenham_line(
if dx > dy { if dx > dy {
for _ in 0..dx { for _ in 0..dx {
gfx_buf[(y*128+x) as usize] = 1; gfx_buf[(y * 128 + x) as usize] = 1;
x += step_x; x += step_x;
err += 2 * dy; err += 2 * dy;
@@ -184,7 +181,7 @@ fn draw_bresenham_line(
} }
} else { } else {
for _ in 0..dy { for _ in 0..dy {
gfx_buf[(y*128+x) as usize] = 1; gfx_buf[(y * 128 + x) as usize] = 1;
y += step_y; y += step_y;
err += 2 * dx; err += 2 * dx;
@@ -195,48 +192,39 @@ fn draw_bresenham_line(
} }
} }
} }
} }
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
fn blit_framebuffer( // start assembling the 8x8 cel
i2c: &mut hal::I2C<pac::I2C0, (Pin<Gpio8, FunctionI2C, PullUp>, Pin<Gpio9, FunctionI2C, PullUp>)>, for x in 0..8 {
graphic: &[u8; 8192]) { let mut col_byte = 0u8;
// for each row of 8x8 pixels // for each row of the cel
for r in 0..8 { for y in 0..8 {
set_page(i2c, r); let pix_x = (c as usize) * 8 + x;
set_col(i2c, 2); let pix_y = (r as usize) * 8 + y;
// for each cel of 8x8 pixels col_byte |= graphic[pix_y * 128 + pix_x] << y;
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); cel[x] = col_byte;
} }
write_cel_pixels(i2c, &cel);
} }
}
} }
fn set_col(i2c: &mut hal::I2C<pac::I2C0, (Pin<Gpio8, FunctionI2C, PullUp>, Pin<Gpio9, FunctionI2C, PullUp>)>, fn set_col(i2c: &mut dyn I2c<Error = Error>, col: u8) {
col: u8) {
let col_lo = col & 0b0000_1111; let col_lo = col & 0b0000_1111;
let col_hi = (col & 0b1111_0000) >> 4; let col_hi = (col & 0b1111_0000) >> 4;
@@ -244,51 +232,43 @@ fn set_col(i2c: &mut hal::I2C<pac::I2C0, (Pin<Gpio8, FunctionI2C, PullUp>, Pin<G
send_command(i2c, &SH1106Cmd::SetColumnHi, col_hi); send_command(i2c, &SH1106Cmd::SetColumnHi, col_hi);
} }
fn set_page(i2c: &mut hal::I2C<pac::I2C0, (Pin<Gpio8, FunctionI2C, PullUp>, Pin<Gpio9, FunctionI2C, PullUp>)>, fn set_page(i2c: &mut dyn I2c<Error = Error>, page: u8) {
page: u8) {
send_command(i2c, &SH1106Cmd::SetPage, page & 0b0000_0111); send_command(i2c, &SH1106Cmd::SetPage, page & 0b0000_0111);
} }
fn clr_screen(i2c: &mut hal::I2C<pac::I2C0, (Pin<Gpio8, FunctionI2C, PullUp>, Pin<Gpio9, FunctionI2C, PullUp>)>) { fn clr_screen(i2c: &mut dyn I2c<Error = Error>) {
let mut writebuf = [0u8; 132*8+1]; let mut writebuf = [0u8; 132 * 8 + 1];
writebuf[0] = 0b0100_0000; writebuf[0] = 0b0100_0000;
for i in 0..8 { for i in 0..8 {
match i2c.write(SH1106_I2C_ADDR, &writebuf) { match i2c.write(SH1106_I2C_ADDR, &writebuf) {
_ => () _ => (),
}; };
set_page(i2c, i); set_page(i2c, i);
} }
} }
fn write_cel_pixels(i2c: &mut dyn I2c<Error = Error>, data: &[u8]) {
fn write_cel_pixels(
i2c: &mut hal::I2C<pac::I2C0, (Pin<Gpio8, FunctionI2C, PullUp>, Pin<Gpio9, FunctionI2C, PullUp>)>,
data: &[u8]) {
let mut writebuf = [0b0100_0000u8; 9]; let mut writebuf = [0b0100_0000u8; 9];
writebuf[0] = 0b0100_0000; writebuf[0] = 0b0100_0000;
writebuf[1..].copy_from_slice(&data[0..8]); writebuf[1..].copy_from_slice(&data[0..8]);
match i2c.write(SH1106_I2C_ADDR, &writebuf) { match i2c.write(SH1106_I2C_ADDR, &writebuf) {
_ => () _ => (),
}; };
} }
fn send_command(i2c: &mut dyn I2c<Error = Error>, command: &SH1106Cmd, data: u8) {
fn send_command(i2c: &mut hal::I2C<pac::I2C0, (Pin<Gpio8, FunctionI2C, PullUp>, Pin<Gpio9, FunctionI2C, PullUp>)>,
command: &SH1106Cmd,
data:u8) {
let mut writebuf: [u8; 2] = [0; 2]; let mut writebuf: [u8; 2] = [0; 2];
writebuf[0] = 0b1000_0000; writebuf[0] = 0b1000_0000;
writebuf[1] = (command.clone() as u8) | data; writebuf[1] = (command.clone() as u8) | data;
match i2c.write(SH1106_I2C_ADDR, &writebuf) { match i2c.write(SH1106_I2C_ADDR, &writebuf) {
_ => () _ => (),
}; };
} }