Add AESCO SAFLAP flipdot driver

This commit is contained in:
Julian Metzler 2022-10-17 18:04:39 +02:00
parent 9d33ced104
commit e98622bdde
11 changed files with 1692 additions and 3 deletions

View File

@ -1,3 +1,5 @@
Support mapping framebuffer to actual display format (e.g. half-panel layouts)
Add font renderer
Add font support (via filesystem?)
Add font support (via filesystem?)
Check 8bpp vs. 1bpp configs for all display types. (e.g. BROSE flipdot is using 8bpp even though it should use 1bpp)
Add buffer conversions for all combinations (needed e.g. in tpm2net receiver)

View File

@ -0,0 +1,3 @@
idf_component_register(SRCS flipdot_aesco_saflap.c
INCLUDE_DIRS include
REQUIRES util)

View File

@ -0,0 +1,27 @@
menu "AESCO SAFLAP Flipdot Configuration"
config SAFLAP_NUM_PANELS_X
int "Number of panels in X direction"
default 5
help
Total number of flipdot panels connected in X direction
config SAFLAP_NUM_PANELS_Y
int "Number of panels in Y direction"
default 3
help
Total number of flipdot panels connected in Y direction
config SAFLAP_DATA_IO
int "Data GPIO"
default 0
help
GPIO pin for sending data to the display
config SAFLAP_DATA_IO_INVERT
bool "Data GPIO Invert"
default 0
help
Invert data GPIO pin
endmenu

View File

