Begin DV Stick display driver

dv_stick
Mike Bell 2023-05-09 22:32:33 +01:00 zatwierdzone przez Phil Howard
rodzic a7efef0049
commit 5f8e7556f0
17 zmienionych plików z 927 dodań i 1 usunięć

Wyświetl plik

@ -44,3 +44,5 @@ add_subdirectory(psram_display)
add_subdirectory(inky73)
add_subdirectory(shiftregister)
add_subdirectory(mlx90640)
add_subdirectory(aps6404)
add_subdirectory(dv_display)

Wyświetl plik

@ -0,0 +1 @@
include(aps6404.cmake)

Wyświetl plik

@ -0,0 +1,12 @@
set(DRIVER_NAME aps6404)
add_library(${DRIVER_NAME} INTERFACE)
target_sources(${DRIVER_NAME} INTERFACE
${CMAKE_CURRENT_LIST_DIR}/aps6404.cpp
)
pico_generate_pio_header(${DRIVER_NAME} ${CMAKE_CURRENT_LIST_DIR}/aps6404.pio)
target_include_directories(${DRIVER_NAME} INTERFACE ${CMAKE_CURRENT_LIST_DIR})
target_link_libraries(${DRIVER_NAME} INTERFACE pico_stdlib hardware_pio hardware_dma hardware_irq)

Wyświetl plik

