Add DMA capabilities to fill solid-color portions of the display

This commit is contained in:
shylie 2026-02-25 14:46:47 -05:00
parent f47af9ee07
commit 19491d6038
4 changed files with 106 additions and 47 deletions

View File

@ -17,6 +17,8 @@ public:
void set_update_area(uint16_t left, uint16_t top, uint16_t width, void set_update_area(uint16_t left, uint16_t top, uint16_t width,
uint16_t height); uint16_t height);
void fill(uint16_t left, uint16_t top, uint16_t width, uint16_t height,
uint8_t r, uint8_t g, uint8_t b);
PixelStream pixels(); PixelStream pixels();
void clear(); void clear();
@ -24,6 +26,7 @@ public:
private: private:
uint8_t sck, tx, cs, dc; uint8_t sck, tx, cs, dc;
spi_inst_t* spi; spi_inst_t* spi;
int dma;
void send_command(uint8_t command); void send_command(uint8_t command);
void send_command(uint8_t command, uint8_t param); void send_command(uint8_t command, uint8_t param);

View File

@ -58,8 +58,6 @@ constexpr int MS_BUTTON_TIMEOUT = 500;
int main() int main()
{ {
set_sys_clock_khz(200 * 1000, false);
gpio_set_function(FLASH_IO_2, GPIO_FUNC_SIO); gpio_set_function(FLASH_IO_2, GPIO_FUNC_SIO);
gpio_set_function(FLASH_IO_3, GPIO_FUNC_SIO); gpio_set_function(FLASH_IO_3, GPIO_FUNC_SIO);

View File

@ -17,27 +17,13 @@ Flash flash(FLASH_SCK, FLASH_TX, FLASH_RX, FLASH_CS, 25 * 1000 * 1000);
void draw_rectangle(float x, float y, float w, float h, uint8_t r, uint8_t g, void draw_rectangle(float x, float y, float w, float h, uint8_t r, uint8_t g,
uint8_t b) uint8_t b)
{ {
int wi = w; display.fill(x - w / 2, y - h / 2, w, h, r, g, b);
int hi = h;
display.set_update_area(x - w / 2, y - h / 2, wi, hi);
const uint8_t buf[24] = {
r, g, b, r, g, b, r, g, b, r, g, b, r, g, b, r, g, b, r, g, b, r, g, b,
};
auto pixels = display.pixels();
for (int i = 0; i < (wi + 1) * (hi + 1); i += 8)
{
pixels.write(buf, sizeof(buf));
}
} }
} }
int main() int main()
{ {
set_sys_clock_khz(150 * 1000, false);
gpio_set_function(FLASH_IO_2, GPIO_FUNC_SIO); gpio_set_function(FLASH_IO_2, GPIO_FUNC_SIO);
gpio_set_function(FLASH_IO_3, GPIO_FUNC_SIO); gpio_set_function(FLASH_IO_3, GPIO_FUNC_SIO);
@ -68,11 +54,14 @@ int main()
constexpr float SPEED = 75; constexpr float SPEED = 75;
float paddle_x = 120; float paddle_x = 120;
float old_paddle_x = paddle_x;
constexpr float paddle_y = 280; constexpr float paddle_y = 280;
constexpr float BALL_SPEED = 125; constexpr float BALL_SPEED = 125;
float ball_x = 120; float ball_x = 120;
float ball_y = 160; float ball_y = 160;
float old_ball_x = ball_x;
float old_ball_y = ball_y;
float INITIAL_BALL_ANGLE = -3.1415926f / 4.0f; float INITIAL_BALL_ANGLE = -3.1415926f / 4.0f;
float ball_dx = cosf(INITIAL_BALL_ANGLE); float ball_dx = cosf(INITIAL_BALL_ANGLE);
@ -88,9 +77,6 @@ int main()
float dt = absolute_time_diff_us(previous_time, current) / 1000000.0f; float dt = absolute_time_diff_us(previous_time, current) / 1000000.0f;
previous_time = current; previous_time = current;
float old_ball_pos_x = ball_x;
float old_ball_pos_y = ball_y;
ball_x += ball_dx * BALL_SPEED * dt; ball_x += ball_dx * BALL_SPEED * dt;
ball_y += ball_dy * BALL_SPEED * dt; ball_y += ball_dy * BALL_SPEED * dt;
@ -116,8 +102,6 @@ int main()
ball_y = paddle_y - 6; ball_y = paddle_y - 6;
} }
float old_pos = paddle_x;
left_history = (left_history << 1) | !gpio_get(BUTTON_LEFT); left_history = (left_history << 1) | !gpio_get(BUTTON_LEFT);
right_history = (right_history << 1) | !gpio_get(BUTTON_RIGHT); right_history = (right_history << 1) | !gpio_get(BUTTON_RIGHT);
@ -133,17 +117,21 @@ int main()
time_since_last_display += dt; time_since_last_display += dt;
if (time_since_last_display > 0.02f) if (time_since_last_display > 0.016f)
{ {
// ball // ball
draw_rectangle(old_ball_pos_x, old_ball_pos_y, 12, 12, 0, 0, 0); draw_rectangle(old_ball_x, old_ball_y, 12, 12, 0, 0, 0);
draw_rectangle(ball_x, ball_y, 4, 4, 0xFF, 0xFF, 0xFF); draw_rectangle(ball_x, ball_y, 4, 4, 0xFF, 0xFF, 0xFF);
// player paddle // player paddle
draw_rectangle(old_pos - 20, paddle_y, 6, 7, 0, 0, 0); draw_rectangle(old_paddle_x - 20, paddle_y, 6, 7, 0, 0, 0);
draw_rectangle(old_pos + 20, paddle_y, 6, 7, 0, 0, 0); draw_rectangle(old_paddle_x + 20, paddle_y, 6, 7, 0, 0, 0);
draw_rectangle(paddle_x, paddle_y, 40, 5, 0xFF, 0xFF, 0xFF); draw_rectangle(paddle_x, paddle_y, 40, 5, 0xFF, 0xFF, 0xFF);
time_since_last_display = 0; time_since_last_display = 0;
old_paddle_x = paddle_x;
old_ball_x = ball_x;
old_ball_y = ball_y;
} }
} }
} }

