diff --git a/include/display.h b/include/display.h index 9cd9f3a..f05946d 100644 --- a/include/display.h +++ b/include/display.h @@ -17,6 +17,8 @@ public: void set_update_area(uint16_t left, uint16_t top, uint16_t width, 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(); void clear(); @@ -24,6 +26,7 @@ public: private: uint8_t sck, tx, cs, dc; spi_inst_t* spi; + int dma; void send_command(uint8_t command); void send_command(uint8_t command, uint8_t param); diff --git a/programs/mtgcard/src/main.cpp b/programs/mtgcard/src/main.cpp index 22c29a4..3b95b91 100644 --- a/programs/mtgcard/src/main.cpp +++ b/programs/mtgcard/src/main.cpp @@ -58,8 +58,6 @@ constexpr int MS_BUTTON_TIMEOUT = 500; int main() { - set_sys_clock_khz(200 * 1000, false); - gpio_set_function(FLASH_IO_2, GPIO_FUNC_SIO); gpio_set_function(FLASH_IO_3, GPIO_FUNC_SIO); diff --git a/programs/pong/src/main.cpp b/programs/pong/src/main.cpp index 7806d04..6b85532 100644 --- a/programs/pong/src/main.cpp +++ b/programs/pong/src/main.cpp @@ -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, uint8_t b) { - int wi = w; - 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)); - } + display.fill(x - w / 2, y - h / 2, w, h, r, g, b); } } int main() { - set_sys_clock_khz(150 * 1000, false); - gpio_set_function(FLASH_IO_2, GPIO_FUNC_SIO); gpio_set_function(FLASH_IO_3, GPIO_FUNC_SIO); @@ -68,11 +54,14 @@ int main() constexpr float SPEED = 75; float paddle_x = 120; + float old_paddle_x = paddle_x; constexpr float paddle_y = 280; constexpr float BALL_SPEED = 125; float ball_x = 120; 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 ball_dx = cosf(INITIAL_BALL_ANGLE); @@ -88,9 +77,6 @@ int main() float dt = absolute_time_diff_us(previous_time, current) / 1000000.0f; 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_y += ball_dy * BALL_SPEED * dt; @@ -116,8 +102,6 @@ int main() ball_y = paddle_y - 6; } - float old_pos = paddle_x; - left_history = (left_history << 1) | !gpio_get(BUTTON_LEFT); right_history = (right_history << 1) | !gpio_get(BUTTON_RIGHT); @@ -133,17 +117,21 @@ int main() time_since_last_display += dt; - if (time_since_last_display > 0.02f) + if (time_since_last_display > 0.016f) { // 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); // player paddle - draw_rectangle(old_pos - 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(old_paddle_x + 20, paddle_y, 6, 7, 0, 0, 0); draw_rectangle(paddle_x, paddle_y, 40, 5, 0xFF, 0xFF, 0xFF); time_since_last_display = 0; + + old_paddle_x = paddle_x; + old_ball_x = ball_x; + old_ball_y = ball_y; } } } diff --git a/src/display.cpp b/src/display.cpp index e9a1314..4082df2 100644 --- a/src/display.cpp +++ b/src/display.cpp @@ -3,11 +3,35 @@ #include "devicelib.h" #include "pixelstream.h" +#include +#include #include #include 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, int baudrate) : 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"); 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(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); sleep_ms(10); - set_update_area(0, 0, 240, 320); - // set memory addressing mode 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 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 send_command(0x29); sleep_ms(10); + + clear(); } void Display::set_update_area(uint16_t left, uint16_t top, uint16_t width, uint16_t height) { - const uint16_t right = left + width; - const uint16_t bottom = top + height; + const uint16_t right = left + width - 1; + const uint16_t bottom = top + height - 1; { uint8_t params[4] = { static_cast(left >> 8), static_cast(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::clear() +void Display::fill(uint16_t left, uint16_t top, uint16_t width, + uint16_t height, uint8_t r, uint8_t g, uint8_t b) { - set_update_area(0, 0, 240, 320); - - auto stream = pixels(); - for (int i = 0; i < 240 * 320; i++) + if (dma >= 0) { - 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) { send_command(command, nullptr, 0);