@ -0,0 +1,168 @@
/*
* Functions for AESCO SAFLAP 12x8 pixel flipdot panels
*/
#include "esp_log.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include <string.h>
#include "flipdot_aesco_saflap.h"
#include "util_gpio.h"
#include "macros.h"
#if defined(CONFIG_DISPLAY_DRIVER_FLIPDOT_SAFLAP)
#define LOG_TAG "FLIPDOT-AESCO-SAFLAP"
static uint8_t display_dirty = 1;
void display_init() {
/*
* Set up all needed peripherals
*/
gpio_reset_pin(CONFIG_SAFLAP_DATA_IO);
gpio_set_direction(CONFIG_SAFLAP_DATA_IO, GPIO_MODE_OUTPUT);
gpio_set(CONFIG_SAFLAP_DATA_IO, 0, CONFIG_SAFLAP_DATA_IO_INVERT);
display_reset();
}
void display_shiftBit(uint8_t bit) {
/*
* Shift out a single bit.
* 1 means 20µs delay, 0 means 5µs,
* followed by a 30µs delay.
* Thus, the maximum data rate for all ones is 20 kbit/s.
*/
gpio_pulse_inv(CONFIG_SAFLAP_DATA_IO, 1, bit ? 20 : 5, 30, CONFIG_SAFLAP_DATA_IO_INVERT);
}
void display_shiftByte(uint8_t byte) {
/*
* Shift out a full byte.
*/
//ESP_LOGD(LOG_TAG, "Shift byte 0x%02x", byte);
for (uint8_t i = 0; i < 8; i++) {
display_shiftBit((byte >> (7 - i)) & 1);
}
}
// void display_setNibble(uint8_t panel, uint8_t row, uint8_t nibbleIdx, uint8_t nibbleVal) {
// /*
// * Set the specified nibble on the specified panel.
// */
// uint8_t colByte = 0x00;
// colByte |= (1 << ((nibbleVal & (1 << 0)) ? 7 : 6));
// colByte |= (1 << ((nibbleVal & (1 << 1)) ? 5 : 4));
// colByte |= (1 << ((nibbleVal & (1 << 2)) ? 3 : 2));
// colByte |= (1 << ((nibbleVal & (1 << 3)) ? 1 : 0));
// display_shiftBit(1); // Start Bit
// display_shiftByte(~panel); // Panel address
// display_shiftByte(row << 5); // Row address
// display_shiftByte(nibbleIdx == 0 ? colByte : 0x00); // Column Byte 1
// display_shiftByte(nibbleIdx == 1 ? colByte : 0x00); // Column Byte 2
// display_shiftByte(nibbleIdx == 2 ? colByte : 0x00); // Column Byte 3
// vTaskDelay(30 / portTICK_PERIOD_MS);
// }
// void display_setRow(uint8_t panel, uint8_t row, uint16_t cols) {
// /*
// * Set the specified row on the specified panel, split into 3 nibbles.
// */
// display_setNibble(panel, row, 0, (cols >> 8) & 0x0F);
// display_setNibble(panel, row, 1, (cols >> 4) & 0x0F);
// display_setNibble(panel, row, 2, cols & 0x0F);
// }
void display_reset() {
/*
* Reset the controller registers to get rid of any rogue bits
*/
for (uint8_t i = 0; i < 20; i++) {
display_shiftByte(0x00);
}
vTaskDelay(40 / portTICK_PERIOD_MS);
}
void display_setRowSingle(uint8_t panel, uint8_t row, uint16_t cols) {
/*
* Set the specified row on the specified panel, all at once.
*/
uint8_t colByte1 = 0x00;
colByte1 |= (1 << ((cols & (1 << 8)) ? 7 : 6));
colByte1 |= (1 << ((cols & (1 << 9)) ? 5 : 4));
colByte1 |= (1 << ((cols & (1 << 10)) ? 3 : 2));
colByte1 |= (1 << ((cols & (1 << 11)) ? 1 : 0));
uint8_t colByte2 = 0x00;
colByte2 |= (1 << ((cols & (1 << 4)) ? 7 : 6));
colByte2 |= (1 << ((cols & (1 << 5)) ? 5 : 4));
colByte2 |= (1 << ((cols & (1 << 6)) ? 3 : 2));
colByte2 |= (1 << ((cols & (1 << 7)) ? 1 : 0));
uint8_t colByte3 = 0x00;
colByte3 |= (1 << ((cols & (1 << 0)) ? 7 : 6));
colByte3 |= (1 << ((cols & (1 << 1)) ? 5 : 4));
colByte3 |= (1 << ((cols & (1 << 2)) ? 3 : 2));
colByte3 |= (1 << ((cols & (1 << 3)) ? 1 : 0));
display_shiftBit(1); // Start Bit
display_shiftByte(~panel); // Panel address
display_shiftByte(row << 5); // Row address
display_shiftByte(colByte1); // Column Byte 1
display_shiftByte(colByte2); // Column Byte 2
display_shiftByte(colByte3); // Column Byte 3
vTaskDelay(40 / portTICK_PERIOD_MS); // Wait for the refresh cycle to complete
}
void display_render_frame_1bpp(uint8_t* frame, uint8_t* prevFrame, uint16_t frameBufSize) {
if(!display_dirty && prevFrame) {
for (uint16_t x_base = 0; x_base < DISPLAY_FRAME_WIDTH; x_base += SAFLAP_PANEL_WIDTH) {
for (uint16_t y = 0; y < DISPLAY_FRAME_HEIGHT; y++) {
uint8_t panelAddr = 1 + (x_base / SAFLAP_PANEL_WIDTH) * CONFIG_SAFLAP_NUM_PANELS_Y + (y / SAFLAP_PANEL_HEIGHT);
uint8_t panelRow = y % SAFLAP_PANEL_HEIGHT;
uint16_t colData = 0x000;
uint16_t prevColData = 0x000;
uint16_t x = 0;
for (uint16_t x_offset = 0; x_offset < SAFLAP_PANEL_WIDTH; x_offset++) {
x = x_base + x_offset;
colData |= ((uint16_t)BUFFER_VAL(frame, x, y)) << (SAFLAP_PANEL_WIDTH - x_offset - 1);
prevColData |= ((uint16_t)BUFFER_VAL(prevFrame, x, y)) << (SAFLAP_PANEL_WIDTH - x_offset - 1);
}
if (colData != prevColData) {
ESP_LOGD(LOG_TAG, "P%u R%u = %#03x", panelAddr, panelRow, colData);
display_setRowSingle(panelAddr, panelRow, colData);
}
}
}
memcpy(prevFrame, frame, frameBufSize);
} else {
for (uint16_t x_base = 0; x_base < DISPLAY_FRAME_WIDTH; x_base += SAFLAP_PANEL_WIDTH) {
for (uint16_t y = 0; y < DISPLAY_FRAME_HEIGHT; y++) {
uint8_t panelAddr = 1 + (x_base / SAFLAP_PANEL_WIDTH) * CONFIG_SAFLAP_NUM_PANELS_Y + (y / SAFLAP_PANEL_HEIGHT);
uint8_t panelRow = y % SAFLAP_PANEL_HEIGHT;
uint16_t colData = 0x000;
uint16_t x = 0;
for (uint16_t x_offset = 0; x_offset < SAFLAP_PANEL_WIDTH; x_offset++) {
x = x_base + x_offset;
colData |= ((uint16_t)BUFFER_VAL(frame, x, y)) << (SAFLAP_PANEL_WIDTH - x_offset - 1);
}
ESP_LOGD(LOG_TAG, "P%u R%u = 0x%03x", panelAddr, panelRow, colData);
display_setRowSingle(panelAddr, panelRow, colData);
}
}
if(display_dirty && prevFrame) memcpy(prevFrame, frame, frameBufSize);
display_dirty = 0;
}
}
#endif

View File

