Add AESCO SAFLAP flipdot driver
This commit is contained in:
parent
9d33ced104
commit
e98622bdde
4
TODO.txt
4
TODO.txt
|
@ -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)
|
|
@ -0,0 +1,3 @@
|
|||
idf_component_register(SRCS flipdot_aesco_saflap.c
|
||||
INCLUDE_DIRS include
|
||||
REQUIRES util)
|
|
@ -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
|
|
@ -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
|
|
@ -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);
|
|
@ -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);
|
|
@ -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)
|
||||
|
|
|
@ -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]
|
File diff suppressed because it is too large
Load Diff
|
@ -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"
|
||||
|
|
|
@ -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)
|
||||
|
|
Loading…
Reference in New Issue