Worked out a font and a canvas

I'm adding a 16-segment-based font here, and have encoded the numbers 0-10.

I've also worked out a way to make a Canvas structure not crash the pico.
This commit is contained in:
Savanni D'Gerinel 2025-03-03 10:24:50 -05:00
parent 21c6f30a7d
commit 85e5d0bb5e
5 changed files with 634 additions and 30 deletions

52
Cargo.lock generated
View File

@ -1,6 +1,6 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 3
version = 4
[[package]]
name = "addr2line"
@ -693,6 +693,12 @@ dependencies = [
"syn 1.0.109",
]
[[package]]
name = "const-default"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0b396d1f76d455557e1218ec8066ae14bba60b4b36ecd55577ba979f5db7ecaa"
[[package]]
name = "const-oid"
version = "0.9.6"
@ -1057,6 +1063,18 @@ dependencies = [
"serde 1.0.218",
]
[[package]]
name = "embedded-alloc"
version = "0.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8f2de9133f68db0d4627ad69db767726c99ff8585272716708227008d3f1bddd"
dependencies = [
"const-default",
"critical-section",
"linked_list_allocator",
"rlsf",
]
[[package]]
name = "embedded-dma"
version = "0.2.0"
@ -2870,6 +2888,12 @@ dependencies = [
"fixed",
]
[[package]]
name = "linked_list_allocator"
version = "0.10.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9afa463f5405ee81cdb9cc2baf37e08ec7e4c8209442b5d72c04cfb2cd6e6286"
[[package]]
name = "linux-raw-sys"
version = "0.4.15"
@ -3540,6 +3564,7 @@ name = "pico-st7789"
version = "0.1.0"
dependencies = [
"cortex-m-rt",
"embedded-alloc",
"embedded-hal 1.0.0",
"fugit",
"panic-halt",
@ -4131,6 +4156,18 @@ dependencies = [
"thiserror 1.0.69",
]
[[package]]
name = "rlsf"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "222fb240c3286247ecdee6fa5341e7cdad0ffdf8e7e401d9937f2d58482a20bf"
dependencies = [
"cfg-if",
"const-default",
"libc",
"svgbobdoc",
]
[[package]]
name = "rp-pico"
version = "0.9.0"
@ -4844,6 +4881,19 @@ version = "2.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292"
[[package]]
name = "svgbobdoc"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f2c04b93fc15d79b39c63218f15e3fdffaa4c227830686e3b7c5f41244eb3e50"
dependencies = [
"base64 0.9.3",
"proc-macro2",
"quote",
"syn 1.0.109",
"unicode-width",
]
[[package]]
name = "syn"
version = "1.0.109"

View File

@ -5,6 +5,7 @@ edition = "2021"
[dependencies]
cortex-m-rt = "0.7.3"
embedded-alloc = "0.6.0"
embedded-hal = "1.0.0"
fugit = "0.3.7"
panic-halt = "1.0.0"

125
pico-st7789/src/drawing.rs Normal file
View File

@ -0,0 +1,125 @@
// a a a
// f b
// f b
// f b
// g g g
// e c
// e c
// e c
// d d d
use alloc::{boxed::Box, vec::Vec};
pub struct Canvas<'a> {
pub buf: &'a mut [u8],
pub width: usize,
}
impl<'a> Canvas<'a> {
pub fn new(buf: &'a mut [u8], columns: usize) -> Self {
Self {
buf,
width: columns,
}
}
pub fn set_pixel(&mut self, x: usize, y: usize, color: (u8, u8, u8)) {
self.buf[(y * self.width + x) * 3 + 0] = color.0 << 2;
self.buf[(y * self.width + x) * 3 + 1] = color.1 << 2;
self.buf[(y * self.width + x) * 3 + 2] = color.2 << 2;
}
}
/*
pub const DIGIT_WIDTH: usize = 5;
pub const DIGIT_HEIGHT: usize = 9;
pub const ZERO_SEVEN_SEGMENT: [bool; 7] = [true, true, true, true, true, true, false];
pub const ONE_SEVEN_SEGMENT: [bool; 7] = [false, true, true, false, false, false, false];
pub const TWO_SEVEN_SEGMENT: [bool; 7] = [true, true, false, true, true, false, true];
pub const THREE_SEVEN_SEGMENT: [bool; 7] = [true, true, true, true, false, false, true];
pub const FOUR_SEVEN_SEGMENT: [bool; 7] = [false, true, true, false, false, true, true];
pub const FIVE_SEVEN_SEGMENT: [bool; 7] = [true, false, true, true, false, true, true];
pub const SIX_SEVEN_SEGMENT: [bool; 7] = [true, false, true, true, true, true, true];
pub const SEVEN_SEVEN_SEGMENT: [bool; 7] = [true, true, true, false, false, false, false];
pub const EIGHT_SEVEN_SEGMENT: [bool; 7] = [true, true, true, true, true, true, true];
pub const NINE_SEVEN_SEGMENT: [bool; 7] = [true, true, true, false, false, true, true];
pub const SEVEN_SEGMENT_FONT: [[bool; 7]; 10] = [
ZERO_SEVEN_SEGMENT,
ONE_SEVEN_SEGMENT,
TWO_SEVEN_SEGMENT,
THREE_SEVEN_SEGMENT,
FOUR_SEVEN_SEGMENT,
FIVE_SEVEN_SEGMENT,
SIX_SEVEN_SEGMENT,
SEVEN_SEVEN_SEGMENT,
EIGHT_SEVEN_SEGMENT,
NINE_SEVEN_SEGMENT,
];
*/
/*
pub fn draw_pixel(frame: &mut Canvas, x: usize, y: usize, color: (u8, u8, u8)) {
frame.buf[(y * frame.width + x) * 3 + 0] = color.0 << 2;
frame.buf[(y * frame.width + x) * 3 + 1] = color.1 << 2;
frame.buf[(y * frame.width + x) * 3 + 2] = color.2 << 2;
}
*/
/*
pub fn draw_seven_segment(
digit: u8,
frame: &mut [u8],
width: usize,
x: usize,
y: usize,
color: (u8, u8, u8),
) {
let segments = SEVEN_SEGMENT_FONT[digit as usize];
if segments[0] {
write_pixel(frame, x + 1, y, color);
write_pixel(frame, x + 2, y, color);
write_pixel(frame, x + 3, y, color);
}
if segments[1] {
write_pixel(frame, x + 4, y + 1, color);
write_pixel(frame, x + 4, y + 2, color);
write_pixel(frame, x + 4, y + 3, color);
}
if segments[2] {
write_pixel(frame, x + 4, y + 5, color);
write_pixel(frame, x + 4, y + 6, color);
write_pixel(frame, x + 4, y + 7, color);
}
if segments[3] {
write_pixel(frame, x + 1, y + 8, color);
write_pixel(frame, x + 2, y + 8, color);
write_pixel(frame, x + 3, y + 8, color);
}
if segments[4] {
write_pixel(frame, x, y + 5, color);
write_pixel(frame, x, y + 6, color);
write_pixel(frame, x, y + 7, color);
}
if segments[5] {
write_pixel(frame, x, y + 1, color);
write_pixel(frame, x, y + 2, color);
write_pixel(frame, x, y + 3, color);
}
if segments[6] {
write_pixel(frame, x + 1, y + 4, color);
write_pixel(frame, x + 2, y + 4, color);
write_pixel(frame, x + 3, y + 4, color);
}
}
*/