@ -0,0 +1,19 @@
#pragma once
#include "esp_system.h"
#define SAFLAP_PANEL_WIDTH 12
#define SAFLAP_PANEL_HEIGHT 8
#ifndef CONFIG_SAFLAP_DATA_IO_INVERT
#define CONFIG_SAFLAP_DATA_IO_INVERT 0
#endif
void display_init();
void display_shiftBit(uint8_t bit);
void display_shiftByte(uint8_t byte);
void display_setNibble(uint8_t panel, uint8_t row, uint8_t nibbleIdx, uint8_t nibbleVal);
void display_reset();
void display_setRow(uint8_t panel, uint8_t row, uint16_t cols);
void display_setRowSingle(uint8_t panel, uint8_t row, uint16_t cols);
void display_render_frame_1bpp(uint8_t* frame, uint8_t* prevFrame, uint16_t frameBufSize);

View File

@ -0,0 +1,6 @@
#pragma once
#include <stdint.h>
void display_init();
void display_render_frame_1bpp(uint8_t* frame, uint8_t* prevFrame, uint16_t frameBufSize);

View File

@ -21,12 +21,16 @@
#if defined(CONFIG_DISPLAY_FRAME_TYPE_1BPP)
#define DISPLAY_FRAME_TYPE "1bpp"
#define DISPLAY_FRAMEBUF_SIZE DISPLAY_FRAMEBUF_SIZE_1BPP
#define DISPLAY_FRAME_HEIGHT_BYTES DIV_CEIL(CONFIG_DISPLAY_FRAME_HEIGHT, 8)
#define BUFFER_VAL(buf, x, y) ((buf[(x * DISPLAY_FRAME_HEIGHT_BYTES) + (y / 8)] >> (y % 8)) & 1)
#elif defined(CONFIG_DISPLAY_FRAME_TYPE_8BPP)
#define DISPLAY_FRAME_TYPE "8bpp"
#define DISPLAY_FRAMEBUF_SIZE DISPLAY_FRAMEBUF_SIZE_8BPP
#define DISPLAY_FRAME_HEIGHT_BYTES CONFIG_DISPLAY_FRAME_HEIGHT
#elif defined(CONFIG_DISPLAY_FRAME_TYPE_24BPP)
#define DISPLAY_FRAME_TYPE "24bpp"
#define DISPLAY_FRAMEBUF_SIZE DISPLAY_FRAMEBUF_SIZE_24BPP
#define DISPLAY_FRAME_HEIGHT_BYTES (CONFIG_DISPLAY_FRAME_HEIGHT * 3)
#endif
#elif defined(CONFIG_DISPLAY_TYPE_CHARACTER)
#define DISPLAY_TYPE "character"
@ -39,6 +43,8 @@
#define DISPLAY_DRIVER "flipdot_lawo_aluma"
#elif defined(CONFIG_DISPLAY_DRIVER_FLIPDOT_BROSE)
#define DISPLAY_DRIVER "flipdot_brose"
#elif defined(CONFIG_DISPLAY_DRIVER_FLIPDOT_SAFLAP)
#define DISPLAY_DRIVER "flipdot_saflap"
#elif defined(CONFIG_DISPLAY_DRIVER_LED_SHIFT_REGISTER)
#define DISPLAY_DRIVER "led_shift_register"
#elif defined(CONFIG_DISPLAY_DRIVER_LED_SHIFT_REGISTER_I2S)

View File

@ -13,7 +13,7 @@ platform = espressif32@3.3.2
board = esp-wrover-kit
framework = espidf
monitor_speed = 115200
upload_port = COM3
upload_port = COM7
board_build.partitions = partition_table.csv
board_build.embed_txtfiles =
src/static/favicon.ico
@ -28,4 +28,6 @@ build_flags =
[env:oilflip]
[env:16segled]
[env:16segled]
[env:saflap]

1450
sdkconfig.saflap Normal file

File diff suppressed because it is too large Load Diff

View File

@ -136,6 +136,10 @@ choice DISPLAY_DRIVER
depends on DISPLAY_TYPE_PIXEL
bool "BROSE flipdot"
config DISPLAY_DRIVER_FLIPDOT_SAFLAP
depends on DISPLAY_TYPE_PIXEL
bool "AESCO SAFLAP flipdot"
config DISPLAY_DRIVER_LED_SHIFT_REGISTER
depends on DISPLAY_TYPE_PIXEL
bool "Generic shift-register based LED display"

View File

@ -24,6 +24,8 @@
#include "driver_display_flipdot_lawo_aluma.h"
#elif defined(CONFIG_DISPLAY_DRIVER_FLIPDOT_BROSE)
#include "driver_display_flipdot_brose.h"
#elif defined(CONFIG_DISPLAY_DRIVER_FLIPDOT_SAFLAP)
#include "driver_display_flipdot_aesco_saflap.h"
#elif defined(CONFIG_DISPLAY_DRIVER_LED_SHIFT_REGISTER) || defined(CONFIG_DISPLAY_DRIVER_LED_SHIFT_REGISTER_I2S)
#include "driver_display_led_shift_register.h"
#elif defined(CONFIG_DISPLAY_DRIVER_LED_AESYS_I2S)