@ -0,0 +1,255 @@
#include <algorithm>
#include "aps6404.hpp"
#include "hardware/dma.h"
#include "hardware/irq.h"
#include "hardware/sync.h"
#include "hardware/clocks.h"
#include "pico/stdlib.h"
#include "aps6404.pio.h"
namespace pimoroni {
APS6404::APS6404(uint pin_csn, uint pin_d0, PIO pio)
: pin_csn(pin_csn)
, pin_d0(pin_d0)
, pio(pio)
{
// Initialize data pins
for (int i = 0; i < 4; ++i) {
gpio_init(pin_d0 + i);
gpio_disable_pulls(pin_d0 + i);
}
pio_prog = &sram_reset_program;
pio_offset = pio_add_program(pio, &sram_reset_program);
pio_sm = pio_claim_unused_sm(pio, true);
// Claim DMA channels
dma_channel = dma_claim_unused_channel(true);
read_cmd_dma_channel = dma_claim_unused_channel(true);
}
void APS6404::init() {
pio_sm_set_enabled(pio, pio_sm, false);
pio_remove_program(pio, pio_prog, pio_offset);
pio_prog = &sram_reset_program;
pio_offset = pio_add_program(pio, &sram_reset_program);
aps6404_reset_program_init(pio, pio_sm, pio_offset, pin_csn, pin_d0);
sleep_us(200);
pio_sm_put_blocking(pio, pio_sm, 0x00000007u);
pio_sm_put_blocking(pio, pio_sm, 0x66000000u);
pio_sm_put_blocking(pio, pio_sm, 0x00000007u);
pio_sm_put_blocking(pio, pio_sm, 0x99000000u);
pio_sm_put_blocking(pio, pio_sm, 0x00000007u);
pio_sm_put_blocking(pio, pio_sm, 0x35000000u);
sleep_us(500);
adjust_clock();
}
void APS6404::set_qpi() {
pio_sm_set_enabled(pio, pio_sm, false);
pio_remove_program(pio, pio_prog, pio_offset);
pio_prog = &sram_reset_program;
pio_offset = pio_add_program(pio, &sram_reset_program);
aps6404_reset_program_init(pio, pio_sm, pio_offset, pin_csn, pin_d0);
pio_sm_put_blocking(pio, pio_sm, 0x00000007u);
pio_sm_put_blocking(pio, pio_sm, 0x35000000u);
while (!pio_sm_is_tx_fifo_empty(pio, pio_sm) || pio->sm[pio_sm].addr != pio_offset);
adjust_clock();
}
void APS6404::set_spi() {
pio_sm_set_enabled(pio, pio_sm, false);
pio_remove_program(pio, pio_prog, pio_offset);
pio_prog = &sram_reset_qpi_program;
pio_offset = pio_add_program(pio, &sram_reset_qpi_program);
aps6404_program_init(pio, pio_sm, pio_offset, pin_csn, pin_d0, false, false, true);
pio_sm_put_blocking(pio, pio_sm, 0x00000001u);
pio_sm_put_blocking(pio, pio_sm, 0xF5000000u);
}
void APS6404::adjust_clock() {
pio_sm_set_enabled(pio, pio_sm, false);
pio_remove_program(pio, pio_prog, pio_offset);
if (clock_get_hz(clk_sys) > 296000000) {
pio_prog = &sram_fast_program;
pio_offset = pio_add_program(pio, &sram_fast_program);
aps6404_program_init(pio, pio_sm, pio_offset, pin_csn, pin_d0, false, true, false);
}
else if (clock_get_hz(clk_sys) < 130000000) {
pio_prog = &sram_slow_program;
pio_offset = pio_add_program(pio, &sram_slow_program);
aps6404_program_init(pio, pio_sm, pio_offset, pin_csn, pin_d0, true, false, false);
}
else {
pio_prog = &sram_program;
pio_offset = pio_add_program(pio, &sram_program);
aps6404_program_init(pio, pio_sm, pio_offset, pin_csn, pin_d0, false, false, false);
}
}
void APS6404::write(uint32_t addr, uint32_t* data, uint32_t len_in_bytes) {
wait_for_finish_blocking();
setup_cmd_buffer_dma(true);
for (int len = len_in_bytes, page_len = std::min(PAGE_SIZE, len);
len > 0;
addr += page_len, data += page_len >> 2, len -= page_len, page_len = std::min(PAGE_SIZE, len))
{
wait_for_finish_blocking();
pio_sm_put_blocking(pio, pio_sm, (page_len << 1) - 1);
pio_sm_put_blocking(pio, pio_sm, 0x38000000u | addr);
pio_sm_put_blocking(pio, pio_sm, pio_offset + sram_offset_do_write);
dma_channel_config c = dma_channel_get_default_config(dma_channel);
channel_config_set_read_increment(&c, true);
channel_config_set_write_increment(&c, false);
channel_config_set_dreq(&c, pio_get_dreq(pio, pio_sm, true));
channel_config_set_transfer_data_size(&c, DMA_SIZE_32);
channel_config_set_bswap(&c, true);
dma_channel_configure(
dma_channel, &c,
&pio->txf[pio_sm],
data,
(page_len >> 2) + 1,
true
);
}
}
void APS6404::write_repeat(uint32_t addr, uint32_t data, uint32_t len_in_bytes) {
repeat_data = data;
wait_for_finish_blocking();
setup_cmd_buffer_dma(true);
for (int len = len_in_bytes, page_len = std::min(PAGE_SIZE, len);
len > 0;
addr += page_len, len -= page_len, page_len = std::min(PAGE_SIZE, len))
{
wait_for_finish_blocking();
pio_sm_put_blocking(pio, pio_sm, (page_len << 1) - 1);
pio_sm_put_blocking(pio, pio_sm, 0x38000000u | addr);
pio_sm_put_blocking(pio, pio_sm, pio_offset + sram_offset_do_write);
dma_channel_config c = dma_channel_get_default_config(dma_channel);
channel_config_set_read_increment(&c, false);
channel_config_set_write_increment(&c, false);
channel_config_set_dreq(&c, pio_get_dreq(pio, pio_sm, true));
channel_config_set_transfer_data_size(&c, DMA_SIZE_32);
channel_config_set_bswap(&c, true);
dma_channel_configure(
dma_channel, &c,
&pio->txf[pio_sm],
&repeat_data,
(page_len >> 2) + 1,
true
);
}
}
void APS6404::read(uint32_t addr, uint32_t* read_buf, uint32_t len_in_words) {
start_read(read_buf, len_in_words);
uint32_t first_page_len = (PAGE_SIZE - (addr & (PAGE_SIZE - 1))) >> 2;
if (first_page_len >= len_in_words) {
pio_sm_put_blocking(pio, pio_sm, (len_in_words * 8) - 4);
pio_sm_put_blocking(pio, pio_sm, 0xeb000000u | addr);
pio_sm_put_blocking(pio, pio_sm, pio_offset + sram_offset_do_read);
}
else {
// Must always reset the cmd buffer DMA because writes use the same DREQ
setup_cmd_buffer_dma();
uint32_t* cmd_buf = add_read_to_cmd_buffer(multi_read_cmd_buffer, addr, len_in_words);
dma_channel_transfer_from_buffer_now(read_cmd_dma_channel, multi_read_cmd_buffer, cmd_buf - multi_read_cmd_buffer);
}
}
void APS6404::multi_read(uint32_t* addresses, uint32_t* lengths, uint32_t num_reads, uint32_t* read_buf, int chain_channel) {
uint32_t total_len = 0;
uint32_t* cmd_buf = multi_read_cmd_buffer;
for (uint32_t i = 0; i < num_reads; ++i) {
total_len += lengths[i];
cmd_buf = add_read_to_cmd_buffer(cmd_buf, addresses[i], lengths[i]);
}
start_read(read_buf, total_len, chain_channel);
setup_cmd_buffer_dma();
dma_channel_transfer_from_buffer_now(read_cmd_dma_channel, multi_read_cmd_buffer, cmd_buf - multi_read_cmd_buffer);
}
void APS6404::start_read(uint32_t* read_buf, uint32_t total_len_in_words, int chain_channel) {
wait_for_finish_blocking();
dma_channel_config c = dma_channel_get_default_config(dma_channel);
channel_config_set_read_increment(&c, false);
channel_config_set_write_increment(&c, true);
channel_config_set_dreq(&c, pio_get_dreq(pio, pio_sm, false));
channel_config_set_transfer_data_size(&c, DMA_SIZE_32);
channel_config_set_bswap(&c, true);
if (chain_channel >= 0) {
channel_config_set_chain_to(&c, chain_channel);
}
dma_channel_configure(
dma_channel, &c,
read_buf,
&pio->rxf[pio_sm],
total_len_in_words,
true
);
}
void APS6404::setup_cmd_buffer_dma(bool clear) {
dma_channel_config c = dma_channel_get_default_config(read_cmd_dma_channel);
channel_config_set_read_increment(&c, true);
channel_config_set_write_increment(&c, false);
if (!clear) channel_config_set_dreq(&c, pio_get_dreq(pio, pio_sm, true));
channel_config_set_transfer_data_size(&c, DMA_SIZE_32);
dma_channel_configure(
read_cmd_dma_channel, &c,
&pio->txf[pio_sm],
multi_read_cmd_buffer,
0,
false
);
}
uint32_t* APS6404::add_read_to_cmd_buffer(uint32_t* cmd_buf, uint32_t addr, uint32_t len_in_words) {
uint32_t len = (PAGE_SIZE - (addr & (PAGE_SIZE - 1))) >> 2;
while (true) {
*cmd_buf++ = (len * 8) - 4;
*cmd_buf++ = 0xeb000000u | addr;
*cmd_buf++ = pio_offset + sram_offset_do_read;
len_in_words -= len;
addr += len << 2;
if (len_in_words == 0) break;
len = len_in_words;
if (len > (PAGE_SIZE >> 2)) len = PAGE_SIZE >> 2;
}
return cmd_buf;
}
void APS6404::wait_for_finish_blocking() {
dma_channel_wait_for_finish_blocking(dma_channel);
}
}

