Skip to content

Commit 2b9aefd

Browse files
committed
esp32-s3-box-3 - move framebuffer allocation from stack to heap
1 parent e9e4f3b commit 2b9aefd

File tree

2 files changed

+70
-23
lines changed

2 files changed

+70
-23
lines changed

esp32-s3-box-3/Cargo.toml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
[package]
22
name = "esp32-conways-game-of-life-rs"
3-
version = "0.5.0"
3+
version = "0.6.0"
44
authors = ["Juraj Michálek <[email protected]>"]
5-
edition = "2021"
5+
edition = "2024"
66
license = "MIT OR Apache-2.0"
77

88

esp32-s3-box-3/src/main.rs

Lines changed: 68 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -3,52 +3,92 @@
33

44
extern crate alloc;
55
use alloc::boxed::Box;
6+
use embedded_graphics_framebuf::backends::FrameBufferBackend;
67

8+
use bevy_ecs::prelude::*;
79
use core::fmt::Write;
8-
use embedded_hal::delay::DelayNs;
910
use embedded_graphics::{
10-
mono_font::{ascii::FONT_8X13, MonoTextStyle},
11+
Drawable,
12+
mono_font::{MonoTextStyle, ascii::FONT_8X13},
1113
pixelcolor::Rgb565,
1214
prelude::*,
1315
primitives::{PrimitiveStyle, Rectangle},
1416
text::Text,
15-
Drawable,
1617
};
1718
use embedded_graphics_framebuf::FrameBuf;
19+
use embedded_hal::delay::DelayNs;
20+
use embedded_hal_bus::spi::ExclusiveDevice;
1821
use esp_hal::delay::Delay;
22+
use esp_hal::dma::{DmaRxBuf, DmaTxBuf};
23+
use esp_hal::dma_buffers;
1924
use esp_hal::{
20-
gpio::{Level, Output, OutputConfig, DriveMode},
21-
rng::Rng,
22-
spi::master::{Spi, SpiDmaBus},
2325
Blocking,
26+
gpio::{DriveMode, Level, Output, OutputConfig},
2427
main,
28+
rng::Rng,
29+
spi::master::{Spi, SpiDmaBus},
2530
time::Rate,
2631
};
27-
use esp_hal::dma::{DmaRxBuf, DmaTxBuf};
28-
use esp_hal::dma_buffers;
29-
use embedded_hal_bus::spi::ExclusiveDevice;
3032
use esp_println::{logger::init_logger_from_env, println};
3133
use log::info;
32-
use mipidsi::{interface::SpiInterface, options::{ColorInversion, Orientation, ColorOrder}};
33-
use mipidsi::{models::ILI9486Rgb565, Builder};
34-
use bevy_ecs::prelude::*; // includes NonSend and NonSendMut
34+
use mipidsi::{Builder, models::ILI9486Rgb565};
35+
use mipidsi::{
36+
interface::SpiInterface,
37+
options::{ColorInversion, ColorOrder, Orientation},
38+
}; // includes NonSend and NonSendMut
3539

3640
#[panic_handler]
3741
fn panic(_info: &core::panic::PanicInfo) -> ! {
3842
println!("Panic: {}", _info);
3943
loop {}
4044
}
4145

46+
/// A wrapper around a boxed array that implements FrameBufferBackend.
47+
/// This allows the framebuffer to be allocated on the heap.
48+
pub struct HeapBuffer<C: PixelColor, const N: usize>(Box<[C; N]>);
49+
50+
impl<C: PixelColor, const N: usize> HeapBuffer<C, N> {
51+
pub fn new(data: Box<[C; N]>) -> Self {
52+
Self(data)
53+
}
54+
}
55+
56+
impl<C: PixelColor, const N: usize> core::ops::Deref for HeapBuffer<C, N> {
57+
type Target = [C; N];
58+
fn deref(&self) -> &Self::Target {
59+
&*self.0
60+
}
61+
}
62+
63+
impl<C: PixelColor, const N: usize> core::ops::DerefMut for HeapBuffer<C, N> {
64+
fn deref_mut(&mut self) -> &mut Self::Target {
65+
&mut *self.0
66+
}
67+
}
68+
69+
impl<C: PixelColor, const N: usize> FrameBufferBackend for HeapBuffer<C, N> {
70+
type Color = C;
71+
fn set(&mut self, index: usize, color: Self::Color) {
72+
self.0[index] = color;
73+
}
74+
fn get(&self, index: usize) -> Self::Color {
75+
self.0[index]
76+
}
77+
fn nr_elements(&self) -> usize {
78+
N
79+
}
80+
}
81+
4282
// --- Type Alias for the Concrete Display ---
4383
// Use the DMA-enabled SPI bus type.
4484
type MyDisplay = mipidsi::Display<
4585
SpiInterface<
4686
'static,
4787
ExclusiveDevice<SpiDmaBus<'static, Blocking>, Output<'static>, Delay>,
48-
Output<'static>
88+
Output<'static>,
4989
>,
5090
ILI9486Rgb565,
51-
Output<'static>
91+
Output<'static>,
5292
>;
5393

