refactor: extract hal/ and driver/ modules
- Created hal/esp32s3.rs with MCU primitives (SPI, I2C, USB, UART, timer, MAC) - Created hal/nrf52840.rs with shared nRF boilerplate (interrupts, bus wrapping, VBUS, MAC) - Moved SH1106 OLED driver from display/ to driver/ (clear separation from app code) - Board esp32s3.rs now delegates MCU init to hal, keeps only peripheral wiring (RadioParts) - nRF board files use hal for shared StaticCell patterns, mac_address, interrupt bindings - Board files still own pin assignments and peripheral construction
This commit is contained in:
1 parent
8bdaa72bc3
commit
fb334118fd
13 files changed
+300
-252
No files matched your search
@@ -0,0 +1,2 @@
|
||||
#[allow(dead_code)]
|
||||
pub mod sh1106;
|
||||
@@ -0,0 +1,132 @@
|
||||
//! Minimal async SH1106 I2C OLED driver (128x64, page addressing).
|
||||
//!
|
||||
//! The SH1106 is very similar to the SSD1306 but only supports page-mode
|
||||
//! addressing (no horizontal/vertical auto-advance). This driver sends
|
||||
//! framebuffer data page-by-page with explicit page/column commands.
|
||||
|
||||
use embedded_graphics::draw_target::DrawTarget;
|
||||
use embedded_graphics::geometry::{OriginDimensions, Size};
|
||||
use embedded_graphics::pixelcolor::BinaryColor;
|
||||
use embedded_graphics::Pixel;
|
||||
use embedded_hal_async::i2c::I2c;
|
||||
|
||||
const WIDTH: usize = 128;
|
||||
const HEIGHT: usize = 64;
|
||||
const PAGES: usize = HEIGHT / 8;
|
||||
const BUF_SIZE: usize = WIDTH * PAGES; // 1024
|
||||
|
||||
/// SH1106 has a 132-column RAM; visible 128 columns are centered with a
|
||||
/// 2-column offset on each side.
|
||||
const COL_OFFSET: u8 = 2;
|
||||
|
||||
pub struct Sh1106<I> {
|
||||
i2c: I,
|
||||
addr: u8,
|
||||
buffer: [u8; BUF_SIZE],
|
||||
}
|
||||
|
||||
impl<I: I2c> Sh1106<I> {
|
||||
pub fn new(i2c: I, addr: u8) -> Self {
|
||||
Self {
|
||||
i2c,
|
||||
addr,
|
||||
buffer: [0u8; BUF_SIZE],
|
||||
}
|
||||
}
|
||||
|
||||
/// Send a sequence of commands (each prefixed with the I2C command byte 0x00).
|
||||
async fn cmd(&mut self, commands: &[u8]) -> Result<(), I::Error> {
|
||||
for &c in commands {
|
||||
self.i2c.write(self.addr, &[0x00, c]).await?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Initialize the display with sensible defaults.
|
||||
pub async fn init(&mut self) -> Result<(), I::Error> {
|
||||
self.cmd(&[
|
||||
0xAE, // display off
|
||||
0xD5, 0x80, // clock div: default
|
||||
0xA8, 0x3F, // multiplex ratio: 64
|
||||
0xD3, 0x00, // display offset: 0
|
||||
0x40, // start line: 0
|
||||
0x8D, 0x14, // charge pump: enable
|
||||
0xA1, // segment remap: column 127 mapped to SEG0
|
||||
0xC8, // COM scan direction: remapped
|
||||
0xDA, 0x12, // COM pin config: alternative, no L/R remap
|
||||
0x81, 0xCF, // contrast: 207
|
||||
0xD9, 0xF1, // pre-charge: phase1=1, phase2=15
|
||||
0xDB, 0x40, // VCOMH deselect: ~0.77×Vcc
|
||||
0xA4, // entire display ON (follow RAM)
|
||||
0xA6, // normal display (not inverted)
|
||||
0xAF, // display on
|
||||
])
|
||||
.await
|
||||
}
|
||||
|
||||
/// Flush the entire framebuffer to the display, page by page.
|
||||
pub async fn flush(&mut self) -> Result<(), I::Error> {
|
||||
// Each I2C data write is prefixed with 0x40 (data continuation byte).
|
||||
let mut page_buf = [0u8; 1 + WIDTH]; // 0x40 + 128 data bytes
|
||||
page_buf[0] = 0x40;
|
||||
|
||||
for page in 0..PAGES {
|
||||
// Set page address and column start (with SH1106 2-column offset).
|
||||
self.cmd(&[
|
||||
0xB0 | page as u8, // page address
|
||||
COL_OFFSET & 0x0F, // column low nibble
|
||||
0x10 | (COL_OFFSET >> 4), // column high nibble
|
||||
])
|
||||
.await?;
|
||||
|
||||
let start = page * WIDTH;
|
||||
page_buf[1..].copy_from_slice(&self.buffer[start..start + WIDTH]);
|
||||
self.i2c.write(self.addr, &page_buf).await?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Set display brightness (contrast).
|
||||
pub async fn set_brightness(&mut self, value: u8) -> Result<(), I::Error> {
|
||||
self.cmd(&[0x81, value]).await
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
impl<I: I2c> DrawTarget for Sh1106<I> {
|
||||
type Color = BinaryColor;
|
||||
type Error = core::convert::Infallible;
|
||||
|
||||
fn draw_iter<T>(&mut self, pixels: T) -> Result<(), Self::Error>
|
||||
where
|
||||
T: IntoIterator<Item = Pixel<Self::Color>>,
|
||||
{
|
||||
for Pixel(pos, color) in pixels {
|
||||
let x = pos.x;
|
||||
let y = pos.y;
|
||||
if x >= 0 && x < WIDTH as i32 && y >= 0 && y < HEIGHT as i32 {
|
||||
let x = x as usize;
|
||||
let y = y as usize;
|
||||
let idx = (y / 8) * WIDTH + x;
|
||||
let bit = (y % 8) as u8;
|
||||
if color.is_on() {
|
||||
self.buffer[idx] |= 1 << bit;
|
||||
} else {
|
||||
self.buffer[idx] &= !(1 << bit);
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn clear(&mut self, color: Self::Color) -> Result<(), Self::Error> {
|
||||
self.buffer.fill(if color.is_on() { 0xFF } else { 0x00 });
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl<I: I2c> OriginDimensions for Sh1106<I> {
|
||||
fn size(&self) -> Size {
|
||||
Size::new(WIDTH as u32, HEIGHT as u32)
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user