Initial commit

This commit is contained in:
JP Stringham
2025-12-26 17:38:22 -05:00
commit f359f3b718
13 changed files with 1109 additions and 0 deletions

42
.cargo/config Normal file
View File

@@ -0,0 +1,42 @@
#
# Cargo Configuration for the https://github.com/rp-rs/rp-hal.git repository.
#
# Copyright (c) The RP-RS Developers, 2021
#
# You might want to make a similar file in your own repository if you are
# writing programs for Raspberry Silicon microcontrollers.
#
# This file is MIT or Apache-2.0 as per the repository README.md file
#
[build]
# Set the default target to match the Cortex-M0+ in the RP2040
target = "thumbv6m-none-eabi"
# Target specific options
[target.thumbv6m-none-eabi]
# Pass some extra options to rustc, some of which get passed on to the linker.
#
# * linker argument --nmagic turns off page alignment of sections (which saves
# flash space)
# * linker argument -Tlink.x tells the linker to use link.x as the linker
# script. This is usually provided by the cortex-m-rt crate, and by default
# the version in that crate will include a file called `memory.x` which
# describes the particular memory layout for your specific chip.
# * inline-threshold=5 makes the compiler more aggressive and inlining functions
# * no-vectorize-loops turns off the loop vectorizer (seeing as the M0+ doesn't
# have SIMD)
rustflags = [
"-C", "link-arg=--nmagic",
"-C", "link-arg=-Tlink.x",
"-C", "inline-threshold=5",
"-C", "no-vectorize-loops",
]
# This runner will make a UF2 file and then copy it to a mounted RP2040 in USB
# Bootloader mode:
runner = "elf2uf2-rs -d"
# This runner will find a supported SWD debug probe and flash your RP2040 over
# SWD:
# runner = "probe-run --chip RP2040"

3
.gitignore vendored Normal file
View File

@@ -0,0 +1,3 @@
/target
.DS_Store

410
Cargo.lock generated Normal file
View File