5494
// --- LCD Resolution and FrameBuffer Type Aliases ---
@@ -57,7 +97,7 @@ const LCD_V_RES: usize = 240;
5797
const LCD_BUFFER_SIZE: usize = LCD_H_RES * LCD_V_RES;
5898

5999
// We want our pixels stored as Rgb565.
60-
type FbBuffer = [Rgb565; LCD_BUFFER_SIZE];
100+
type FbBuffer = HeapBuffer<Rgb565, LCD_BUFFER_SIZE>;
61101
// Define a type alias for the complete FrameBuf.
62102
type MyFrameBuf = FrameBuf<Rgb565, FbBuffer>;
63103

@@ -68,9 +108,10 @@ struct FrameBufferResource {
68108

69109
impl FrameBufferResource {
70110
fn new() -> Self {
71-
// Allocate the framebuffer data as an owned array of Rgb565.
72-
let fb_data: FbBuffer = *Box::new([Rgb565::BLACK; LCD_BUFFER_SIZE]);
73-
let frame_buf = MyFrameBuf::new(fb_data, LCD_H_RES, LCD_V_RES);
111+
// Allocate the framebuffer data on the heap.
112+
let fb_data: Box<[Rgb565; LCD_BUFFER_SIZE]> = Box::new([Rgb565::BLACK; LCD_BUFFER_SIZE]);
113+
let heap_buffer = HeapBuffer::new(fb_data);
114+
let frame_buf = MyFrameBuf::new(heap_buffer, LCD_H_RES, LCD_V_RES);
74115
Self { frame_buf }
75116
}
76117
}
@@ -100,7 +141,9 @@ fn update_game_of_life(grid: &mut [[u8; GRID_WIDTH]; GRID_HEIGHT]) {
100141
let mut alive_neighbors = 0;
101142
for i in 0..3 {
102143
for j in 0..3 {
103-
if i == 1 && j == 1 { continue; }
144+
if i == 1 && j == 1 {
145+
continue;
146+
}
104147
let nx = (x + i + GRID_WIDTH - 1) % GRID_WIDTH;
105148
let ny = (y + j + GRID_HEIGHT - 1) % GRID_HEIGHT;
106149
if grid[ny][nx] > 0 {
@@ -128,7 +171,7 @@ fn update_game_of_life(grid: &mut [[u8; GRID_WIDTH]; GRID_HEIGHT]) {
128171
*grid = new_grid;
129172
}
130173

131-
/// Maps cell age (1..=max_age) to a color. Newborn cells are dark blue and older cells become brighter (toward white).
174+
/// Maps cell age (1...=max_age) to a color. Newborn cells are dark blue and older cells become brighter (toward white).
132175
fn age_to_color(age: u8) -> Rgb565 {
133176
if age == 0 {
134177
Rgb565::BLACK
@@ -315,7 +358,11 @@ fn main() -> ! {
315358
display_delay.delay_ns(500_000u32);
316359

317360
// Reset pin: OpenDrain required for ESP32-S3-BOX! Tricky setting.
318-
let reset = Output::new(peripherals.GPIO48, Level::High, OutputConfig::default().with_drive_mode(DriveMode::OpenDrain));
361+
let reset = Output::new(
362+
peripherals.GPIO48,
363+
Level::High,
364+
OutputConfig::default().with_drive_mode(DriveMode::OpenDrain),
365+
);
319366
// Initialize the display using mipidsi's builder.
320367
let mut display: MyDisplay = Builder::new(ILI9486Rgb565, di)
321368
.reset_pin(reset)

0 commit comments

Comments
 (0)