View File

@ -3,11 +3,35 @@
#include "devicelib.h" #include "devicelib.h"
#include "pixelstream.h" #include "pixelstream.h"
#include <hardware/dma.h>
#include <hardware/irq.h>
#include <hardware/spi.h> #include <hardware/spi.h>
#include <pico/stdlib.h> #include <pico/stdlib.h>
using namespace lib; using namespace lib;
namespace
{
constexpr uint PIXELS_PER_TRANSFER = 16;
int dma = -1;
uint8_t cs;
volatile int transfers_left = 0;
volatile uint8_t color_buffer[3 * PIXELS_PER_TRANSFER];
void dma_handler()
{
dma_hw->ints0 = 1u << dma;
transfers_left -= PIXELS_PER_TRANSFER;
dma_channel_set_read_addr(dma, color_buffer, transfers_left > 0);
gpio_put(cs, transfers_left <= 0);
}
}
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) :
sck(sck), sck(sck),
@ -21,6 +45,36 @@ Display::Display(uint8_t sck, uint8_t tx, uint8_t cs, uint8_t dc,
hard_assert(sck_spi == tx_spi, "Invalid configuration"); hard_assert(sck_spi == tx_spi, "Invalid configuration");
spi = sck_spi; spi = sck_spi;
// only support one display with DMA for now
if (::dma < 0)
{
dma = dma_claim_unused_channel(false);
}
else
{
dma = -1;
}
if (dma >= 0)
{
::dma = dma;
::cs = cs;
dma_channel_config config = dma_channel_get_default_config(dma);
channel_config_set_transfer_data_size(&config, DMA_SIZE_8);
channel_config_set_read_increment(&config, true);
channel_config_set_write_increment(&config, false);
channel_config_set_dreq(&config, spi_get_dreq(spi, true));
dma_channel_configure(dma, &config, &spi_get_hw(spi)->dr, color_buffer,
3 * PIXELS_PER_TRANSFER, false);
dma_channel_set_irq0_enabled(dma, true);
irq_set_exclusive_handler(DMA_IRQ_0, dma_handler);
irq_set_enabled(DMA_IRQ_0, true);
}
gpio_set_function(sck, GPIO_FUNC_SPI); gpio_set_function(sck, GPIO_FUNC_SPI);
gpio_set_function(tx, GPIO_FUNC_SPI); gpio_set_function(tx, GPIO_FUNC_SPI);
@ -46,8 +100,6 @@ Display::Display(uint8_t sck, uint8_t tx, uint8_t cs, uint8_t dc,
send_command(0x3A, 0x66); send_command(0x3A, 0x66);
sleep_ms(10); sleep_ms(10);
set_update_area(0, 0, 240, 320);
// set memory addressing mode // set memory addressing mode
send_command(0x36, 0b11000000); send_command(0x36, 0b11000000);
@ -61,25 +113,18 @@ Display::Display(uint8_t sck, uint8_t tx, uint8_t cs, uint8_t dc,
// set display brightness // set display brightness
send_command(0x51, 0x00); send_command(0x51, 0x00);
// clear display to black
{
auto p = pixels();
for (int i = 0; i < 320 * 240; i++)
{
p.write(0, 0, 0);
}
}
// turn on display // turn on display
send_command(0x29); send_command(0x29);
sleep_ms(10); sleep_ms(10);
clear();
} }
void Display::set_update_area(uint16_t left, uint16_t top, uint16_t width, void Display::set_update_area(uint16_t left, uint16_t top, uint16_t width,
uint16_t height) uint16_t height)
{ {
const uint16_t right = left + width; const uint16_t right = left + width - 1;
const uint16_t bottom = top + height; const uint16_t bottom = top + height - 1;
{ {
uint8_t params[4] uint8_t params[4]
= { static_cast<uint8_t>(left >> 8), static_cast<uint8_t>(left & 0xFF), = { static_cast<uint8_t>(left >> 8), static_cast<uint8_t>(left & 0xFF),
@ -98,19 +143,44 @@ void Display::set_update_area(uint16_t left, uint16_t top, uint16_t width,
} }
} }
PixelStream Display::pixels() { return PixelStream(*this); } void Display::fill(uint16_t left, uint16_t top, uint16_t width,
uint16_t height, uint8_t r, uint8_t g, uint8_t b)
void Display::clear()
{ {
set_update_area(0, 0, 240, 320); if (dma >= 0)
auto stream = pixels();
for (int i = 0; i < 240 * 320; i++)
{ {
stream.write(0, 0, 0); while (transfers_left > 0)
;
for (int i = 0; i < PIXELS_PER_TRANSFER; i++)
{
color_buffer[3 * i + 0] = r;
color_buffer[3 * i + 1] = g;
color_buffer[3 * i + 2] = b;
}
set_update_area(left, top, width, height);
gpio_put(cs, false);
send_command_and_begin_data_stream(0x2C);
transfers_left = width * height + PIXELS_PER_TRANSFER;
dma_channel_set_read_addr(dma, color_buffer, true);
}
// fallback if DMA is not available
else
{
set_update_area(left, top, width, height);
auto pixels = this->pixels();
for (int i = 0; i < width * height; i++)
{
pixels.write(r, g, b);
}
} }
} }
PixelStream Display::pixels() { return PixelStream(*this); }
void Display::clear() { fill(0, 0, 240, 320, 0, 0, 0); }
void Display::send_command(uint8_t command) void Display::send_command(uint8_t command)
{ {
send_command(command, nullptr, 0); send_command(command, nullptr, 0);