@@ -0,0 +1,410 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 3
[[package]]
name = "arrayvec"
version = "0.7.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "96d30a06541fbafbc7f82ed10c06164cfbd2c401138f6addd8404629c4b16711"
[[package]]
name = "bare-metal"
version = "0.2.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5deb64efa5bd81e31fcd1938615a6d98c82eafcbcd787162b6f63b91d6bac5b3"
dependencies = [
"rustc_version",
]
[[package]]
name = "bitfield"
version = "0.13.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "46afbd2983a5d5a7bd740ccb198caf5b82f45c40c09c0eed36052d91cb92e719"
[[package]]
name = "cortex-m"
version = "0.7.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8ec610d8f49840a5b376c69663b6369e71f4b34484b9b2eb29fb918d92516cb9"
dependencies = [
"bare-metal",
"bitfield",
"embedded-hal",
"volatile-register",
]
[[package]]
name = "cortex-m-rt"
version = "0.7.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ee84e813d593101b1723e13ec38b6ab6abbdbaaa4546553f5395ed274079ddb1"
dependencies = [
"cortex-m-rt-macros",
]
[[package]]
name = "cortex-m-rt-macros"
version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f0f6f3e36f203cfedbc78b357fb28730aa2c6dc1ab060ee5c2405e843988d3c7"
dependencies = [
"proc-macro2",
"quote",
"syn 1.0.109",
]
[[package]]
name = "crc-any"
version = "2.4.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c01a5e1f881f6fb6099a7bdf949e946719fd4f1fefa56264890574febf0eb6d0"
dependencies = [
"debug-helper",
]
[[package]]
name = "critical-section"
version = "1.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7059fff8937831a9ae6f0fe4d658ffabf58f2ca96aa9dec1c889f936f705f216"
[[package]]
name = "debug-helper"
version = "0.3.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f578e8e2c440e7297e008bb5486a3a8a194775224bbc23729b0dbdfaeebf162e"
[[package]]
name = "either"
version = "1.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07"
[[package]]
name = "embedded-dma"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "994f7e5b5cb23521c22304927195f236813053eb9c065dd2226a32ba64695446"
dependencies = [
"stable_deref_trait",
]
[[package]]
name = "embedded-hal"
version = "0.2.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "35949884794ad573cf46071e41c9b60efb0cb311e3ca01f7af807af1debc66ff"
dependencies = [
"nb 0.1.3",
"void",
]
[[package]]
name = "frunk"
version = "0.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "11a351b59e12f97b4176ee78497dff72e4276fb1ceb13e19056aca7fa0206287"
dependencies = [
"frunk_core",
"frunk_derives",
]
[[package]]
name = "frunk_core"
version = "0.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "af2469fab0bd07e64ccf0ad57a1438f63160c69b2e57f04a439653d68eb558d6"
[[package]]
name = "frunk_derives"
version = "0.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b0fa992f1656e1707946bbba340ad244f0814009ef8c0118eb7b658395f19a2e"
dependencies = [
"frunk_proc_macro_helpers",
"quote",
"syn 2.0.48",
]
[[package]]
name = "frunk_proc_macro_helpers"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "35b54add839292b743aeda6ebedbd8b11e93404f902c56223e51b9ec18a13d2c"
dependencies = [
"frunk_core",
"proc-macro2",
"quote",
"syn 2.0.48",
]
[[package]]
name = "fugit"
version = "0.3.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "17186ad64927d5ac8f02c1e77ccefa08ccd9eaa314d5a4772278aa204a22f7e7"
dependencies = [
"gcd",
]
[[package]]
name = "gcd"
version = "2.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1d758ba1b47b00caf47f24925c0074ecb20d6dfcffe7f6d53395c0465674841a"
[[package]]
name = "itertools"
version = "0.10.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473"
dependencies = [
"either",
]
[[package]]
name = "nb"
version = "0.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "801d31da0513b6ec5214e9bf433a77966320625a37860f910be265be6e18d06f"
dependencies = [
"nb 1.1.0",
]
[[package]]
name = "nb"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8d5439c4ad607c3c23abf66de8c8bf57ba8adcd1f129e699851a6e43935d339d"
[[package]]
name = "num_enum"
version = "0.5.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1f646caf906c20226733ed5b1374287eb97e3c2a5c227ce668c1f2ce20ae57c9"
dependencies = [
"num_enum_derive",
]
[[package]]
name = "num_enum_derive"
version = "0.5.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dcbff9bc912032c62bf65ef1d5aea88983b420f4f839db1e9b0c281a25c9c799"
dependencies = [
"proc-macro2",
"quote",
"syn 1.0.109",
]
[[package]]
name = "panic-halt"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "de96540e0ebde571dc55c73d60ef407c653844e6f9a1e2fdbd40c07b9252d812"
[[package]]
name = "paste"
version = "1.0.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "de3145af08024dea9fa9914f381a17b8fc6034dfb00f3a84013f7ff43f29ed4c"
[[package]]
name = "pio"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "76e09694b50f89f302ed531c1f2a7569f0be5867aee4ab4f8f729bbeec0078e3"
dependencies = [
"arrayvec",
"num_enum",
"paste",
]
[[package]]
name = "proc-macro2"
version = "1.0.75"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "907a61bd0f64c2f29cd1cf1dc34d05176426a3f504a78010f08416ddb7b13708"
dependencies = [
"unicode-ident",
]
[[package]]
name = "quote"
version = "1.0.35"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef"
dependencies = [
"proc-macro2",
]
[[package]]
name = "rand_core"
version = "0.6.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
[[package]]
name = "rp2040-boot2"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7c92f344f63f950ee36cf4080050e4dce850839b9175da38f9d2ffb69b4dbb21"
dependencies = [
"crc-any",
]
[[package]]
name = "rp2040-hal"
version = "0.9.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "08f21deadb5f29f05be9e665049c9c6d6385c6ed3437574c995658a041f71453"
dependencies = [
"cortex-m",
"critical-section",
"embedded-dma",
"embedded-hal",
"frunk",
"fugit",
"itertools",
"nb 1.1.0",
"paste",
"pio",
"rand_core",
"rp2040-hal-macros",
"rp2040-pac",
"usb-device",
"vcell",
"void",
]
[[package]]
name = "rp2040-hal-macros"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "86479063e497efe1ae81995ef9071f54fd1c7427e04d6c5b84cde545ff672a5e"
dependencies = [
"cortex-m-rt",
"proc-macro2",
"quote",
"syn 1.0.109",
]
[[package]]
name = "rp2040-pac"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "12d9d8375815f543f54835d01160d4e47f9e2cae75f17ff8f1ec19ce1da96e4c"
dependencies = [
"cortex-m",
"cortex-m-rt",
"critical-section",
"vcell",
]
[[package]]
name = "rustc_version"
version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a"
dependencies = [
"semver",
]
[[package]]
name = "semver"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403"
dependencies = [
"semver-parser",
]
[[package]]
name = "semver-parser"
version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3"
[[package]]
name = "sh1106-pico-rs"
version = "0.1.0"
dependencies = [
"cortex-m",
"cortex-m-rt",
"critical-section",
"embedded-hal",
"panic-halt",
"rp2040-boot2",
"rp2040-hal",
"rp2040-pac",
"tiny-rng",
]
[[package]]
name = "stable_deref_trait"
version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3"
[[package]]
name = "syn"
version = "1.0.109"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237"
dependencies = [
"proc-macro2",
"quote",
"unicode-ident",
]
[[package]]
name = "syn"
version = "2.0.48"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0f3531638e407dfc0814761abb7c00a5b54992b849452a0646b7f65c9f770f3f"
dependencies = [
"proc-macro2",
"quote",
"unicode-ident",
]
[[package]]
name = "tiny-rng"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d63628286617a0c67ee3c4ef92ccf62044e3813ed7376bb82a4586d2ff7974dd"
[[package]]
name = "unicode-ident"
version = "1.0.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"
[[package]]
name = "usb-device"
version = "0.2.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1f6cc3adc849b5292b4075fc0d5fdcf2f24866e88e336dd27a8943090a520508"
[[package]]
name = "vcell"
version = "0.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "77439c1b53d2303b20d9459b1ade71a83c716e3f9c34f3228c00e6f185d6c002"
[[package]]
name = "void"
version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d"
[[package]]
name = "volatile-register"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "de437e2a6208b014ab52972a27e59b33fa2920d3e00fe05026167a1c509d19cc"
dependencies = [
"vcell",
]