Wyświetl plik

@ -0,0 +1,65 @@
#pragma once
#include <stdint.h>
#include "hardware/pio.h"
namespace pimoroni {
class APS6404 {
public:
static constexpr int RAM_SIZE = 8 * 1024 * 1024;
static constexpr int PAGE_SIZE = 1024;
APS6404(uint pin_csn = 17, uint pin_d0 = 19, PIO pio = pio0);
void init();
void set_qpi();
void set_spi();
// Must be called if the system clock rate is changed after init().
void adjust_clock();
// Start a write, this completes asynchronously, this function blocks if another
// transfer is already in progress
// Writes should always be <= 1KB.
void write(uint32_t addr, uint32_t* data, uint32_t len_in_bytes);
void write_repeat(uint32_t addr, uint32_t data, uint32_t len_in_bytes);
// Start a read, this completes asynchronously, this function only blocks if another
// transfer is already in progress
void read(uint32_t addr, uint32_t* read_buf, uint32_t len_in_words);
// Start multiple reads to the same buffer. They completes asynchronously,
// this function only blocks if another transfer is already in progress
void multi_read(uint32_t* addresses, uint32_t* lengths, uint32_t num_addresses, uint32_t* read_buf, int chain_channel = -1);
// Read and block until completion
void read_blocking(uint32_t addr, uint32_t* read_buf, uint32_t len_in_words) {
read(addr, read_buf, len_in_words);
wait_for_finish_blocking();
}
// Block until any outstanding read or write completes
void wait_for_finish_blocking();
private:
void start_read(uint32_t* read_buf, uint32_t total_len_in_words, int chain_channel = -1);
void setup_cmd_buffer_dma(bool clear = false);
uint32_t* add_read_to_cmd_buffer(uint32_t* cmd_buf, uint32_t addr, uint32_t len_in_words);
uint pin_csn; // CSn, SCK must be next pin after CSn
uint pin_d0; // D0, D1, D2, D3 must be consecutive
PIO pio;
uint16_t pio_sm;
uint16_t pio_offset;
const pio_program* pio_prog;
uint dma_channel;
uint read_cmd_dma_channel;
static constexpr int MULTI_READ_MAX_PAGES = 128;
uint32_t multi_read_cmd_buffer[3 * MULTI_READ_MAX_PAGES];
uint32_t repeat_data;
};
}