398
pico-st7789/src/font.rs Normal file
View File

@ -0,0 +1,398 @@
use alloc::collections::btree_map::BTreeMap;
use crate::drawing::Canvas;
pub trait Font<A> {
fn glyph(&self, c: char) -> &A;
}
pub trait Glyph {
fn draw(&self, canvas: &mut Canvas, x: usize, y: usize, color: (u8, u8, u8));
}
// Sixteen Segments
//
// a1 a2
// f h i jb
// f i j b
// f hij b
// g1 g2
// e klm c
// e k l m c
// ek l mc
// d1 d2
pub struct SixteenSegmentGlyph {
a1: bool,
a2: bool,
b: bool,
c: bool,
d1: bool,
d2: bool,
e: bool,
f: bool,
g1: bool,
g2: bool,
h: bool,
i: bool,
j: bool,
k: bool,
l: bool,
m: bool,
}
pub struct SixteenSegmentFont(BTreeMap<char, SixteenSegmentGlyph>);
impl SixteenSegmentFont {
pub fn new() -> Self {
let mut font = BTreeMap::new();
font.insert(
'*',
SixteenSegmentGlyph {
a1: true,
a2: true,
b: true,
c: true,
d1: true,
d2: true,
e: true,
f: true,
g1: true,
g2: true,
h: true,
i: true,
j: true,
k: true,
l: true,
m: true,
},
);
font.insert(
'0',
SixteenSegmentGlyph {
a1: true,
a2: true,
b: true,
c: true,
d1: true,
d2: true,
e: true,
f: true,
g1: false,
g2: false,
h: false,
i: false,
j: true,
k: true,
l: false,
m: false,
},
);
font.insert(
'1',
SixteenSegmentGlyph {
a1: false,
a2: false,
b: true,
c: true,
d1: false,
d2: false,
e: false,
f: false,
g1: false,
g2: false,
h: false,
i: false,
j: true,
k: false,
l: false,
m: false,
},
);
font.insert(
'2',
SixteenSegmentGlyph {
a1: true,
a2: true,
b: true,
c: false,
d1: true,
d2: true,
e: true,
f: false,
g1: true,
g2: true,
h: false,
i: false,
j: false,
k: false,
l: false,
m: false,
},
);
font.insert(
'3',
SixteenSegmentGlyph {
a1: true,
a2: true,
b: true,
c: true,
d1: true,
d2: true,
e: false,
f: false,
g1: false,
g2: true,
h: false,
i: false,
j: false,
k: false,
l: false,
m: false,
},
);
font.insert(
'4',
SixteenSegmentGlyph {
a1: false,
a2: false,
b: true,
c: true,
d1: false,
d2: false,
e: false,
f: true,
g1: true,
g2: true,
h: false,
i: false,
j: false,
k: false,
l: false,
m: false,
},
);
font.insert(
'5',
SixteenSegmentGlyph {
a1: true,
a2: true,
b: false,
c: false,
d1: true,
d2: true,
e: false,
f: true,
g1: true,
g2: false,
h: false,
i: false,
j: false,
k: false,
l: false,
m: true,
},
);
font.insert(
'6',
SixteenSegmentGlyph {
a1: true,
a2: true,
b: false,
c: true,
d1: true,
d2: true,
e: true,
f: true,
g1: true,
g2: true,
h: false,
i: false,
j: false,
k: false,
l: false,
m: false,
},
);
font.insert(
'7',
SixteenSegmentGlyph {
a1: true,
a2: true,
b: true,
c: true,
d1: false,
d2: false,
e: false,
f: false,
g1: false,
g2: false,
h: false,
i: false,
j: false,
k: false,
l: false,
m: false,
},
);
font.insert(
'8',
SixteenSegmentGlyph {
a1: true,
a2: true,
b: true,
c: true,
d1: true,
d2: true,
e: true,
f: true,
g1: true,
g2: true,
h: false,
i: false,
j: false,
k: false,
l: false,
m: false,
},
);
font.insert(
'9',
SixteenSegmentGlyph {
a1: true,
a2: true,
b: true,
c: true,
d1: true,
d2: true,
e: false,
f: true,
g1: true,
g2: true,
h: false,
i: false,
j: false,
k: false,
l: false,
m: false,
},
);
Self(font)
}
}
impl Font<SixteenSegmentGlyph> for SixteenSegmentFont {
fn glyph(&self, c: char) -> &SixteenSegmentGlyph {
self.0.get(&c).unwrap()
}
}
impl Glyph for SixteenSegmentGlyph {
fn draw(&self, canvas: &mut Canvas, x: usize, y: usize, color: (u8, u8, u8)) {
if self.a1 {
canvas.set_pixel(x + 1, y, color);
canvas.set_pixel(x + 2, y, color);
canvas.set_pixel(x + 3, y, color);
}
if self.a2 {
canvas.set_pixel(x + 5, y, color);
canvas.set_pixel(x + 6, y, color);
canvas.set_pixel(x + 7, y, color);
}
if self.b {
canvas.set_pixel(x + 8, y + 1, color);
canvas.set_pixel(x + 8, y + 2, color);
canvas.set_pixel(x + 8, y + 3, color);
canvas.set_pixel(x + 8, y + 4, color);
canvas.set_pixel(x + 8, y + 5, color);
}
if self.c {
canvas.set_pixel(x + 8, y + 7, color);
canvas.set_pixel(x + 8, y + 8, color);
canvas.set_pixel(x + 8, y + 9, color);
canvas.set_pixel(x + 8, y + 10, color);
canvas.set_pixel(x + 8, y + 11, color);
}
if self.d1 {
canvas.set_pixel(x + 1, y + 12, color);
canvas.set_pixel(x + 2, y + 12, color);
canvas.set_pixel(x + 3, y + 12, color);
}
if self.d2 {
canvas.set_pixel(x + 5, y + 12, color);
canvas.set_pixel(x + 6, y + 12, color);
canvas.set_pixel(x + 7, y + 12, color);
}
if self.e {
canvas.set_pixel(x, y + 7, color);
canvas.set_pixel(x, y + 8, color);
canvas.set_pixel(x, y + 9, color);
canvas.set_pixel(x, y + 10, color);
canvas.set_pixel(x, y + 11, color);
}
if self.f {
canvas.set_pixel(x, y + 1, color);
canvas.set_pixel(x, y + 2, color);
canvas.set_pixel(x, y + 3, color);
canvas.set_pixel(x, y + 4, color);
canvas.set_pixel(x, y + 5, color);
}
if self.g1 {
canvas.set_pixel(x + 1, y + 6, color);
canvas.set_pixel(x + 2, y + 6, color);
canvas.set_pixel(x + 3, y + 6, color);
}
if self.g2 {
canvas.set_pixel(x + 5, y + 6, color);
canvas.set_pixel(x + 6, y + 6, color);
canvas.set_pixel(x + 7, y + 6, color);
}
if self.h {
canvas.set_pixel(x + 1, y + 1, color);
canvas.set_pixel(x + 1, y + 2, color);
canvas.set_pixel(x + 2, y + 3, color);
canvas.set_pixel(x + 3, y + 4, color);
canvas.set_pixel(x + 3, y + 5, color);
}
if self.i {
canvas.set_pixel(x + 4, y + 1, color);
canvas.set_pixel(x + 4, y + 2, color);
canvas.set_pixel(x + 4, y + 3, color);
canvas.set_pixel(x + 4, y + 4, color);
canvas.set_pixel(x + 4, y + 5, color);
}
if self.j {
canvas.set_pixel(x + 7, y + 1, color);
canvas.set_pixel(x + 7, y + 2, color);
canvas.set_pixel(x + 6, y + 3, color);
canvas.set_pixel(x + 5, y + 4, color);
canvas.set_pixel(x + 5, y + 5, color);
}
if self.k {
canvas.set_pixel(x + 3, y + 7, color);
canvas.set_pixel(x + 3, y + 8, color);
canvas.set_pixel(x + 2, y + 9, color);
canvas.set_pixel(x + 1, y + 10, color);
canvas.set_pixel(x + 1, y + 11, color);
}
if self.l {
canvas.set_pixel(x + 4, y + 7, color);
canvas.set_pixel(x + 4, y + 8, color);
canvas.set_pixel(x + 4, y + 9, color);
canvas.set_pixel(x + 4, y + 10, color);
canvas.set_pixel(x + 4, y + 11, color);
}
if self.m {
canvas.set_pixel(x + 5, y + 7, color);
canvas.set_pixel(x + 5, y + 8, color);
canvas.set_pixel(x + 6, y + 9, color);
canvas.set_pixel(x + 7, y + 10, color);
canvas.set_pixel(x + 7, y + 11, color);
}
}
}