38
Cargo.toml Normal file
View File

@@ -0,0 +1,38 @@
[package]
name = "sh1106-pico-rs"
version = "0.1.0"
edition = "2021"
[dependencies]
cortex-m = "0.7.7"
cortex-m-rt = "0.7.3"
embedded-hal = "0.2.7"
panic-halt = "0.2.0"
rp2040-boot2 = "0.3.0"
rp2040-hal = "0.9.1"
rp2040-pac = "0.5.0"
critical-section = "1.0.0"
tiny-rng = "0.2.0"
[features]
default = ["rt", "critical-section-impl", "rom-func-cache"]
# Minimal startup / runtime for Cortex-M microcontrollers
rt = ["rp2040-pac/rt"]
# This enables a fix for USB errata 5: USB device fails to exit RESET state on busy USB bus.
# Only required for RP2040 B0 and RP2040 B1, but it also works for RP2040 B2 and above
rp2040-e5 = ["rp2040-hal/rp2040-e5"]
# Memoize(cache) ROM function pointers on first use to improve performance
rom-func-cache = ["rp2040-hal/rom-func-cache"]
# Disable automatic mapping of language features (like floating point math) to ROM functions
disable-intrinsics = ["rp2040-hal/disable-intrinsics"]
# This enables ROM functions for f64 math that were not present in the earliest RP2040s
rom-v2-intrinsics = ["rp2040-hal/rom-v2-intrinsics"]
# critical section that is safe for multicore use
critical-section-impl = ["rp2040-hal/critical-section-impl"]

BIN
image.bin Normal file

Binary file not shown.

BIN
kenney_comp.bin Normal file