Wyświetl plik

@ -0,0 +1,222 @@
; Pins:
; - CSn is side-set 0
; - SCK is side-set 1
; - D0 is IN/OUT/SET pin 0
;
; DMA is 32 bit. Stream takes form:
; 32 bits For read: length of read in nibbles, minus 4
; For write: length of write in nibbles, minus 1
; Original data length in bits must be a multiple of 32
; 8 bits AP6404 cmd (0xeb = read, 0x38 = write)
; 24 bits address
; 32 bits sram_offset_do_read or sram_offset_do_write
; For write, data
; Program for running at high frequencies (130MHz-290MHz RP2040 system clock)
; Waits an extra half cycle on read as the data back from the PSRAM appears a fixed
; amount of *time* after the CLK
.program sram
.side_set 2
.wrap_target
top:
set x, 6 side 0b01
out y, 32 side 0b01
set pindirs, 0xf side 0b01
; Write command, followed by data
addr_lp:
out pins, 4 side 0b00
jmp x--, addr_lp side 0b10
out pins, 4 side 0b00
out pc, 32 side 0b10
PUBLIC do_write:
out pins, 4 side 0b00
jmp y--, do_write side 0b10
set pindirs, 0 side 0b00
out null, 32 side 0b01
jmp top side 0b01
; Read command
PUBLIC do_read:
; Wait for 8.5 clocks (6 + 2.5 for latencies)
set x, 7 side 0b00
rwt_lp:
set pindirs, 0 side 0b10
jmp x-- rwt_lp side 0b00
set x, 2 side 0b10
rd_lp:
in pins, 4 side 0b00
jmp y--, rd_lp side 0b10
; Read the remaining 3 nibbles, but no more clocks so we don't read beyond
; the requested length from the SRAM.
rd_rem:
in pins, 4 side 0b01
jmp x--, rd_rem side 0b01
.wrap
; Program for running at low frequencies (<130MHz RP2040 system clock)
.program sram_slow
.side_set 2
.wrap_target
top:
set x, 6 side 0b01
out y, 32 side 0b01
set pindirs, 0xf side 0b01
; Write command, followed by data
addr_lp:
out pins, 4 side 0b00
jmp x--, addr_lp side 0b10
out pins, 4 side 0b00
out pc, 32 side 0b10
PUBLIC do_write:
out pins, 4 side 0b00
jmp y--, do_write side 0b10
set pindirs, 0 side 0b00
out null, 32 side 0b01
jmp top side 0b01
; Read command
PUBLIC do_read:
set pindirs, 0 side 0b00
; Wait for 8 clocks (6 + 2 for latencies)
set x, 6 side 0b10
rwt_lp:
nop side 0b00
jmp x-- rwt_lp side 0b10
set x, 2 side 0b00
rd_lp:
in pins, 4 side 0b10
jmp y--, rd_lp side 0b00
; Read the remaining 3 nibbles, but no more clocks so we don't read beyond
; the requested length from the SRAM.
rd_rem:
in pins, 4 side 0b01
jmp x--, rd_rem side 0b01
.wrap
; Program for running at extreme frequencies (>290MHz RP2040 system clock)
; Runs clock at 1/3rd RP2040 clock with 66% duty cycle. Out of spec but
; then everything we are doing here is!
.program sram_fast
.side_set 2
.wrap_target
top:
set x, 6 side 0b01
out y, 32 side 0b01
set pindirs, 0xf side 0b01
; Write command, followed by data
addr_lp:
out pins, 4 side 0b00
jmp x--, addr_lp [1] side 0b10
out pins, 4 side 0b00
out pc, 32 [1] side 0b10
PUBLIC do_write:
out pins, 4 side 0b00
jmp y--, do_write [1] side 0b10
set pindirs, 0 side 0b00
out null, 32 side 0b01
jmp top side 0b01
; Read command
PUBLIC do_read:
set pindirs, 0 side 0b00
; Wait for 8 clocks (6 + 2 for latencies)
set x, 6 [1] side 0b10
rwt_lp:
nop side 0b00
jmp x-- rwt_lp [1] side 0b10
set x, 2 side 0b00
rd_lp:
nop side 0b10
in pins, 4 side 0b10
jmp y--, rd_lp side 0b00
; Read the remaining 2 nibbles, but no more clocks so we don't read beyond
; the requested length from the SRAM.
rd_rem:
in pins, 4 [1] side 0b01
jmp x--, rd_rem side 0b01
.wrap
; Write only program that writes a short (<32 bit) command,
; ignoring the rest of the 32 bit input word.
; This is used to write the reset command and enter QPI mode.
.program sram_reset
.side_set 2
.wrap_target
out y, 32 side 0b01
pull side 0b01
wr_lp:
out pins, 1 side 0b00
jmp y--, wr_lp side 0b10
out null, 32 [7] side 0b01 ; Ensure large enough gap between commands for reset time
nop [7] side 0b01
.wrap
.program sram_reset_qpi
.side_set 2
.wrap_target
out y, 32 side 0b01
pull side 0b01
set pindirs, 0xF side 0b01
wr_lp:
out pins, 4 side 0b00
jmp y--, wr_lp side 0b10
out null, 32 [7] side 0b01 ; Ensure large enough gap between commands for reset time
set pindirs, 0 [7] side 0b01
.wrap
% c-sdk {
void aps6404_reset_program_init(PIO pio, uint sm, uint offset, uint csn, uint mosi) {
uint miso = mosi + 1;
pio_gpio_init(pio, csn);
pio_gpio_init(pio, csn + 1);
pio_gpio_init(pio, mosi);
pio_sm_set_consecutive_pindirs(pio, sm, csn, 2, true);
pio_sm_set_consecutive_pindirs(pio, sm, mosi, 1, true);
pio_sm_set_consecutive_pindirs(pio, sm, miso, 1, false);
pio_sm_config c = sram_reset_program_get_default_config(offset);
sm_config_set_in_pins(&c, miso);
sm_config_set_in_shift(&c, false, true, 32);
sm_config_set_out_pins(&c, mosi, 1);
sm_config_set_out_shift(&c, false, true, 32);
sm_config_set_sideset_pins(&c, csn);
sm_config_set_clkdiv(&c, 4.f);
pio_sm_init(pio, sm, offset, &c);
pio_sm_set_enabled(pio, sm, true);
}
void aps6404_program_init(PIO pio, uint sm, uint offset, uint csn, uint mosi, bool slow, bool fast, bool reset) {
pio_gpio_init(pio, csn);
pio_gpio_init(pio, csn + 1);
pio_gpio_init(pio, mosi);
pio_gpio_init(pio, mosi + 1);
pio_gpio_init(pio, mosi + 2);
pio_gpio_init(pio, mosi + 3);
pio_sm_set_consecutive_pindirs(pio, sm, csn, 2, true);
pio_sm_set_consecutive_pindirs(pio, sm, mosi, 4, false);
pio_sm_config c = slow ? sram_slow_program_get_default_config(offset) :
fast ? sram_fast_program_get_default_config(offset) :
reset ? sram_reset_qpi_program_get_default_config(offset) :
sram_program_get_default_config(offset);
sm_config_set_in_pins(&c, mosi);
sm_config_set_in_shift(&c, false, true, 32);
sm_config_set_out_pins(&c, mosi, 4);
sm_config_set_out_shift(&c, false, true, 32);
sm_config_set_set_pins(&c, mosi, 4);
sm_config_set_sideset_pins(&c, csn);
pio_sm_init(pio, sm, offset, &c);
pio_sm_set_enabled(pio, sm, true);
}
%}