View File

@ -1,23 +1,26 @@
#![no_main]
#![no_std]
use embedded_hal::{delay::DelayNs, digital::OutputPin, spi::SpiBus};
extern crate alloc;
use embedded_alloc::LlffHeap as Heap;
use embedded_hal::{delay::DelayNs, digital::OutputPin};
use fugit::RateExtU32;
use panic_halt as _;
use rp_pico::{
entry,
hal::{
clocks::init_clocks_and_plls,
gpio::{FunctionSio, Pin, PinId, PullDown, SioOutput},
spi::{Enabled, Spi, SpiDevice, ValidSpiPinout},
Clock, Sio, Timer, Watchdog,
},
hal::{clocks::init_clocks_and_plls, spi::Spi, Clock, Sio, Timer, Watchdog},
pac, Pins,
};
mod drawing;
use drawing::{Canvas};
mod font;
use font::{Font, Glyph, SixteenSegmentFont};
mod st7789;
use st7789::{ST7789Display, SETUP_PROGRAM};
pub use st7789::Step;
const XOSC_CRYSTAL_FREQ: u32 = 12_000_000; // MHz, https://forums.raspberrypi.com/viewtopic.php?t=356764
@ -25,15 +28,21 @@ const ROWS: usize = 320;
const COLUMNS: usize = 170;
const FRAMEBUF: usize = ROWS * COLUMNS * 3;
/*
struct Frame {
columns: usize,
buf: [u8; FRAMEBUF],
}
*/
#[global_allocator]
static HEAP: Heap = Heap::empty();
#[entry]
unsafe fn main() -> ! {
{
use core::mem::MaybeUninit;
const HEAP_SIZE: usize = 1024;
static mut HEAP_MEM: [MaybeUninit<u8>; HEAP_SIZE] = [MaybeUninit::uninit(); HEAP_SIZE];
unsafe { HEAP.init(HEAP_MEM.as_ptr() as usize, HEAP_SIZE) }
}
let font_sixteen = SixteenSegmentFont::new();
// rp_pico::pac::Peripherals is a reference to physical hardware defined on the Pico.
let mut peripherals = pac::Peripherals::take().unwrap();
@ -92,8 +101,8 @@ unsafe fn main() -> ! {
&mut peripherals.RESETS,
// The SPI system uses the peripheral clock
clocks.peripheral_clock.freq(),
// Transmit data at a rate of 1Mbit.
1_u32.MHz(),
// Transmit data at a rate of 32Mbit.
32_u32.MHz(),
// Run with SPI Mode 1. This means that the clock line should start high and that data will
// be sampled starting at the first falling edge.
embedded_hal::spi::MODE_3,
@ -110,31 +119,52 @@ unsafe fn main() -> ! {
timer.delay_ms(1000);
let mut frame: [u8; FRAMEBUF] = [0; FRAMEBUF];
let mut framebuf = [0; FRAMEBUF];
let mut canvas = Canvas::new(&mut framebuf, COLUMNS);
let mut strength = 0;
loop {
led.set_high();
{
let display = display.acquire();
display.send_buf(&frame);
let _ = led.set_high();
timer.delay_ms(100);
display.send_buf(&canvas.buf);
let _ = led.set_low();
}
/*
draw_seven_segment(0, &mut frame, 0, 0, (255, 255, 255));
draw_seven_segment(1, &mut frame, 7, 0, (255, 255, 255));
draw_seven_segment(2, &mut frame, 14, 0, (255, 255, 255));
draw_seven_segment(3, &mut frame, 21, 0, (255, 255, 255));
draw_seven_segment(4, &mut frame, 28, 0, (255, 255, 255));
draw_seven_segment(5, &mut frame, 35, 0, (255, 255, 255));
draw_seven_segment(6, &mut frame, 42, 0, (255, 255, 255));
draw_seven_segment(7, &mut frame, 49, 0, (255, 255, 255));
draw_seven_segment(8, &mut frame, 56, 0, (255, 255, 255));
draw_seven_segment(9, &mut frame, 63, 0, (255, 255, 255));
*/
font_sixteen.glyph('*').draw(&mut canvas, 0, 10, (255, 255, 255));
font_sixteen.glyph('0').draw(&mut canvas, 11, 10, (255, 255, 255));
font_sixteen.glyph('1').draw(&mut canvas, 22, 10, (255, 255, 255));
font_sixteen.glyph('2').draw(&mut canvas, 33, 10, (255, 255, 255));
font_sixteen.glyph('3').draw(&mut canvas, 44, 10, (255, 255, 255));
font_sixteen.glyph('4').draw(&mut canvas, 55, 10, (255, 255, 255));
font_sixteen.glyph('5').draw(&mut canvas, 66, 10, (255, 255, 255));
font_sixteen.glyph('6').draw(&mut canvas, 77, 10, (255, 255, 255));
font_sixteen.glyph('7').draw(&mut canvas, 88, 10, (255, 255, 255));
font_sixteen.glyph('8').draw(&mut canvas, 99, 10, (255, 255, 255));
font_sixteen.glyph('9').draw(&mut canvas, 110, 10, (255, 255, 255));
/*
for x in 80..90 {
for y in 155..165 {
write_pixel(&mut frame, x, y, (0, 0, 63));
draw_pixel(&mut frame, x, y, (0, 0, 63));
}
}
timer.delay_ms(10);
led.set_low();
*/
timer.delay_ms(1000);
}
}
fn write_pixel(framebuf: &mut [u8], x: usize, y: usize, color: (u8, u8, u8)) {
framebuf[(y * COLUMNS + x) * 3 + 0] = color.0 << 2;
framebuf[(y * COLUMNS + x) * 3 + 1] = color.1 << 2;
framebuf[(y * COLUMNS + x) * 3 + 2] = color.2 << 2;
}