Load image from flash instead

This commit is contained in:
shylie 2026-01-05 15:32:51 -05:00
parent c3166e54b2
commit 88a6671d37
10 changed files with 239 additions and 41 deletions

View File

@ -8,6 +8,8 @@ add_executable(mtgcard
src/main.cpp src/main.cpp
src/display.cpp src/display.cpp
src/pixelstream.cpp src/pixelstream.cpp
src/lib.cpp
src/flash.cpp
src/usb_descriptors.c src/usb_descriptors.c
) )

View File

@ -3,7 +3,7 @@
#include <hardware/spi.h> #include <hardware/spi.h>
namespace display namespace lib
{ {
class PixelStream; class PixelStream;

58
include/flash.h Normal file
View File

@ -0,0 +1,58 @@
#ifndef FLASH_H
#define FLASH_H
#include <hardware/spi.h>
namespace lib
{
class Flash
{
public:
class Page
{
public:
static constexpr size_t SIZE = 256;
static constexpr size_t COUNT_PER_SECTOR = 4096 / SIZE;
operator uint8_t*();
operator const uint8_t*() const;
uint8_t& operator[](int address);
uint8_t operator[](int address) const;
private:
uint8_t buffer[SIZE];
};
Flash(uint8_t sck, uint8_t tx, uint8_t rx, uint8_t cs, int baudrate);
void read_page(uint16_t page_index);
void write_page(uint16_t page_index);
void erase_sector(uint16_t page_index);
Page& page();
const Page& page() const;
private:
uint8_t sck, tx, rx, cs;
spi_inst_t* spi;
Page page_buffer;
void write_enable();
void wait_done();
static constexpr uint8_t CMD_PAGE_PROGRAM = 0x02;
static constexpr uint8_t CMD_READ = 0x03;
static constexpr uint8_t CMD_STATUS = 0x05;
static constexpr uint8_t CMD_WRITE_EN = 0x06;
static constexpr uint8_t CMD_SECTOR_ERASE = 0x20;
static constexpr uint8_t STATUS_BUSY_MASK = 0x01;
};
}
#endif // FLASH_H

13
include/lib.h Normal file
View File

@ -0,0 +1,13 @@
#ifndef LIB_H
#define LIB_H
#include <hardware/spi.h>
namespace lib::detail
{
spi_inst_t* get_spi_instance(uint8_t gpio);
}
#endif // LIB_H

View File

@ -3,7 +3,7 @@
#include <pico/stdlib.h> #include <pico/stdlib.h>
namespace display namespace lib
{ {
class Display; class Display;
@ -17,6 +17,8 @@ public:
PixelStream& operator=(const PixelStream&) = delete; PixelStream& operator=(const PixelStream&) = delete;
~PixelStream(); ~PixelStream();
// prefer other overload when possible
void write(uint8_t data);
void write(uint8_t red, uint8_t green, uint8_t blue); void write(uint8_t red, uint8_t green, uint8_t blue);
private: private:

View File

@ -1,21 +1,12 @@
#include "display.h" #include "display.h"
#include "lib.h"
#include "pixelstream.h" #include "pixelstream.h"
#include <hardware/spi.h> #include <hardware/spi.h>
#include <pico/stdlib.h> #include <pico/stdlib.h>
namespace using namespace lib;
{
spi_inst_t* get_spi_instance(uint8_t gpio)
{
return gpio % 16 < 8 ? spi0 : spi1;
}
}
using namespace display;
Display::Display(uint8_t sck, uint8_t tx, uint8_t cs, uint8_t dc, Display::Display(uint8_t sck, uint8_t tx, uint8_t cs, uint8_t dc,
int baudrate) : int baudrate) :
@ -24,8 +15,8 @@ Display::Display(uint8_t sck, uint8_t tx, uint8_t cs, uint8_t dc,
cs(cs), cs(cs),
dc(dc) dc(dc)
{ {
spi_inst_t* sck_spi = get_spi_instance(sck); spi_inst_t* sck_spi = detail::get_spi_instance(sck);
spi_inst_t* tx_spi = get_spi_instance(tx); spi_inst_t* tx_spi = detail::get_spi_instance(tx);
hard_assert(sck_spi == tx_spi, "Invalid configuration"); hard_assert(sck_spi == tx_spi, "Invalid configuration");
spi = sck_spi; spi = sck_spi;
@ -68,7 +59,7 @@ Display::Display(uint8_t sck, uint8_t tx, uint8_t cs, uint8_t dc,
sleep_ms(10); sleep_ms(10);
// set display brightness // set display brightness
send_command(0x51, 0xFF); send_command(0x51, 0x00);
// clear display to black // clear display to black
{ {

102
src/flash.cpp Normal file
View File

@ -0,0 +1,102 @@
#include "flash.h"
#include "lib.h"
#include <pico/stdlib.h>
using namespace lib;
Flash::Page::operator unsigned char*() { return buffer; }
Flash::Page::operator const unsigned char*() const { return buffer; }
uint8_t& Flash::Page::operator[](int address) { return buffer[address]; }
uint8_t Flash::Page::operator[](int address) const { return buffer[address]; }
Flash::Flash(uint8_t sck, uint8_t tx, uint8_t rx, uint8_t cs, int baudrate) :
sck(sck),
tx(tx),
rx(rx),
cs(cs)
{
spi_inst_t* sck_spi = detail::get_spi_instance(sck);
spi_inst_t* tx_spi = detail::get_spi_instance(tx);
spi_inst_t* rx_spi = detail::get_spi_instance(rx);
hard_assert(sck_spi == tx_spi && tx_spi == rx_spi, "Invalid configuration");
spi = sck_spi;
gpio_set_function(sck, GPIO_FUNC_SPI);
gpio_set_function(tx, GPIO_FUNC_SPI);
gpio_set_function(rx, GPIO_FUNC_SPI);
gpio_set_function(cs, GPIO_FUNC_SIO);
gpio_set_dir(cs, GPIO_OUT);
gpio_put(cs, true);
spi_init(spi, baudrate);
}
void Flash::read_page(uint16_t page_index)
{
const uint8_t cmd[5] = { CMD_READ, static_cast<uint8_t>(page_index >> 8),
static_cast<uint8_t>(page_index), 0, 0 };
gpio_put(cs, false);
spi_write_blocking(spi, cmd, 4);
spi_read_blocking(spi, 0, page_buffer, Page::SIZE);
gpio_put(cs, true);
}
void Flash::write_page(uint16_t page_index)
{
write_enable();
uint8_t cmd[4] = { CMD_PAGE_PROGRAM, static_cast<uint8_t>(page_index >> 8),
static_cast<uint8_t>(page_index), 0 };
gpio_put(cs, false);
spi_write_blocking(spi, cmd, 4);
spi_write_blocking(spi, page_buffer, Page::SIZE);
gpio_put(cs, true);
wait_done();
}
void Flash::erase_sector(uint16_t page_index)
{
write_enable();
uint8_t cmd[4] = { CMD_SECTOR_ERASE, static_cast<uint8_t>(page_index >> 8),
static_cast<uint8_t>(page_index), 0 };
gpio_put(cs, false);
spi_write_blocking(spi, cmd, 4);
gpio_put(cs, true);
wait_done();
}
Flash::Page& Flash::page() { return page_buffer; }
const Flash::Page& Flash::page() const { return page_buffer; }
void Flash::write_enable()
{
uint8_t cmd = CMD_WRITE_EN;
gpio_put(cs, false);
spi_write_blocking(spi, &cmd, 1);
gpio_put(cs, true);
}
void Flash::wait_done()
{
for (uint8_t status = STATUS_BUSY_MASK; status & STATUS_BUSY_MASK;)
{
uint8_t cmd = CMD_STATUS;
gpio_put(cs, false);
spi_write_blocking(spi, &cmd, 1);
spi_read_blocking(spi, 0, &status, 1);
gpio_put(cs, true);
}
}

8
src/lib.cpp Normal file
View File

@ -0,0 +1,8 @@
#include "lib.h"
using namespace lib;
spi_inst_t* detail::get_spi_instance(uint8_t gpio)
{
return gpio % 16 < 8 ? spi0 : spi1;
}

View File

@ -1,16 +1,17 @@
#include "display.h" #include "display.h"
#include "flash.h"
#include "pixelstream.h" #include "pixelstream.h"
#include "tusb.h"
#include <pico/stdlib.h> #include <pico/stdlib.h>
#include <tusb.h>
using namespace lib;
namespace namespace
{ {
display::Display disp(2, 3, 5, 6, 5000000); Display disp(2, 3, 5, 6, 120000000);
uint16_t row = 0; Flash flash(26, 27, 28, 29, 70000000);
uint16_t column = 0;
uint8_t row_data[240 * 3];
} }
@ -20,6 +21,20 @@ int main()
= { .role = TUSB_ROLE_DEVICE, .speed = TUSB_SPEED_AUTO }; = { .role = TUSB_ROLE_DEVICE, .speed = TUSB_SPEED_AUTO };
tud_init(0); tud_init(0);
{
auto pixels = disp.pixels();
for (int page_index = 0; page_index < 900; page_index++)
{
flash.read_page(page_index);
const auto& page = flash.page();
for (int i = 0; i < Flash::Page::SIZE; i++)
{
pixels.write(page[i]);
}
}
}
while (true) while (true)
{ {
tud_task(); tud_task();
@ -28,32 +43,37 @@ int main()
void tud_vendor_rx_cb(uint8_t itf, const uint8_t* buffer, uint16_t bufsize) void tud_vendor_rx_cb(uint8_t itf, const uint8_t* buffer, uint16_t bufsize)
{ {
size_t written = 0; static size_t written = 0;
static uint16_t page_index = 0;
while (written < bufsize) if (written == 0 && page_index == 0)
{ {
size_t copy_size = bufsize - written < (240 * 3) - column for (int i = 0; i < 320 * 240 * 3 / 256; i += 4096 / 256)
? bufsize - written {
: (240 * 3) - column; flash.erase_sector(i);
memcpy(row_data + column, buffer + written, copy_size); }
column += copy_size; }
for (size_t read = 0; read < bufsize;)
{
size_t copy_size = bufsize - read < Flash::Page::SIZE - written
? bufsize - read
: Flash::Page::SIZE - written;
memcpy(flash.page() + written, buffer + read, copy_size);
read += copy_size;
written += copy_size; written += copy_size;
if (column >= 240 * 3) if (written == 256)
{ {
disp.set_update_area(0, row, 240, 320); flash.write_page(page_index);
auto pixels = disp.pixels(); page_index += 1;
for (int i = 0; i < 240; i++) written = 0;
{
pixels.write(row_data[i * 3], row_data[i * 3 + 1],
row_data[i * 3 + 2]);
}
column -= 240 * 3; if (page_index == 900)
row += 1;
if (row == 320)
{ {
row = 0; page_index = 0;
} }
} }
} }

View File

@ -2,7 +2,7 @@
#include "display.h" #include "display.h"
using namespace display; using namespace lib;
PixelStream::PixelStream(Display& display) : PixelStream::PixelStream(Display& display) :
display(display) display(display)
@ -10,6 +10,8 @@ PixelStream::PixelStream(Display& display) :
display.send_command_and_begin_data_stream(0x2C); display.send_command_and_begin_data_stream(0x2C);
} }
void PixelStream::write(uint8_t data) { display.send_data(data); }
void PixelStream::write(uint8_t red, uint8_t green, uint8_t blue) void PixelStream::write(uint8_t red, uint8_t green, uint8_t blue)
{ {
display.send_data(red); display.send_data(red);