Wyświetl plik

@ -0,0 +1 @@
include(dv_display.cmake)

Wyświetl plik

@ -0,0 +1,10 @@
set(DRIVER_NAME dv_display)
add_library(${DRIVER_NAME} INTERFACE)
target_sources(${DRIVER_NAME} INTERFACE
${CMAKE_CURRENT_LIST_DIR}/${DRIVER_NAME}.cpp)
target_include_directories(${DRIVER_NAME} INTERFACE ${CMAKE_CURRENT_LIST_DIR})
# Pull in pico libraries that we need
target_link_libraries(${DRIVER_NAME} INTERFACE pico_stdlib aps6404 pimoroni_i2c)

Wyświetl plik

@ -0,0 +1,102 @@
#include "dv_display.hpp"
#include <cstdlib>
#include <math.h>
#include <string.h>
namespace pimoroni {
void DVDisplay::init() {
gpio_init(RAM_SEL);
gpio_put(RAM_SEL, 0);
gpio_set_dir(RAM_SEL, GPIO_OUT);
gpio_init(VSYNC);
gpio_set_dir(VSYNC, GPIO_IN);
ram.init();
write_header(0);
gpio_put(RAM_SEL, 1);
ram.init();
write_header(1);
bank = 0;
gpio_put(RAM_SEL, 0);
i2c.reg_write_uint8(I2C_ADDR, I2C_REG_START, 1);
}
void DVDisplay::flip() {
bank ^= 1;
ram.wait_for_finish_blocking();
while (gpio_get(VSYNC) == 0);
gpio_put(RAM_SEL, bank);
}
void DVDisplay::write(uint32_t address, size_t len, const uint16_t *data)
{
if ((uintptr_t)data & 2) {
write(address, 1, *data);
data++;
len--;
address += 2;
}
if (len > 0) {
ram.write(address, (uint32_t*)data, len << 1);
}
}
void DVDisplay::write(uint32_t address, size_t len, const uint16_t colour)
{
uint32_t val = colour | ((uint32_t)colour << 16);
ram.write_repeat(address, val, len << 1);
}
void DVDisplay::read(uint32_t address, size_t len, uint16_t *data)
{
ram.read_blocking(address, (uint32_t*)data, (len + 1) >> 1);
}
void DVDisplay::write_pixel(const Point &p, uint16_t colour)
{
write(pointToAddress(p), 1, colour);
}
void DVDisplay::write_pixel_span(const Point &p, uint l, uint16_t colour)
{
write(pointToAddress(p), l, colour);
}
void DVDisplay::read_pixel_span(const Point &p, uint l, uint16_t *data)
{
read(pointToAddress(p), l, data);
}
void DVDisplay::write_header(uint bank)
{
uint32_t buf[8];
buf[0] = 0x4F434950;
buf[1] = 0x01010101;
buf[2] = (uint32_t)width << 16;
buf[3] = (uint32_t)height << 16;
buf[4] = 0x00000001;
buf[5] = 0x00010000 + height + (bank << 24);
buf[6] = 0x00000000;
ram.write(0, buf, 7 * 4);
ram.wait_for_finish_blocking();
uint addr = 4 * 7;
for (int i = 0; i < height; i += 8) {
for (int j = 0; j < 8; ++j) {
buf[j] = 0x91000000 + ((i + j) * width * 2) + base_address;
}
ram.write(addr, buf, 8 * 4);
ram.wait_for_finish_blocking();
addr += 4 * 8;
}
sleep_us(100);
}
}