Binary file not shown.

BIN
kenney_mono.bin Normal file

Binary file not shown.

13
memory.x Normal file
View File

@@ -0,0 +1,13 @@
MEMORY {
BOOT_LOADER : ORIGIN = 0x10000000, LENGTH = 0x100
FLASH : ORIGIN = 0x10000100, LENGTH = 2048K - 0x100
RAM : ORIGIN = 0x20000000, LENGTH = 256K
}
SECTIONS {
/* ### Boot loader */
.boot_loader ORIGIN(BOOT_LOADER) :
{
KEEP(*(.boot_loader));
} > BOOT_LOADER
} INSERT BEFORE .text;

BIN
sprites/sprites_atlas.bin Normal file

Binary file not shown.

1
sprites/sprites_tex.bin Normal file

File diff suppressed because one or more lines are too long

37
src/dpad.rs Normal file
View File

@@ -0,0 +1,37 @@
use embedded_hal::digital::v2::InputPin;
use rp2040_hal::gpio::{Pin, DynPinId, FunctionSioInput, PullUp};
pub struct Dpad {
pinarray: [Pin<DynPinId, FunctionSioInput, PullUp>; 4]
}
impl Dpad {
// expects pins clockwise starting from top
pub fn new(pinarray: [Pin<DynPinId, FunctionSioInput, PullUp>; 4]) -> Self {
Self {
pinarray
}
}
pub fn get_state(&self) -> DpadState {
let up = self.pinarray[0].is_low().unwrap();
let right = self.pinarray[1].is_low().unwrap();
let down = self.pinarray[2].is_low().unwrap();
let left = self.pinarray[3].is_low().unwrap();
DpadState {
up,
down,
left,
right
}
}
}
pub struct DpadState {
pub up: bool,
pub down: bool,
pub left: bool,
pub right: bool
}

361
src/main.rs Normal file
View File

