mirror of
https://github.com/sle118/squeezelite-esp32.git
synced 2025-12-06 11:36:59 +03:00
adding SSD1675 driver
This commit is contained in:
@@ -42,12 +42,12 @@ static void Update( struct GDS_Device* Device ) {
|
||||
#ifdef SHADOW_BUFFER
|
||||
struct PrivateSpace *Private = (struct PrivateSpace*) Device->Private;
|
||||
// not sure the compiler does not have to redo all calculation in for loops, so local it is
|
||||
int width = Device->Width, rows = Device->Height / 8;
|
||||
int width = Device->Width, pages = Device->Height / 8;
|
||||
uint8_t *optr = Private->Shadowbuffer, *iptr = Device->Framebuffer;
|
||||
int CurrentRow = -1, FirstCol = -1, LastCol = -1;
|
||||
int CurrentPage = -1, FirstCol = -1, LastCol = -1;
|
||||
|
||||
// by row, find first and last columns that have been updated
|
||||
for (int r = 0; r < rows; r++) {
|
||||
for (int p = 0; p < pages; p++) {
|
||||
uint8_t first = 0, last;
|
||||
for (int c = 0; c < width; c++) {
|
||||
if (*iptr != *optr) {
|
||||
@@ -70,15 +70,15 @@ static void Update( struct GDS_Device* Device ) {
|
||||
}
|
||||
|
||||
// Set row only when needed, otherwise let auto-increment work
|
||||
if (r != CurrentRow) SetPageAddress( Device, r, Device->Height / 8 - 1 );
|
||||
CurrentRow = r + 1;
|
||||
if (p != CurrentPage) SetPageAddress( Device, p, Device->Height / 8 - 1 );
|
||||
CurrentPage = p + 1;
|
||||
|
||||
// actual write
|
||||
Device->WriteData( Device, Private->Shadowbuffer + r*width + first, last - first + 1);
|
||||
Device->WriteData( Device, Private->Shadowbuffer + p*width + first, last - first + 1);
|
||||
}
|
||||
}
|
||||
#else
|
||||
// automatic counter and end Page/Column
|
||||
// automatic counter and end Page/Column (we assume Height % 8 == 0)
|
||||
SetColumnAddress( Device, 0, Device->Width - 1);
|
||||
SetPageAddress( Device, 0, Device->Height / 8 - 1);
|
||||
Device->WriteData( Device, Device->Framebuffer, Device->FramebufferSize );
|
||||
|
||||
@@ -162,7 +162,7 @@ static void IRAM_ATTR DrawPixel1Fast( struct GDS_Device* Device, int X, int Y, i
|
||||
*FBOffset ^= BIT( 7 - XBit );
|
||||
} else {
|
||||
// we might be able to save the 7-Xbit using BitRemap (A0 bit 2)
|
||||
*FBOffset = ( Color == GDS_COLOR_WHITE ) ? *FBOffset | BIT( 7 - XBit ) : *FBOffset & ~BIT( 7 - XBit );
|
||||
*FBOffset = ( Color == GDS_COLOR_BLACK ) ? *FBOffset & ~BIT( 7 - XBit ) : *FBOffset | BIT( 7 - XBit );
|
||||
}
|
||||
}
|
||||
|
||||
@@ -181,8 +181,6 @@ static void ClearWindow( struct GDS_Device* Device, int x1, int y1, int x2, int
|
||||
c += chunk * 8;
|
||||
while (c <= x2) DrawPixel1Fast( Device, c++, r, Color );
|
||||
}
|
||||
|
||||
Device->Dirty = true;
|
||||
}
|
||||
|
||||
static void DrawBitmapCBR(struct GDS_Device* Device, uint8_t *Data, int Width, int Height, int Color ) {
|
||||
@@ -193,8 +191,6 @@ static void DrawBitmapCBR(struct GDS_Device* Device, uint8_t *Data, int Width, i
|
||||
|
||||
// just do bitreverse and if BitRemap works, there will be even nothing to do
|
||||
for (int i = Height * Width >> 3; --i >= 0;) *optr++ = BitReverseTable256[*Data++];
|
||||
|
||||
// Dirty is set for us
|
||||
}
|
||||
|
||||
static void SetHFlip( struct GDS_Device* Device, bool On ) {
|
||||
|
||||
252
components/display/SSD1675.c
Normal file
252
components/display/SSD1675.c
Normal file
@@ -0,0 +1,252 @@
|
||||
/**
|
||||
* Copyright (c) 2017-2018 Tara Keeling
|
||||
* 2020 Philippe G.
|
||||
*
|
||||
* This software is released under the MIT License.
|
||||
* https://opensource.org/licenses/MIT
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include "driver/gpio.h"
|
||||
#include <esp_log.h>
|
||||
|
||||
#include "gds.h"
|
||||
#include "gds_private.h"
|
||||
|
||||
static char TAG[] = "SSD1675";
|
||||
|
||||
const unsigned char EPD_lut_full_update[] = {
|
||||
0x80,0x60,0x40,0x00,0x00,0x00,0x00, //LUT0: BB: VS 0 ~7
|
||||
0x10,0x60,0x20,0x00,0x00,0x00,0x00, //LUT1: BW: VS 0 ~7
|
||||
0x80,0x60,0x40,0x00,0x00,0x00,0x00, //LUT2: WB: VS 0 ~7
|
||||
0x10,0x60,0x20,0x00,0x00,0x00,0x00, //LUT3: WW: VS 0 ~7
|
||||
0x00,0x00,0x00,0x00,0x00,0x00,0x00, //LUT4: VCOM: VS 0 ~7
|
||||
0x03,0x03,0x00,0x00,0x02, // TP0 A~D RP0
|
||||
0x09,0x09,0x00,0x00,0x02, // TP1 A~D RP1
|
||||
0x03,0x03,0x00,0x00,0x02, // TP2 A~D RP2
|
||||
0x00,0x00,0x00,0x00,0x00, // TP3 A~D RP3
|
||||
0x00,0x00,0x00,0x00,0x00, // TP4 A~D RP4
|
||||
0x00,0x00,0x00,0x00,0x00, // TP5 A~D RP5
|
||||
0x00,0x00,0x00,0x00,0x00, // TP6 A~D RP6
|
||||
0x15,0x41,0xA8,0x32,0x30,0x0A,
|
||||
};
|
||||
|
||||
const unsigned char EPD_lut_partial_update[]= { //20 bytes
|
||||
0x00,0x00,0x00,0x00,0x00,0x00,0x00, //LUT0: BB: VS 0 ~7
|
||||
0x80,0x00,0x00,0x00,0x00,0x00,0x00, //LUT1: BW: VS 0 ~7
|
||||
0x40,0x00,0x00,0x00,0x00,0x00,0x00, //LUT2: WB: VS 0 ~7
|
||||
0x00,0x00,0x00,0x00,0x00,0x00,0x00, //LUT3: WW: VS 0 ~7
|
||||
0x00,0x00,0x00,0x00,0x00,0x00,0x00, //LUT4: VCOM: VS 0 ~7
|
||||
0x0A,0x00,0x00,0x00,0x00, // TP0 A~D RP0
|
||||
0x00,0x00,0x00,0x00,0x00, // TP1 A~D RP1
|
||||
0x00,0x00,0x00,0x00,0x00, // TP2 A~D RP2
|
||||
0x00,0x00,0x00,0x00,0x00, // TP3 A~D RP3
|
||||
0x00,0x00,0x00,0x00,0x00, // TP4 A~D RP4
|
||||
0x00,0x00,0x00,0x00,0x00, // TP5 A~D RP5
|
||||
0x00,0x00,0x00,0x00,0x00, // TP6 A~D RP6
|
||||
0x15,0x41,0xA8,0x32,0x30,0x0A,
|
||||
};
|
||||
|
||||
struct PrivateSpace {
|
||||
int ReadyPin;
|
||||
uint16_t Height;
|
||||
};
|
||||
|
||||
// Functions are not declared to minimize # of lines
|
||||
|
||||
void WaitReady( struct GDS_Device* Device) {
|
||||
struct PrivateSpace *Private = (struct PrivateSpace*) Device->Private;
|
||||
if (Private->ReadyPin >= 0) {
|
||||
int count = 4*1000;
|
||||
while (gpio_get_level( Private->ReadyPin ) && count) {
|
||||
vTaskDelay( pdMS_TO_TICKS(100) );
|
||||
count -= 100;
|
||||
}
|
||||
} else {
|
||||
vTaskDelay( pdMS_TO_TICKS(2000) );
|
||||
}
|
||||
}
|
||||
|
||||
static void WriteByte( struct GDS_Device* Device, uint8_t Data ) {
|
||||
Device->WriteData( Device, &Data, 1 );
|
||||
}
|
||||
|
||||
static void SetColumnAddress( struct GDS_Device* Device, uint8_t Start, uint8_t End ) {
|
||||
// start might be greater than end if we decrement
|
||||
Device->WriteCommand( Device, 0x44 );
|
||||
Device->WriteData( Device, &Start, 1 );
|
||||
Device->WriteData( Device, &End, 1 );
|
||||
|
||||
// we obviously want to start ... from the start
|
||||
Device->WriteCommand( Device, 0x4e );
|
||||
WriteByte( Device, Start );
|
||||
}
|
||||
static void SetRowAddress( struct GDS_Device* Device, uint16_t Start, uint16_t End ) {
|
||||
// start might be greater than end if we decrement
|
||||
Device->WriteCommand( Device, 0x45 );
|
||||
WriteByte( Device, Start );
|
||||
WriteByte( Device, Start >> 8 );
|
||||
WriteByte( Device, End );
|
||||
WriteByte( Device, End >> 8 );
|
||||
|
||||
// we obviously want to start ... from the start
|
||||
Device->WriteCommand( Device, 0x4f );
|
||||
WriteByte( Device, Start );
|
||||
WriteByte( Device, Start >> 8 );
|
||||
}
|
||||
|
||||
static void Update( struct GDS_Device* Device ) {
|
||||
uint8_t *iptr = Device->Framebuffer;
|
||||
|
||||
Device->WriteCommand( Device, 0x24 );
|
||||
|
||||
// this is awfully slow, but e-ink are slow anyway ...
|
||||
for (int i = Device->FramebufferSize; --i >= 0;) {
|
||||
WriteByte( Device, ~*iptr++ );
|
||||
}
|
||||
|
||||
Device->WriteCommand( Device, 0x22 );
|
||||
WriteByte( Device, 0xC7);
|
||||
Device->WriteCommand( Device, 0X20 );
|
||||
|
||||
WaitReady( Device );
|
||||
}
|
||||
|
||||
// remember that for these ELD drivers W and H are "inverted"
|
||||
static void IRAM_ATTR DrawPixelFast( struct GDS_Device* Device, int X, int Y, int Color ) {
|
||||
uint32_t YBit = ( Y & 0x07 );
|
||||
Y>>= 3;
|
||||
|
||||
uint8_t* FBOffset = Device->Framebuffer + ( ( Y * Device->Width ) + X );
|
||||
*FBOffset = ( Color == GDS_COLOR_BLACK ) ? *FBOffset & ~BIT( 7-YBit ) : *FBOffset | BIT( 7-YBit );
|
||||
}
|
||||
|
||||
static void ClearWindow( struct GDS_Device* Device, int x1, int y1, int x2, int y2, int Color ) {
|
||||
for (int r = y1; r <= y2; r++) {
|
||||
for (int c = x1; c <= x2; c++) {
|
||||
DrawPixelFast( Device, c, r, Color );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void DrawBitmapCBR(struct GDS_Device* Device, uint8_t *Data, int Width, int Height, int Color ) {
|
||||
if (!Height) Height = Device->Height;
|
||||
if (!Width) Width = Device->Width;
|
||||
|
||||
// just do row/column swap
|
||||
for (int r = 0; r < Height; r++) {
|
||||
uint8_t *optr = Device->Framebuffer + r*Device->Width, *iptr = Data + r;
|
||||
for (int c = Width; --c >= 0;) {
|
||||
*optr++ = *iptr;
|
||||
iptr += Height;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static bool Init( struct GDS_Device* Device ) {
|
||||
struct PrivateSpace *Private = (struct PrivateSpace*) Device->Private;
|
||||
|
||||
// need to re-adjust framebuffer because these are non % 8
|
||||
Private->Height = Device->Height;
|
||||
if (Device->Height & 0x07) Device->Height = ((Device->Height >> 3) + 1) << 3;
|
||||
|
||||
Device->FramebufferSize = Device->Width * Device->Height / 8;
|
||||
Device->Framebuffer = calloc(1, Device->FramebufferSize);
|
||||
NullCheck( Device->Framebuffer, return false );
|
||||
|
||||
if (Private->ReadyPin >= 0) {
|
||||
gpio_pad_select_gpio( Private->ReadyPin );
|
||||
gpio_set_pull_mode( Private->ReadyPin, GPIO_PULLUP_ONLY);
|
||||
gpio_set_direction( Private->ReadyPin, GPIO_MODE_INPUT );
|
||||
}
|
||||
|
||||
// soft reset
|
||||
vTaskDelay(pdMS_TO_TICKS( 2000 ));
|
||||
Device->WriteCommand( Device, 0x12 );
|
||||
WaitReady( Device );
|
||||
|
||||
Device->WriteCommand( Device, 0x74 );
|
||||
WriteByte( Device, 0x54 );
|
||||
Device->WriteCommand( Device, 0x7e );
|
||||
WriteByte( Device, 0x3B );
|
||||
|
||||
Device->WriteCommand( Device, 0x3c );
|
||||
WriteByte( Device, 0x03 );
|
||||
|
||||
Device->WriteCommand( Device, 0x2c );
|
||||
WriteByte( Device, 0x55 );
|
||||
|
||||
Device->WriteCommand( Device, 0x03 );
|
||||
WriteByte( Device, EPD_lut_full_update[70] );
|
||||
|
||||
Device->WriteCommand( Device, 0x04 );
|
||||
WriteByte( Device, EPD_lut_full_update[71] );
|
||||
WriteByte( Device, EPD_lut_full_update[72] );
|
||||
WriteByte( Device, EPD_lut_full_update[73] );
|
||||
|
||||
Device->WriteCommand( Device, 0x3a );
|
||||
WriteByte( Device, EPD_lut_full_update[74] );
|
||||
Device->WriteCommand( Device, 0x3b );
|
||||
WriteByte( Device, EPD_lut_full_update[75] );
|
||||
|
||||
Device->WriteCommand( Device, 0X32 );
|
||||
for (int i = 0; i < 70; i++) {
|
||||
WriteByte( Device, EPD_lut_full_update[i] );
|
||||
}
|
||||
|
||||
// now deal with funny X/Y layout (W and H are "inverted")
|
||||
Device->WriteCommand( Device, 0x01 );
|
||||
WriteByte( Device, Device->Width - 1 );
|
||||
WriteByte( Device, (Device->Width - 1) >> 8 );
|
||||
WriteByte( Device, (0 << 0) );
|
||||
|
||||
/*
|
||||
Start from 0, Ymax, incX, decY. Starting from X=Height would be difficult
|
||||
as we would hit the extra bits added because height % 8 != 0 and they are
|
||||
not written by the DrawPixel. By starting from X=0 we are aligned and
|
||||
doing incY is like a clockwise 90° rotation (vs otherwise we would virtually
|
||||
do a counter-clockwise rotation but again have the "border" issue.
|
||||
*/
|
||||
Device->WriteCommand( Device, 0x11 );
|
||||
WriteByte( Device, (1 << 2) | (0 << 1) | (1 << 0));
|
||||
|
||||
// must be in order with increment/decrement i.e start might be > end if we decrement
|
||||
SetColumnAddress( Device, 0x0, (Device->Height >> 3) - 1 );
|
||||
SetRowAddress ( Device, Device->Width - 1, 0 );
|
||||
|
||||
WaitReady( Device );
|
||||
|
||||
Update( Device );
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static const struct GDS_Device SSD1675 = {
|
||||
.DrawBitmapCBR = DrawBitmapCBR, .ClearWindow = ClearWindow,
|
||||
.DrawPixelFast = DrawPixelFast,
|
||||
.Update = Update, .Init = Init,
|
||||
};
|
||||
|
||||
struct GDS_Device* SSD1675_Detect(char *Driver, struct GDS_Device* Device) {
|
||||
if (!strcasestr(Driver, "SSD1675")) return NULL;
|
||||
|
||||
if (!Device) Device = calloc(1, sizeof(struct GDS_Device));
|
||||
*Device = SSD1675;
|
||||
|
||||
Device->Depth = 1;
|
||||
Device->Alloc = GDS_ALLOC_NONE;
|
||||
|
||||
char *p;
|
||||
struct PrivateSpace* Private = (struct PrivateSpace*) Device->Private;
|
||||
Private->ReadyPin = -1;
|
||||
if ((p = strcasestr(Driver, "ready")) != NULL) Private->ReadyPin = atoi(strchr(p, '=') + 1);
|
||||
|
||||
ESP_LOGI(TAG, "SSD1675 driver with ready GPIO %d", Private->ReadyPin);
|
||||
|
||||
return Device;
|
||||
}
|
||||
@@ -133,11 +133,18 @@ bool GDS_Reset( struct GDS_Device* Device ) {
|
||||
}
|
||||
|
||||
bool GDS_Init( struct GDS_Device* Device ) {
|
||||
Device->FramebufferSize = ( Device->Width * Device->Height ) / (8 / Device->Depth);
|
||||
|
||||
if ((Device->Alloc && GDS_ALLOC_IRAM) || ((Device->Alloc & GDS_ALLOC_IRAM_SPI) && Device->IF == GDS_IF_SPI)) heap_caps_calloc( 1, Device->FramebufferSize, MALLOC_CAP_INTERNAL | MALLOC_CAP_DMA );
|
||||
else Device->Framebuffer = calloc( 1, Device->FramebufferSize );
|
||||
NullCheck( Device->Framebuffer, return false );
|
||||
Device->FramebufferSize = (Device->Width * Device->Height) / (8 / Device->Depth);
|
||||
|
||||
// allocate FB unless explicitely asked not to
|
||||
if (!(Device->Alloc & GDS_ALLOC_NONE)) {
|
||||
if ((Device->Alloc & GDS_ALLOC_IRAM) || ((Device->Alloc & GDS_ALLOC_IRAM_SPI) && Device->IF == GDS_IF_SPI)) {
|
||||
heap_caps_calloc( 1, Device->FramebufferSize, MALLOC_CAP_INTERNAL | MALLOC_CAP_DMA );
|
||||
} else {
|
||||
Device->Framebuffer = calloc( 1, Device->FramebufferSize );
|
||||
}
|
||||
NullCheck( Device->Framebuffer, return false );
|
||||
}
|
||||
|
||||
bool Res = Device->Init( Device );
|
||||
if (!Res) free(Device->Framebuffer);
|
||||
|
||||
@@ -13,13 +13,13 @@
|
||||
monochrome mode is not such type of screen, SH1106 and SSD1306 are
|
||||
*/
|
||||
|
||||
enum { GDS_COLOR_L0 = 0, GDS_COLOR_L1 = 1, GDS_COLOR_L2, GDS_COLOR_L3, GDS_COLOR_L4, GDS_COLOR_L5, GDS_COLOR_L6, GDS_COLOR_L7,
|
||||
enum { GDS_COLOR_L0 = 0, GDS_COLOR_L1, GDS_COLOR_L2, GDS_COLOR_L3, GDS_COLOR_L4, GDS_COLOR_L5, GDS_COLOR_L6, GDS_COLOR_L7,
|
||||
GDS_COLOR_L8, GDS_COLOR_L9, GDS_COLOR_L10, GDS_COLOR_L11, GDS_COLOR_L12, GDS_COLOR_L13, GDS_COLOR_L14, GDS_COLOR_L15,
|
||||
GDS_COLOR_MAX
|
||||
};
|
||||
|
||||
#define GDS_COLOR_BLACK GDS_COLOR_L0
|
||||
#define GDS_COLOR_WHITE (GDS_COLOR_MAX - 1)
|
||||
#define GDS_COLOR_BLACK (0)
|
||||
#define GDS_COLOR_WHITE (-1)
|
||||
#define GDS_COLOR_XOR (GDS_COLOR_MAX + 1)
|
||||
|
||||
struct GDS_Device;
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
#include "gds.h"
|
||||
#include "gds_err.h"
|
||||
|
||||
#define GDS_ALLOC_NONE 0x80
|
||||
#define GDS_ALLOC_IRAM 0x01
|
||||
#define GDS_ALLOC_IRAM_SPI 0x02
|
||||
|
||||
@@ -158,7 +159,7 @@ inline void IRAM_ATTR GDS_DrawPixel1Fast( struct GDS_Device* Device, int X, int
|
||||
if ( Color == GDS_COLOR_XOR ) {
|
||||
*FBOffset ^= BIT( YBit );
|
||||
} else {
|
||||
*FBOffset = ( Color >= GDS_COLOR_WHITE / 2 ) ? *FBOffset | BIT( YBit ) : *FBOffset & ~BIT( YBit );
|
||||
*FBOffset = ( Color == GDS_COLOR_BLACK ) ? *FBOffset & ~BIT( YBit ) : *FBOffset | BIT( YBit );
|
||||
}
|
||||
}
|
||||
|
||||
@@ -166,7 +167,7 @@ inline void IRAM_ATTR GDS_DrawPixel4Fast( struct GDS_Device* Device, int X, int
|
||||
uint8_t* FBOffset;
|
||||
|
||||
FBOffset = Device->Framebuffer + ( (Y * Device->Width >> 1) + (X >> 1));
|
||||
*FBOffset = X & 0x01 ? (*FBOffset & 0x0f) | (Color << 4) : ((*FBOffset & 0xf0) | Color);
|
||||
*FBOffset = X & 0x01 ? (*FBOffset & 0x0f) | ((Color & 0x0f) << 4) : ((*FBOffset & 0xf0) | (Color & 0x0f));
|
||||
}
|
||||
|
||||
inline void IRAM_ATTR GDS_DrawPixelFast( struct GDS_Device* Device, int X, int Y, int Color ) {
|
||||
|
||||
@@ -59,8 +59,8 @@ static EXT_RAM_ATTR struct {
|
||||
static void displayer_task(void *args);
|
||||
|
||||
struct GDS_Device *display;
|
||||
extern GDS_DetectFunc SSD1306_Detect, SSD132x_Detect, SH1106_Detect;
|
||||
GDS_DetectFunc *drivers[] = { SH1106_Detect, SSD1306_Detect, SSD132x_Detect, NULL };
|
||||
extern GDS_DetectFunc SSD1306_Detect, SSD132x_Detect, SH1106_Detect, SSD1675_Detect;
|
||||
GDS_DetectFunc *drivers[] = { SH1106_Detect, SSD1306_Detect, SSD132x_Detect, SSD1675_Detect, NULL };
|
||||
|
||||
/****************************************************************************************
|
||||
*
|
||||
@@ -86,6 +86,9 @@ void display_init(char *welcome) {
|
||||
|
||||
// so far so good
|
||||
if (display && width > 0 && height > 0) {
|
||||
int RST_pin = -1;
|
||||
if ((p = strcasestr(config, "reset")) != NULL) RST_pin = atoi(strchr(p, '=') + 1);
|
||||
|
||||
// Detect driver interface
|
||||
if (strstr(config, "I2C") && i2c_system_port != -1) {
|
||||
int address = 0x3C;
|
||||
@@ -94,7 +97,7 @@ void display_init(char *welcome) {
|
||||
|
||||
init = true;
|
||||
GDS_I2CInit( i2c_system_port, -1, -1, i2c_system_speed ) ;
|
||||
GDS_I2CAttachDevice( display, width, height, address, -1 );
|
||||
GDS_I2CAttachDevice( display, width, height, address, RST_pin );
|
||||
|
||||
ESP_LOGI(TAG, "Display is I2C on port %u", address);
|
||||
} else if (strstr(config, "SPI") && spi_system_host != -1) {
|
||||
@@ -105,7 +108,7 @@ void display_init(char *welcome) {
|
||||
|
||||
init = true;
|
||||
GDS_SPIInit( spi_system_host, spi_system_dc_gpio );
|
||||
GDS_SPIAttachDevice( display, width, height, CS_pin, -1, speed );
|
||||
GDS_SPIAttachDevice( display, width, height, CS_pin, RST_pin, speed );
|
||||
|
||||
ESP_LOGI(TAG, "Display is SPI host %u with cs:%d", spi_system_host, CS_pin);
|
||||
} else {
|
||||
|
||||
Reference in New Issue
Block a user