Wyświetl plik

@ -0,0 +1,105 @@
#pragma once
#include <cstring>
#include "pico/stdlib.h"
#include "hardware/gpio.h"
#include "common/pimoroni_common.hpp"
#include "common/pimoroni_i2c.hpp"
#include "drivers/aps6404/aps6404.hpp"
#include "libraries/pico_graphics/pico_graphics.hpp"
// Pimoroni DV Stick
namespace pimoroni {
// This is ARGB1555 only for now
class DVDisplay : public IDirectDisplayDriver<uint16_t> {
//--------------------------------------------------
// Variables
//--------------------------------------------------
private:
// Ram accessed through the APS6404 driver
APS6404 ram;
// I2C interface to driver
I2C i2c;
// interface pins
static constexpr uint CS = 17;
static constexpr uint D0 = 19;
static constexpr uint VSYNC = 16;
static constexpr uint RAM_SEL = 8;
static constexpr uint I2C_SDA = 6;
static constexpr uint I2C_SCL = 7;
// I2C
static constexpr uint I2C_ADDR = 0x0D;
static constexpr uint I2C_REG_START = 0xF9;
static constexpr uint32_t base_address = 0x10000;
uint16_t width = 0;
uint16_t height = 0;
uint8_t bank = 0;
public:
DVDisplay(uint16_t width, uint16_t height)
: ram(CS, D0)
, i2c(I2C_SDA, I2C_SCL)
, width(width), height(height)
{}
//--------------------------------------------------
// Methods
//--------------------------------------------------
public:
void test(void){
char writeBuffer[256];
char readBuffer[256];
uint mb = 8;
for (uint b = 0; b < 2; ++b) {
gpio_put(RAM_SEL, b);
for(uint k = 0; k < 1024*mb; k++) {
sprintf(writeBuffer, "%u-%u", b, k);
write(k*1024, strlen(writeBuffer)+1, (uint16_t *)writeBuffer);
}
}
for (uint b = 0; b < 2; ++b) {
gpio_put(RAM_SEL, b);
bool bSame = true;
for(uint k = 0; k < 1024*mb && bSame; k++)
{
sprintf(writeBuffer, "%u-%u", b, k);
read(k*1024, strlen(writeBuffer)+1, (uint16_t *)readBuffer);
bSame = strcmp(writeBuffer, readBuffer) == 0;
printf("[%u] %s == %s ? %s\n", k, writeBuffer, readBuffer, bSame ? "Success" : "Failure");
}
}
}
void write_pixel(const Point &p, uint16_t colour) override;
void write_pixel_span(const Point &p, uint l, uint16_t colour) override;
void read_pixel_span(const Point &p, uint l, uint16_t *data) override;
void init();
void flip();
private:
void write(uint32_t address, size_t len, const uint16_t *data);
void write(uint32_t address, size_t len, const uint16_t byte);
void read(uint32_t address, size_t len, uint16_t *data);
void write_header(uint bank);
uint32_t pointToAddress(const Point &p)
{
return base_address + ((p.y * (uint32_t)width) + p.x) * 2;
}
};
}

