kopia lustrzana https://github.com/pimoroni/pimoroni-pico
Begin DV Stick display driver
rodzic
a7efef0049
commit
5f8e7556f0
|
@ -44,3 +44,5 @@ add_subdirectory(psram_display)
|
||||||
add_subdirectory(inky73)
|
add_subdirectory(inky73)
|
||||||
add_subdirectory(shiftregister)
|
add_subdirectory(shiftregister)
|
||||||
add_subdirectory(mlx90640)
|
add_subdirectory(mlx90640)
|
||||||
|
add_subdirectory(aps6404)
|
||||||
|
add_subdirectory(dv_display)
|
|
@ -0,0 +1 @@
|
||||||
|
include(aps6404.cmake)
|
|
@ -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)
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
|
@ -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;
|
||||||
|
};
|
||||||
|
}
|
|
@ -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);
|
||||||
|
}
|
||||||
|
%}
|
|
@ -0,0 +1 @@
|
||||||
|
include(dv_display.cmake)
|
|
@ -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)
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
|
@ -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;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
|
@ -62,3 +62,4 @@ add_subdirectory(galactic_unicorn)
|
||||||
add_subdirectory(gfx_pack)
|
add_subdirectory(gfx_pack)
|
||||||
add_subdirectory(cosmic_unicorn)
|
add_subdirectory(cosmic_unicorn)
|
||||||
add_subdirectory(stellar_unicorn)
|
add_subdirectory(stellar_unicorn)
|
||||||
|
add_subdirectory(dv_stick)
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
include(dv_stick_test.cmake)
|
|
@ -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})
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
|
@ -18,6 +18,7 @@ add_library(pico_graphics
|
||||||
${CMAKE_CURRENT_LIST_DIR}/pico_graphics_pen_rgb565.cpp
|
${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_rgb888.cpp
|
||||||
${CMAKE_CURRENT_LIST_DIR}/pico_graphics_pen_inky7.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})
|
target_include_directories(pico_graphics INTERFACE ${CMAKE_CURRENT_LIST_DIR})
|
||||||
|
|
|
@ -26,6 +26,7 @@
|
||||||
namespace pimoroni {
|
namespace pimoroni {
|
||||||
typedef uint8_t RGB332;
|
typedef uint8_t RGB332;
|
||||||
typedef uint16_t RGB565;
|
typedef uint16_t RGB565;
|
||||||
|
typedef uint16_t RGB555;
|
||||||
typedef uint32_t RGB888;
|
typedef uint32_t RGB888;
|
||||||
|
|
||||||
|
|
||||||
|
@ -108,6 +109,14 @@ namespace pimoroni {
|
||||||
return __builtin_bswap16(p);
|
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() {
|
constexpr RGB565 to_rgb332() {
|
||||||
return (r & 0b11100000) | ((g & 0b11100000) >> 3) | ((b & 0b11000000) >> 6);
|
return (r & 0b11100000) | ((g & 0b11100000) >> 3) | ((b & 0b11000000) >> 6);
|
||||||
}
|
}
|
||||||
|
@ -192,7 +201,8 @@ namespace pimoroni {
|
||||||
PEN_RGB332,
|
PEN_RGB332,
|
||||||
PEN_RGB565,
|
PEN_RGB565,
|
||||||
PEN_RGB888,
|
PEN_RGB888,
|
||||||
PEN_INKY7
|
PEN_INKY7,
|
||||||
|
PEN_DV_RGB555
|
||||||
};
|
};
|
||||||
|
|
||||||
void *frame_buffer;
|
void *frame_buffer;
|
||||||
|
@ -579,4 +589,22 @@ namespace pimoroni {
|
||||||
return w * h;
|
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);
|
||||||
|
}
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
Ładowanie…
Reference in New Issue