Add DMA capabilities to fill solid-color portions of the display
This commit is contained in:
parent
f47af9ee07
commit
19491d6038
@ -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);
|
||||||
|
|||||||
@ -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);
|
||||||
|
|
||||||
|
|||||||
@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
112
src/display.cpp
112
src/display.cpp
@ -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);
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user