Wyświetl plik

@ -62,3 +62,4 @@ add_subdirectory(galactic_unicorn)
add_subdirectory(gfx_pack)
add_subdirectory(cosmic_unicorn)
add_subdirectory(stellar_unicorn)
add_subdirectory(dv_stick)

Wyświetl plik

@ -0,0 +1 @@
include(dv_stick_test.cmake)

Wyświetl plik

@ -0,0 +1,14 @@
set(OUTPUT_NAME dv_stick_test)
add_executable(
${OUTPUT_NAME}
dv_stick_test.cpp
)
# Pull in pico libraries that we need
target_link_libraries(${OUTPUT_NAME} pico_stdlib dv_display hardware_i2c pico_graphics)
pico_enable_stdio_usb(${OUTPUT_NAME} 1)
# create map/bin/hex file etc.
pico_add_extra_outputs(${OUTPUT_NAME})

Wyświetl plik

@ -0,0 +1,77 @@
#include <cstdio>
#include <math.h>
#include <stdio.h>
#include "hardware/gpio.h"
#include "drivers/dv_display/dv_display.hpp"
#include "libraries/pico_graphics/pico_graphics.hpp"
using namespace pimoroni;
#define FRAME_WIDTH 640
#define FRAME_HEIGHT 480
int main() {
stdio_init_all();
constexpr uint BUTTON_A = 9;
gpio_init(BUTTON_A);
gpio_set_dir(BUTTON_A, GPIO_IN);
gpio_pull_up(BUTTON_A);
sleep_ms(5000);
DVDisplay display(FRAME_WIDTH, FRAME_HEIGHT);
display.init();
//display.test();
PicoGraphics_PenDV_RGB555 graphics(FRAME_WIDTH, FRAME_HEIGHT, display);
graphics.set_pen(0x001F);
graphics.clear();
display.flip();
sleep_ms(2000);
graphics.set_pen(0x7C00);
graphics.clear();
display.flip();
printf("Starting\n");
while (true) {
while(gpio_get(BUTTON_A) == 1) {
sleep_ms(10);
}
uint32_t render_start_time = time_us_32();
graphics.set_pen(0xFFFF);
graphics.clear();
for(int i =0 ; i < 50 ; i++)
{
uint size = 25 + (rand() % 50);
uint x = rand() % graphics.bounds.w;
uint y = rand() % graphics.bounds.h;
graphics.set_pen(0);
graphics.circle(Point(x, y), size);
graphics.set_pen(RGB::from_hsv(i * 0.02f, 1.0f, 1.0f).to_rgb555());
graphics.circle(Point(x, y), size-2);
}
#if 0
uint x = 260; //rand() % graphics.bounds.w;
uint y = 468; //rand() % graphics.bounds.h;
printf("Circle at (%d, %d)\n", x, y);
graphics.set_pen(0);
graphics.circle(Point(x, y), 25);
#endif
uint32_t render_time = time_us_32() - render_start_time;
uint32_t flip_start_time = time_us_32();
display.flip();
uint32_t flip_time = time_us_32() - flip_start_time;
printf("Render: %.3f, flip: %.3f\n", render_time / 1000.f, flip_time / 1000.f);
}
}