@@ -0,0 +1,361 @@
#![no_std]
#![no_main]
use embedded_hal::digital::v2::{OutputPin, InputPin};
use embedded_hal::blocking::i2c::Write;
use hal::gpio::bank0::{Gpio0, Gpio1};
use hal::gpio::PullDown;
use rp2040_hal as hal;
use hal::{
pac as pac,
gpio::{FunctionI2C, Pin},
fugit::RateExtU32,
};
use panic_halt as _;
mod sprite;
mod dpad;
use crate::dpad::Dpad;
use crate::sprite::*;
#[link_section = ".boot_loader"]
#[used]
pub static BOOT_LOADER: [u8; 256] = rp2040_boot2::BOOT_LOADER_W25Q080;
#[repr(u8)]
#[derive(PartialEq, Copy, Clone)]
enum SH1106Cmd {
SetColumnLo = 0,
SetColumnHi = 0x10,
DisplayInvert = 0xA6,
DisplayOnOff = 0xAE,
ColDirectionNorm = 0xC0,
ColDirectionRev = 0xC8,
SetPage = 0xB0,
}
const SH1106_ADDR:u8 = 0x78u8 >> 1;
#[rp2040_hal::entry]
fn main() -> ! {
// Grab our singleton objects
let mut pac = pac::Peripherals::take().unwrap();
let core = pac::CorePeripherals::take().unwrap();
// Set up the watchdog driver - needed by the clock setup code
let mut watchdog = hal::Watchdog::new(pac.WATCHDOG);
const XTAL_FREQ_HZ: u32 = 12_000_000u32;
// Configure the clocks
let clocks = hal::clocks::init_clocks_and_plls(
XTAL_FREQ_HZ,
pac.XOSC,
pac.CLOCKS,
pac.PLL_SYS,
pac.PLL_USB,
&mut pac.RESETS,
&mut watchdog,
)
.ok()
.unwrap();
let mut delay = cortex_m::delay::Delay::new(core.SYST, 133_000_000u32);
let sio = hal::Sio::new(pac.SIO);
let pins = hal::gpio::Pins::new(
pac.IO_BANK0,
pac.PADS_BANK0,
sio.gpio_bank0,
&mut pac.RESETS
);
// Configure two pins as being I²C, not GPIO
// let sda_pin: Pin<_, FunctionI2C, PullUp> = pins.gpio0.reconfigure();
// let scl_pin: Pin<_, FunctionI2C, PullUp> = pins.gpio1.reconfigure();
// let not_an_scl_pin: Pin<_, FunctionI2C, PullUp> = pins.gpio20.reconfigure();
// Create the I²C drive, using the two pre-configured pins. This will fail
// at compile time if the pins are in the wrong mode, or if this I²C
// peripheral isn't available on these pins!
let mut i2c = hal::I2C::i2c0(
pac.I2C0,
pins.gpio0.into_function(),
pins.gpio1.into_function(), // Try `not_an_scl_pin` here
400.kHz(),
&mut pac.RESETS,
&clocks.system_clock,
);
let mut led_pin = pins.gpio25.into_push_pull_output();
led_pin.set_high().unwrap();
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);
let mut gfx_buf = [0u8; 8192];
// gfx_buf.copy_from_slice(&TILES[0..8192]);
let sprite_atlas = SpriteAtlas::new(include_bytes!("../sprites/sprites_atlas.bin"));
let mut sprites = [Sprite::new(); 30];
// for (i, sprite) in sprites.iter_mut().enumerate() {
// sprite.sprite_index = i+30;
// let i = i as i32;
// sprite.x = (i*10) % 128;
// sprite.y = (i*10) % 64;
// sprite.x_vel = i % 3 - 2;
// sprite.y_vel = i % 4 - 1;
// }
sprites[0].sprite_index = 94;
sprites[0].x = 90;
sprites[0].y = 30;
sprites[0].visible = true;
let dpad = Dpad::new([
pins.gpio2.into_pull_up_input().into_dyn_pin(),
pins.gpio3.into_pull_up_input().into_dyn_pin(),
pins.gpio4.into_pull_up_input().into_dyn_pin(),
pins.gpio5.into_pull_up_input().into_dyn_pin(),
]);
// let dpad_down = pins.gpio2.into_pull_up_input();
// let dpad_left = pins.gpio3.into_pull_up_input();
// let dpad_up = pins.gpio4.into_pull_up_input();
// let dpad_right = pins.gpio5.into_pull_up_input();
loop {
gfx_buf.iter_mut().for_each(|x| *x = 0);
sprites.iter_mut().for_each(|s| {
if s.visible {
sprite_atlas.draw_sprite(s.sprite_index, s.x as u8, s.y as u8, s.flip, &mut gfx_buf);
}
let dpad_state = dpad.get_state();
if dpad_state.up {
s.y -= 1;
}
if dpad_state.down {
s.y += 1;
}
if dpad_state.left {
s.x -= 1;
s.flip = SpriteFlip::None;
}
if dpad_state.right {
s.x += 1;
s.flip = SpriteFlip::Horizontal;
}
});
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);
blit_framebuffer(&mut i2c, &gfx_buf);
delay.delay_ms(1);
}
}
fn draw_bresenham_line(
x0: u8, y0: u8,
x1: u8, y1: u8,
gfx_buf: &mut [u8; 8192],
) {
let mut dx = x1 as i32 - x0 as i32;
let mut dy = y1 as i32 - y0 as i32;
let mut x = x0 as i32;
let mut y = y0 as i32;
let mut err = 0;
let mut step_x = 1;
let mut step_y = 1;
if dx < 0 {
dx = -dx;
step_x = -1;
}
if dy < 0 {
dy = -dy;
step_y = -1;
}
if dx > dy {
for _ in 0..dx {
gfx_buf[(y*128+x) as usize] = 1;
x += step_x;
err += 2 * dy;
if err > dx {
y += step_y;
err -= 2 * dx;
}
}
} else {
for _ in 0..dy {
gfx_buf[(y*128+x) as usize] = 1;
y += step_y;
err += 2 * dx;
if err > dy {
x += step_x;
err -= 2 * dy;
}
}
}
}
fn blit_framebuffer(
i2c: &mut hal::I2C<pac::I2C0, (Pin<Gpio0, FunctionI2C, PullDown>, Pin<Gpio1, FunctionI2C, PullDown>)>,
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);
}
}
}
// as our display is in 8 pages of 128 columns, we need to transform the graphic
// to match the display's orientation
// fn transform_hcompress_graphic(graphic: [u8; 1024]) -> [u8; 1024] {
// let mut cel:[u8;8];
// let mut transformed_graphic = [0u8;1024];
// for row in 0..8 {
// for x in 0..16 {
// cel = [0u8; 8];
// for y in 0..8 {
// let byte = graphic[(row*8+y)*16+x];
// for i in 0..8 {
// cel[i] |= ((byte & (0b1000_0000 >> i)) << i) >> y;
// }
// }
// transformed_graphic[row*128+x*8..row*128+x*8+8].copy_from_slice(&cel[0..8]);
// // transformed_graphic[x*8..x*8+8].copy_from_slice(&cel[0..8]);
// }
// }
// transformed_graphic
// }
fn set_col(i2c: &mut hal::I2C<pac::I2C0, (Pin<Gpio0, FunctionI2C, PullDown>, Pin<Gpio1, FunctionI2C, PullDown>)>,
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 hal::I2C<pac::I2C0, (Pin<Gpio0, FunctionI2C, PullDown>, Pin<Gpio1, FunctionI2C, PullDown>)>,
page: u8) {
send_command(i2c, &SH1106Cmd::SetPage, page & 0b0000_0111);
}
fn clr_screen(i2c: &mut hal::I2C<pac::I2C0, (Pin<Gpio0, FunctionI2C, PullDown>, Pin<Gpio1, FunctionI2C, PullDown>)>) {
let mut writebuf = [0u8; 132*8+1];
writebuf[0] = 0b0100_0000;
for i in 0..8 {
match i2c.write(SH1106_ADDR, &writebuf) {
_ => ()
};
set_page(i2c, i);
}
}
fn write_cel_pixels(
i2c: &mut hal::I2C<pac::I2C0, (Pin<Gpio0, FunctionI2C, PullDown>, Pin<Gpio1, FunctionI2C, PullDown>)>,
data: &[u8]) {
let mut writebuf = [0b0100_0000u8; 9];
writebuf[0] = 0b0100_0000;
writebuf[1..].copy_from_slice(&data[0..8]);
match i2c.write(SH1106_ADDR, &writebuf) {
_ => ()
};
}
fn send_command(i2c: &mut hal::I2C<pac::I2C0, (Pin<Gpio0, FunctionI2C, PullDown>, Pin<Gpio1, FunctionI2C, PullDown>)>,
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_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");

204
src/sprite.rs Normal file
View File

@@ -0,0 +1,204 @@
static SPRITES_COMPRESSED:[u8;16384] = *include_bytes!("../sprites/sprites_tex.bin");
const ATLAS_WIDTH:usize = 256;
#[derive(Copy, Clone)]
pub struct Sprite {
pub x: i32,
pub y: i32,
pub sprite_index: usize,
pub visible: bool,
pub flip: SpriteFlip,
}
impl Sprite {
pub fn new() -> Sprite {
Sprite {
x: 0,
y: 0,
sprite_index: 0,
visible: false,
flip: SpriteFlip::None,
}
}
}
#[repr(u8)]
#[derive(Copy, Clone)]
pub enum SpriteFlip {
None = 0,
Horizontal = 1,
Vertical = 2,
Both = 3,
}
#[derive(Copy, Clone)]
pub struct SpriteAtlasEntry {
pub x: u8,
pub y: u8,
pub width: u8,
pub height: u8,
}
// For the current implementation we assume the first 94 sprites are ASCII font characters.
// So each ASCII character sprite index is: ASCII - 33
pub struct SpriteAtlas {
entries: [SpriteAtlasEntry; 256],
}
impl SpriteAtlas {
pub fn new(from_bin:&[u8]) -> SpriteAtlas {
if from_bin.len() > 256*4 {
panic!("SpriteAtlas::new() - from_bin is too large");
}
let mut entries = [SpriteAtlasEntry {
x: 0,
y: 0,
width: 0,
height: 0,
}; 256];
from_bin.chunks(4).enumerate().for_each(|(i, chunk)| {
entries[i].x = chunk[0];
entries[i].y = chunk[1];
entries[i].width = chunk[2];
entries[i].height = chunk[3];
});
SpriteAtlas {
entries: entries,
}
}
pub fn draw_textfield(&self,
x: u8, y: u8,
width: u8, height: u8,
string: &str,
gfx_buf: &mut [u8; 8192],
) {
let mut cur_x = x;
let mut cur_y = y;
let words = string.split_ascii_whitespace();
for word in words {
let word_width = self.get_str_pixel_width(word);
if cur_x + word_width as u8 > x + width {
cur_x = 0;
cur_y += 8;
}
self.draw_string(cur_x, cur_y, word, gfx_buf);
cur_x += word_width as u8 + 4;
}
}
pub fn draw_string(&self,
x: u8, y: u8,
string: &str,
gfx_buf: &mut [u8; 8192],
) {
let mut x = x;
for c in string.chars() {
if c == ' ' {
x += 4;
continue;
}
let sprite_index = c as usize - 33;
self.draw_sprite(sprite_index, x, y, SpriteFlip::None, gfx_buf);
x += self.entries[sprite_index].width + 1;
}
}
fn get_str_pixel_width(&self, str:&str) -> usize {
let mut width = 0;
for c in str.chars() {
if c == ' ' {
width += 4;
continue;
}
let sprite_index = c as usize - 33;
width += self.entries[sprite_index].width as usize + 1;
}
width
}
pub fn draw_sprite(&self,
sprite_index: usize,
x: u8, y:u8,
flip: SpriteFlip,
gfx_buf: &mut [u8; 8192]) {
let entry = self.entries[sprite_index];
for sprite_y in 0..entry.height {
let tgt_y = sprite_y + y;
if tgt_y >= 64 {
continue;
}
for sprite_x in 0..entry.width {
let tgt_x = sprite_x + x;
if tgt_x >= 128 {
continue;
}
let px = match flip {
SpriteFlip::None => self.px_for_entry(sprite_index, sprite_x, sprite_y),
SpriteFlip::Horizontal => self.px_for_entry(sprite_index, entry.width - sprite_x - 1, sprite_y),
SpriteFlip::Vertical => self.px_for_entry(sprite_index, sprite_x, entry.height - sprite_y - 1),
SpriteFlip::Both => self.px_for_entry(sprite_index, entry.width - sprite_x - 1, entry.height - sprite_y - 1),
};
if px == 3 {
continue;
}
let target_index = (tgt_y as usize)*128 + tgt_x as usize;
gfx_buf[target_index] = px;
}
}
}
pub fn px_for_entry(&self, entry_index:usize, x:u8, y:u8) -> u8 {
let entry = self.entries[entry_index];
// input is uncompressed coordinate system
// we need the pixels from the compressed coordinate system
let x = x + entry.x; // uncompressed
let y = y + entry.y; // uncompressed
// uncompressed, we're looking for x,y
// however as the compressed tiles are 2bpp (4px per byte)
// we need to find the byte that contains the pixel we're looking for
let sprite_row_byte_index = (y as usize)*ATLAS_WIDTH/4 + (x as usize)/4;
let sprite_row_byte = SPRITES_COMPRESSED[sprite_row_byte_index];
let x_remain = x % 4;
// eg if x = 0, we want the first 2 bits of the byte
// if x = 1, we want the second 2 bits of the byte
// therefore, @ x = 0:
// (sprite_row_byte & (0b1100_0000 >> 0)) >> 6
// @ x = 1:
// (sprite_row_byte & (0b1100_0000 >> 2)) >> 4
// @ x = 2:
// (sprite_row_byte & (0b1100_0000 >> 4)) >> 2
// etc
let px_byte = (sprite_row_byte & (0b1100_0000 >> (x_remain*2))) >> (6 - x_remain*2);
px_byte
}
}