Skip to content

Commit

Permalink
Merge pull request #17 from PPUC/rgb565
Browse files Browse the repository at this point in the history
-  added RGB565 zone streaming
-  reduced chunk size to 1888 avoid ESP32 serial buffer timing issues
 -  fixed zones resets (caused artifacts)
  • Loading branch information
mkalkbrenner authored Jan 21, 2024
2 parents 31db448 + 52b7558 commit b2190a3
Show file tree
Hide file tree
Showing 5 changed files with 199 additions and 17 deletions.
37 changes: 36 additions & 1 deletion src/ZeDMD.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ ZeDMD::ZeDMD() {
m_pScaledFrameBuffer = nullptr;
m_pCommandBuffer = nullptr;
m_pPlanes = nullptr;
m_pRgb565Buffer = nullptr;

m_pZeDMDComm = new ZeDMDComm();
m_pZeDMDWiFi = new ZeDMDWiFi();
Expand All @@ -41,6 +42,10 @@ ZeDMD::~ZeDMD() {
if (m_pPlanes) {
delete m_pPlanes;
}

if (m_pRgb565Buffer) {
delete m_pRgb565Buffer;
}
}

void ZeDMD::SetLogCallback(ZeDMD_LogCallback callback, const void* userData) {
Expand Down Expand Up @@ -187,6 +192,7 @@ bool ZeDMD::Open() {
m_pCommandBuffer =
(uint8_t*)malloc(ZEDMD_MAX_WIDTH * ZEDMD_MAX_HEIGHT * 3 + 192);
m_pPlanes = (uint8_t*)malloc(ZEDMD_MAX_WIDTH * ZEDMD_MAX_HEIGHT * 3);
m_pRgb565Buffer = (uint16_t*)malloc(ZEDMD_MAX_WIDTH * ZEDMD_MAX_HEIGHT);

m_hd = (m_pZeDMDComm->GetWidth() == 256);

Expand Down Expand Up @@ -262,6 +268,8 @@ void ZeDMD::ClearScreen() {
} else if (m_wifi) {
m_pZeDMDWiFi->QueueCommand(ZEDMD_COMM_COMMAND::ClearScreen);
}
// "Blank" the frame buffer.
memset(m_pFrameBuffer, 0, ZEDMD_MAX_WIDTH * ZEDMD_MAX_HEIGHT * 3);
}

void ZeDMD::RenderGray2(uint8_t* pFrame) {
Expand Down Expand Up @@ -388,7 +396,7 @@ void ZeDMD::RenderRgb24(uint8_t* pFrame) {
if (m_wifi) {
m_pZeDMDWiFi->QueueCommand(ZEDMD_COMM_COMMAND::RGB24ZonesStream, m_pPlanes,
bufferSize, width, height);
} else if (m_hd || m_rgb24Streaming) {
} else if (m_hd || m_rgb24Streaming || m_streaming) {
m_pZeDMDComm->QueueCommand(ZEDMD_COMM_COMMAND::RGB24ZonesStream, m_pPlanes,
bufferSize, width, height);
} else if (m_usb) {
Expand All @@ -397,6 +405,29 @@ void ZeDMD::RenderRgb24(uint8_t* pFrame) {
}
}

void ZeDMD::RenderRgb24EncodedAs565(uint8_t* pFrame) {
if (!m_usb || !UpdateFrameBuffer24(pFrame)) {
return;
}

uint16_t width;
uint16_t height;

int bufferSize = Scale(m_pPlanes, m_pFrameBuffer, 3, &width, &height);
int rgb565BufferSize = bufferSize / 3;
for (uint16_t i = 0; i < rgb565BufferSize; i++) {
m_pRgb565Buffer[i] = (((uint16_t)(m_pPlanes[i * 3] & 0xF8)) << 8) |
(((uint16_t)(m_pPlanes[i * 3 + 1] & 0xFC)) << 3) |
(m_pPlanes[i * 3 + 2] >> 3);
}

if (m_usb) {
m_pZeDMDComm->QueueRgb565Command(ZEDMD_COMM_COMMAND::RGB565ZonesStream,
m_pRgb565Buffer, rgb565BufferSize, width,
height);
}
}

bool ZeDMD::UpdateFrameBuffer8(uint8_t* pFrame) {
if (!memcmp(m_pFrameBuffer, pFrame, m_romWidth * m_romHeight)) {
return false;
Expand Down Expand Up @@ -867,3 +898,7 @@ ZEDMDAPI void ZeDMD_RenderColoredGray6(ZeDMD* pZeDMD, uint8_t* frame,
ZEDMDAPI void ZeDMD_RenderRgb24(ZeDMD* pZeDMD, uint8_t* frame) {
return pZeDMD->RenderRgb24(frame);
}

ZEDMDAPI void ZeDMD_RenderRgb24EncodedAs565(ZeDMD* pZeDMD, uint8_t* frame) {
return pZeDMD->RenderRgb24EncodedAs565(frame);
}
8 changes: 6 additions & 2 deletions src/ZeDMD.h
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
#pragma once

#define ZEDMD_VERSION_MAJOR 0 // X Digits
#define ZEDMD_VERSION_MINOR 4 // Max 2 Digits
#define ZEDMD_VERSION_PATCH 1 // Max 2 Digits
#define ZEDMD_VERSION_MINOR 5 // Max 2 Digits
#define ZEDMD_VERSION_PATCH 0 // Max 2 Digits

#define _ZEDMD_STR(x) #x
#define ZEDMD_STR(x) _ZEDMD_STR(x)
Expand Down Expand Up @@ -78,6 +78,7 @@ class ZEDMDAPI ZeDMD {
void RenderColoredGray6(uint8_t* frame, uint8_t* palette, uint8_t* rotations);
void RenderColoredGray6(uint8_t* frame, uint8_t* rotations);
void RenderRgb24(uint8_t* frame);
void RenderRgb24EncodedAs565(uint8_t* frame);

private:
bool UpdateFrameBuffer8(uint8_t* pFrame);
Expand Down Expand Up @@ -111,6 +112,7 @@ class ZEDMDAPI ZeDMD {
uint8_t* m_pScaledFrameBuffer;
uint8_t* m_pCommandBuffer;
uint8_t* m_pPlanes;
uint16_t* m_pRgb565Buffer;

uint8_t m_palette4[4 * 3] = {0};
uint8_t m_palette16[16 * 3] = {0};
Expand Down Expand Up @@ -166,6 +168,8 @@ extern ZEDMDAPI void ZeDMD_RenderGray4(ZeDMD* pZeDMD, uint8_t* frame);
extern ZEDMDAPI void ZeDMD_RenderColoredGray6(ZeDMD* pZeDMD, uint8_t* frame,
uint8_t* rotations);
extern ZEDMDAPI void ZeDMD_RenderRgb24(ZeDMD* pZeDMD, uint8_t* frame);
extern ZEDMDAPI void ZeDMD_RenderRgb24EncodedAs565(ZeDMD* pZeDMD,
uint8_t* frame);

#ifdef __cplusplus
}
Expand Down
87 changes: 85 additions & 2 deletions src/ZeDMDComm.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,8 @@ void ZeDMDComm::Run() {
int8_t lastStreamId = -1;

while (IsConnected()) {
bool frame_completed = false;

m_frameQueueMutex.lock();

if (m_frames.empty()) {
Expand Down Expand Up @@ -89,12 +91,14 @@ void ZeDMDComm::Run() {
if (frame.streamId != lastStreamId) {
if (lastStreamId != -1) {
m_frameCounter--;
frame_completed = true;
}

lastStreamId = frame.streamId;
}
} else {
m_frameCounter--;
frame_completed = true;
}

m_frameQueueMutex.unlock();
Expand All @@ -112,6 +116,7 @@ void ZeDMDComm::Run() {
}

if (!success) {
// Allow ZeDMD to empty its buffers.
std::this_thread::sleep_for(std::chrono::milliseconds(2));
}
}
Expand Down Expand Up @@ -147,6 +152,8 @@ void ZeDMDComm::QueueCommand(char command, uint8_t* data, int size,
m_delayedFrameReady = true;
m_delayedFrameMutex.unlock();
m_lastStreamId = -1;
// Next streaming needs to be complete.
memset(m_zoneHashes, 0, sizeof(m_zoneHashes));
}
// delayed streamed zones
else if (streamId != -1 && delayed) {
Expand All @@ -162,6 +169,10 @@ void ZeDMDComm::QueueCommand(char command, uint8_t* data, int size,
}
m_frames.push(frame);
m_frameQueueMutex.unlock();
if (streamId == -1) {
// Next streaming needs to be complete.
memset(m_zoneHashes, 0, sizeof(m_zoneHashes));
}
}
}

Expand Down Expand Up @@ -203,7 +214,7 @@ void ZeDMDComm::QueueCommand(char command, uint8_t* data, int size,

m_delayedFrameMutex.unlock();
// A delayed frame needs to be complete.
memset(m_zoneHashes, 0, 128);
memset(m_zoneHashes, 0, sizeof(m_zoneHashes));
}

for (uint16_t y = 0; y < height; y += m_zoneHeight) {
Expand Down Expand Up @@ -241,6 +252,78 @@ void ZeDMDComm::QueueCommand(char command, uint8_t* data, int size,
}
}

void ZeDMDComm::QueueRgb565Command(char command, uint16_t* data, int size,
uint16_t width, uint16_t height) {
uint8_t buffer[256 * 16 * 2 + 16];
uint16_t bufferSize = 0;
uint8_t idx = 0;
uint8_t zone[16 * 8 * 2] = {0};
uint16_t zonesBytesLimit = 0;
if (m_zonesBytesLimit) {
while (zonesBytesLimit < m_zonesBytesLimit) {
zonesBytesLimit += m_zoneWidth * m_zoneHeight * 2 + 1;
}
} else {
zonesBytesLimit = width * m_zoneHeight * 2 + 16;
}

if (++m_streamId > 64) {
m_streamId = 0;
}

bool delayed = false;
if (FillDelayed()) {
delayed = true;
m_delayedFrameMutex.lock();
m_delayedFrameReady = false;
while (m_delayedFrames.size() > 0) {
m_delayedFrames.pop();
}

m_delayedFrameMutex.unlock();
// A delayed frame needs to be complete.
memset(m_zoneHashes, 0, sizeof(m_zoneHashes));
}

for (uint16_t y = 0; y < height; y += m_zoneHeight) {
for (uint16_t x = 0; x < width; x += m_zoneWidth) {
for (uint8_t zy = 0; zy < m_zoneHeight; zy++) {
for (uint8_t zx = 0; zx < m_zoneWidth; zx++) {
zone[(zy * m_zoneWidth + zx) * 2] =
data[((y + zy) * width) + x + zx] >> 8;
zone[(zy * m_zoneWidth + zx) * 2 + 1] =
data[((y + zy) * width) + x + zx] & 0xFF;
}
}

uint64_t hash = komihash(zone, m_zoneWidth * m_zoneHeight * 2, 0);
if (hash != m_zoneHashes[idx]) {
m_zoneHashes[idx] = hash;

buffer[bufferSize++] = idx;
memcpy(&buffer[bufferSize], zone, m_zoneWidth * m_zoneHeight * 2);
bufferSize += m_zoneWidth * m_zoneHeight * 2;

if (bufferSize >= zonesBytesLimit) {
QueueCommand(command, buffer, bufferSize, m_streamId, delayed);
bufferSize = 0;
}
}
idx++;
}
}

if (bufferSize > 0) {
QueueCommand(command, buffer, bufferSize, m_streamId, delayed);
}

if (delayed) {
m_delayedFrameMutex.lock();
m_delayedFrameReady = true;
m_delayedFrameMutex.unlock();
}
}

bool ZeDMDComm::FillDelayed() {
uint8_t count = 0;
bool delayed = false;
Expand Down Expand Up @@ -400,7 +483,7 @@ bool ZeDMDComm::Connect(char* pDevice) {
ZEDMD_COMM_SERIAL_READ_TIMEOUT) &&
data[0] == 'R') {
data[0] = ZEDMD_COMM_COMMAND::Chunk;
data[1] = ZEDMD_COMM_MAX_SERIAL_WRITE_AT_ONCE / 256;
data[1] = ZEDMD_COMM_MAX_SERIAL_WRITE_AT_ONCE / 32;
sp_blocking_write(m_pSerialPort, (void*)CTRL_CHARS_HEADER, 6,
ZEDMD_COMM_SERIAL_WRITE_TIMEOUT);
sp_blocking_write(m_pSerialPort, (void*)data, 2,
Expand Down
7 changes: 5 additions & 2 deletions src/ZeDMDComm.h
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ typedef enum {

RGB24 = 0x03,
RGB24ZonesStream = 0x04,
RGB565ZonesStream = 0x05,
Gray2 = 0x08,
ColGray4 = 0x09,
ColGray6 = 0x0b,
Expand All @@ -69,11 +70,11 @@ struct ZeDMDFrame {
#define ZEDMD_COMM_BAUD_RATE 921600

#if defined(_WIN32) || defined(_WIN64)
#define ZEDMD_COMM_MAX_SERIAL_WRITE_AT_ONCE 8192
#define ZEDMD_COMM_MAX_SERIAL_WRITE_AT_ONCE 1888
#define ZEDMD_COMM_SERIAL_READ_TIMEOUT 16
#define ZEDMD_COMM_SERIAL_WRITE_TIMEOUT 16
#else
#define ZEDMD_COMM_MAX_SERIAL_WRITE_AT_ONCE 4096
#define ZEDMD_COMM_MAX_SERIAL_WRITE_AT_ONCE 1888
#define ZEDMD_COMM_SERIAL_READ_TIMEOUT 32
#define ZEDMD_COMM_SERIAL_WRITE_TIMEOUT 16
#endif
Expand Down Expand Up @@ -107,6 +108,8 @@ class ZeDMDComm {
void Run();
void QueueCommand(char command, uint8_t* buffer, int size, uint16_t width,
uint16_t height);
void QueueRgb565Command(char command, uint16_t* buffer, int size,
uint16_t width, uint16_t height);
void QueueCommand(char command, uint8_t* buffer, int size,
int8_t streamId = -1, bool delayed = false);
void QueueCommand(char command);
Expand Down
Loading

0 comments on commit b2190a3

Please sign in to comment.