Wyświetl plik

@ -18,6 +18,7 @@ add_library(pico_graphics
${CMAKE_CURRENT_LIST_DIR}/pico_graphics_pen_rgb565.cpp
${CMAKE_CURRENT_LIST_DIR}/pico_graphics_pen_rgb888.cpp
${CMAKE_CURRENT_LIST_DIR}/pico_graphics_pen_inky7.cpp
${CMAKE_CURRENT_LIST_DIR}/pico_graphics_pen_dv_rgb555.cpp
)
target_include_directories(pico_graphics INTERFACE ${CMAKE_CURRENT_LIST_DIR})

Wyświetl plik

@ -26,6 +26,7 @@
namespace pimoroni {
typedef uint8_t RGB332;
typedef uint16_t RGB565;
typedef uint16_t RGB555;
typedef uint32_t RGB888;
@ -108,6 +109,14 @@ namespace pimoroni {
return __builtin_bswap16(p);
}
constexpr RGB555 to_rgb555() {
uint16_t p = ((r & 0b11111000) << 7) |
((g & 0b11111000) << 2) |
((b & 0b11111000) >> 3);
return p;
}
constexpr RGB565 to_rgb332() {
return (r & 0b11100000) | ((g & 0b11100000) >> 3) | ((b & 0b11000000) >> 6);
}
@ -192,7 +201,8 @@ namespace pimoroni {
PEN_RGB332,
PEN_RGB565,
PEN_RGB888,
PEN_INKY7
PEN_INKY7,
PEN_DV_RGB555
};
void *frame_buffer;
@ -579,4 +589,22 @@ namespace pimoroni {
return w * h;
}
};
class PicoGraphics_PenDV_RGB555 : public PicoGraphics {
public:
RGB555 color;
IDirectDisplayDriver<uint16_t> &driver;
PicoGraphics_PenDV_RGB555(uint16_t width, uint16_t height, IDirectDisplayDriver<uint16_t> &direct_display_driver);
void set_pen(uint c) override;
void set_pen(uint8_t r, uint8_t g, uint8_t b) override;
int create_pen(uint8_t r, uint8_t g, uint8_t b) override;
int create_pen_hsv(float h, float s, float v) override;
void set_pixel(const Point &p) override;
void set_pixel_span(const Point &p, uint l) override;
static size_t buffer_size(uint w, uint h) {
return w * h * sizeof(RGB555);
}
};
}

Wyświetl plik

@ -0,0 +1,29 @@
#include "pico_graphics.hpp"
namespace pimoroni {
PicoGraphics_PenDV_RGB555::PicoGraphics_PenDV_RGB555(uint16_t width, uint16_t height, IDirectDisplayDriver<uint16_t> &direct_display_driver)
: PicoGraphics(width, height, nullptr),
driver(direct_display_driver)
{
this->pen_type = PEN_DV_RGB555;
}
void PicoGraphics_PenDV_RGB555::set_pen(uint c) {
color = c;
}
void PicoGraphics_PenDV_RGB555::set_pen(uint8_t r, uint8_t g, uint8_t b) {
RGB src_color{r, g, b};
color = src_color.to_rgb555();
}
int PicoGraphics_PenDV_RGB555::create_pen(uint8_t r, uint8_t g, uint8_t b) {
return RGB(r, g, b).to_rgb555();
}
int PicoGraphics_PenDV_RGB555::create_pen_hsv(float h, float s, float v) {
return RGB::from_hsv(h, s, v).to_rgb555();
}
void PicoGraphics_PenDV_RGB555::set_pixel(const Point &p) {
driver.write_pixel(p, color);
}
void PicoGraphics_PenDV_RGB555::set_pixel_span(const Point &p, uint l) {
driver.write_pixel_span(p, l, color);
}
}