mirror of
https://github.com/sle118/squeezelite-esp32.git
synced 2025-12-06 11:36:59 +03:00
driver for ST7735/89 & LED PWM
This commit is contained in:
@@ -73,6 +73,7 @@ static void Update16( struct GDS_Device* Device ) {
|
|||||||
LastCol = LastCol * 2 + 1;
|
LastCol = LastCol * 2 + 1;
|
||||||
SetRowAddress( Device, FirstRow, LastRow );
|
SetRowAddress( Device, FirstRow, LastRow );
|
||||||
SetColumnAddress( Device, FirstCol, LastCol );
|
SetColumnAddress( Device, FirstCol, LastCol );
|
||||||
|
Device->WriteCommand( Device, ENABLE_WRITE );
|
||||||
|
|
||||||
int ChunkSize = (LastCol - FirstCol + 1) * 2;
|
int ChunkSize = (LastCol - FirstCol + 1) * 2;
|
||||||
|
|
||||||
@@ -83,12 +84,10 @@ static void Update16( struct GDS_Device* Device ) {
|
|||||||
memcpy(optr, Private->Shadowbuffer + (i * Device->Width + FirstCol) * 2, ChunkSize);
|
memcpy(optr, Private->Shadowbuffer + (i * Device->Width + FirstCol) * 2, ChunkSize);
|
||||||
optr += ChunkSize;
|
optr += ChunkSize;
|
||||||
if (optr - Private->iRAM < PAGE_BLOCK && i < LastRow) continue;
|
if (optr - Private->iRAM < PAGE_BLOCK && i < LastRow) continue;
|
||||||
Device->WriteCommand( Device, ENABLE_WRITE );
|
|
||||||
Device->WriteData(Device, Private->iRAM, optr - Private->iRAM);
|
Device->WriteData(Device, Private->iRAM, optr - Private->iRAM);
|
||||||
optr = Private->iRAM;
|
optr = Private->iRAM;
|
||||||
}
|
}
|
||||||
} else for (int i = FirstRow; i <= LastRow; i++) {
|
} else for (int i = FirstRow; i <= LastRow; i++) {
|
||||||
Device->WriteCommand( Device, ENABLE_WRITE );
|
|
||||||
Device->WriteData( Device, Private->Shadowbuffer + (i * Device->Width + FirstCol) * 2, ChunkSize );
|
Device->WriteData( Device, Private->Shadowbuffer + (i * Device->Width + FirstCol) * 2, ChunkSize );
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -103,13 +102,12 @@ static void Update16( struct GDS_Device* Device ) {
|
|||||||
int Height = min(Private->PageSize, Device->Height - r);
|
int Height = min(Private->PageSize, Device->Height - r);
|
||||||
|
|
||||||
SetRowAddress( Device, r, r + Height - 1 );
|
SetRowAddress( Device, r, r + Height - 1 );
|
||||||
|
Device->WriteCommand(Device, ENABLE_WRITE);
|
||||||
|
|
||||||
if (Private->iRAM) {
|
if (Private->iRAM) {
|
||||||
memcpy(Private->iRAM, Device->Framebuffer + r * Device->Width * 2, Height * Device->Width * 2 );
|
memcpy(Private->iRAM, Device->Framebuffer + r * Device->Width * 2, Height * Device->Width * 2 );
|
||||||
Device->WriteCommand(Device, ENABLE_WRITE);
|
|
||||||
Device->WriteData( Device, Private->iRAM, Height * Device->Width * 2 );
|
Device->WriteData( Device, Private->iRAM, Height * Device->Width * 2 );
|
||||||
} else {
|
} else {
|
||||||
Device->WriteCommand(Device, ENABLE_WRITE);
|
|
||||||
Device->WriteData( Device, Device->Framebuffer + r * Device->Width * 2, Height * Device->Width * 2 );
|
Device->WriteData( Device, Device->Framebuffer + r * Device->Width * 2, Height * Device->Width * 2 );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -142,6 +140,7 @@ static void Update24( struct GDS_Device* Device ) {
|
|||||||
LastCol = (LastCol * 2 + 1) / 3;
|
LastCol = (LastCol * 2 + 1) / 3;
|
||||||
SetRowAddress( Device, FirstRow, LastRow );
|
SetRowAddress( Device, FirstRow, LastRow );
|
||||||
SetColumnAddress( Device, FirstCol, LastCol );
|
SetColumnAddress( Device, FirstCol, LastCol );
|
||||||
|
Device->WriteCommand( Device, ENABLE_WRITE );
|
||||||
|
|
||||||
int ChunkSize = (LastCol - FirstCol + 1) * 3;
|
int ChunkSize = (LastCol - FirstCol + 1) * 3;
|
||||||
|
|
||||||
@@ -152,12 +151,10 @@ static void Update24( struct GDS_Device* Device ) {
|
|||||||
memcpy(optr, Private->Shadowbuffer + (i * Device->Width + FirstCol) * 3, ChunkSize);
|
memcpy(optr, Private->Shadowbuffer + (i * Device->Width + FirstCol) * 3, ChunkSize);
|
||||||
optr += ChunkSize;
|
optr += ChunkSize;
|
||||||
if (optr - Private->iRAM < PAGE_BLOCK && i < LastRow) continue;
|
if (optr - Private->iRAM < PAGE_BLOCK && i < LastRow) continue;
|
||||||
Device->WriteCommand( Device, ENABLE_WRITE );
|
|
||||||
Device->WriteData(Device, Private->iRAM, optr - Private->iRAM);
|
Device->WriteData(Device, Private->iRAM, optr - Private->iRAM);
|
||||||
optr = Private->iRAM;
|
optr = Private->iRAM;
|
||||||
}
|
}
|
||||||
} else for (int i = FirstRow; i <= LastRow; i++) {
|
} else for (int i = FirstRow; i <= LastRow; i++) {
|
||||||
Device->WriteCommand( Device, ENABLE_WRITE );
|
|
||||||
Device->WriteData( Device, Private->Shadowbuffer + (i * Device->Width + FirstCol) * 3, ChunkSize );
|
Device->WriteData( Device, Private->Shadowbuffer + (i * Device->Width + FirstCol) * 3, ChunkSize );
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -167,15 +164,14 @@ static void Update24( struct GDS_Device* Device ) {
|
|||||||
#else
|
#else
|
||||||
// always update by full lines
|
// always update by full lines
|
||||||
SetColumnAddress( Device, 0, Device->Width - 1);
|
SetColumnAddress( Device, 0, Device->Width - 1);
|
||||||
|
Device->WriteCommand(Device, ENABLE_WRITE);
|
||||||
|
|
||||||
for (int r = 0; r < Device->Height; r += Private->PageSize) {
|
for (int r = 0; r < Device->Height; r += Private->PageSize) {
|
||||||
SetRowAddress( Device, r, r + Private->PageSize - 1 );
|
SetRowAddress( Device, r, r + Private->PageSize - 1 );
|
||||||
if (Private->iRAM) {
|
if (Private->iRAM) {
|
||||||
memcpy(Private->iRAM, Device->Framebuffer + r * Device->Width * 3, Private->PageSize * Device->Width * 3 );
|
memcpy(Private->iRAM, Device->Framebuffer + r * Device->Width * 3, Private->PageSize * Device->Width * 3 );
|
||||||
Device->WriteCommand(Device, ENABLE_WRITE);
|
|
||||||
Device->WriteData( Device, Private->iRAM, Private->PageSize * Device->Width * 3 );
|
Device->WriteData( Device, Private->iRAM, Private->PageSize * Device->Width * 3 );
|
||||||
} else {
|
} else {
|
||||||
Device->WriteCommand(Device, ENABLE_WRITE);
|
|
||||||
Device->WriteData( Device, Device->Framebuffer + r * Device->Width * 3, Private->PageSize * Device->Width * 3 );
|
Device->WriteData( Device, Device->Framebuffer + r * Device->Width * 3, Private->PageSize * Device->Width * 3 );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
280
components/display/ST77xx.c
Normal file
280
components/display/ST77xx.c
Normal file
@@ -0,0 +1,280 @@
|
|||||||
|
/**
|
||||||
|
* 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 <esp_heap_caps.h>
|
||||||
|
#include <esp_log.h>
|
||||||
|
|
||||||
|
#include "gds.h"
|
||||||
|
#include "gds_private.h"
|
||||||
|
|
||||||
|
#define SHADOW_BUFFER
|
||||||
|
#define USE_IRAM
|
||||||
|
#define PAGE_BLOCK 2048
|
||||||
|
#define ENABLE_WRITE 0x2c
|
||||||
|
|
||||||
|
#define min(a,b) (((a) < (b)) ? (a) : (b))
|
||||||
|
|
||||||
|
static char TAG[] = "ST77xx";
|
||||||
|
|
||||||
|
enum { ST7735, ST7789 };
|
||||||
|
|
||||||
|
struct PrivateSpace {
|
||||||
|
uint8_t *iRAM, *Shadowbuffer;
|
||||||
|
uint8_t MADCtl, PageSize;
|
||||||
|
uint8_t Model;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Functions are not declared to minimize # of lines
|
||||||
|
|
||||||
|
static void WriteByte( struct GDS_Device* Device, uint8_t Data ) {
|
||||||
|
Device->WriteData( Device, &Data, 1 );
|
||||||
|
}
|
||||||
|
|
||||||
|
static void SetColumnAddress( struct GDS_Device* Device, uint16_t Start, uint16_t End ) {
|
||||||
|
uint32_t Addr = __builtin_bswap16(Start) | (__builtin_bswap16(End) << 16);
|
||||||
|
Device->WriteCommand( Device, 0x2A );
|
||||||
|
Device->WriteData( Device, (uint8_t*) &Addr, 4 );
|
||||||
|
}
|
||||||
|
|
||||||
|
static void SetRowAddress( struct GDS_Device* Device, uint16_t Start, uint16_t End ) {
|
||||||
|
uint32_t Addr = __builtin_bswap16(Start) | (__builtin_bswap16(End) << 16);
|
||||||
|
Device->WriteCommand( Device, 0x2B );
|
||||||
|
Device->WriteData( Device, (uint8_t*) &Addr, 4 );
|
||||||
|
}
|
||||||
|
|
||||||
|
static void Update16( struct GDS_Device* Device ) {
|
||||||
|
struct PrivateSpace *Private = (struct PrivateSpace*) Device->Private;
|
||||||
|
|
||||||
|
#ifdef SHADOW_BUFFER
|
||||||
|
uint32_t *optr = (uint32_t*) Private->Shadowbuffer, *iptr = (uint32_t*) Device->Framebuffer;
|
||||||
|
int FirstCol = Device->Width / 2, LastCol = 0, FirstRow = -1, LastRow = 0;
|
||||||
|
|
||||||
|
for (int r = 0; r < Device->Height; r++) {
|
||||||
|
// look for change and update shadow (cheap optimization = width is always a multiple of 2)
|
||||||
|
for (int c = 0; c < Device->Width / 2; c++, iptr++, optr++) {
|
||||||
|
if (*optr != *iptr) {
|
||||||
|
*optr = *iptr;
|
||||||
|
if (c < FirstCol) FirstCol = c;
|
||||||
|
if (c > LastCol) LastCol = c;
|
||||||
|
if (FirstRow < 0) FirstRow = r;
|
||||||
|
LastRow = r;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// wait for a large enough window - careful that window size might increase by more than a line at once !
|
||||||
|
if (FirstRow < 0 || ((LastCol - FirstCol + 1) * (r - FirstRow + 1) * 4 < PAGE_BLOCK && r != Device->Height - 1)) continue;
|
||||||
|
|
||||||
|
FirstCol *= 2;
|
||||||
|
LastCol = LastCol * 2 + 1;
|
||||||
|
SetRowAddress( Device, FirstRow, LastRow );
|
||||||
|
SetColumnAddress( Device, FirstCol, LastCol );
|
||||||
|
Device->WriteCommand( Device, ENABLE_WRITE );
|
||||||
|
|
||||||
|
int ChunkSize = (LastCol - FirstCol + 1) * 2;
|
||||||
|
|
||||||
|
// own use of IRAM has not proven to be much better than letting SPI do its copy
|
||||||
|
if (Private->iRAM) {
|
||||||
|
uint8_t *optr = Private->iRAM;
|
||||||
|
for (int i = FirstRow; i <= LastRow; i++) {
|
||||||
|
memcpy(optr, Private->Shadowbuffer + (i * Device->Width + FirstCol) * 2, ChunkSize);
|
||||||
|
optr += ChunkSize;
|
||||||
|
if (optr - Private->iRAM < PAGE_BLOCK && i < LastRow) continue;
|
||||||
|
Device->WriteData(Device, Private->iRAM, optr - Private->iRAM);
|
||||||
|
optr = Private->iRAM;
|
||||||
|
}
|
||||||
|
} else for (int i = FirstRow; i <= LastRow; i++) {
|
||||||
|
Device->WriteData( Device, Private->Shadowbuffer + (i * Device->Width + FirstCol) * 2, ChunkSize );
|
||||||
|
}
|
||||||
|
|
||||||
|
FirstCol = Device->Width / 2; LastCol = 0;
|
||||||
|
FirstRow = -1;
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
// always update by full lines
|
||||||
|
SetColumnAddress( Device, 0, Device->Width - 1);
|
||||||
|
|
||||||
|
for (int r = 0; r < Device->Height; r += min(Private->PageSize, Device->Height - r)) {
|
||||||
|
int Height = min(Private->PageSize, Device->Height - r);
|
||||||
|
|
||||||
|
SetRowAddress( Device, r, r + Height - 1 );
|
||||||
|
Device->WriteCommand(Device, ENABLE_WRITE);
|
||||||
|
|
||||||
|
if (Private->iRAM) {
|
||||||
|
memcpy(Private->iRAM, Device->Framebuffer + r * Device->Width * 2, Height * Device->Width * 2 );
|
||||||
|
Device->WriteData( Device, Private->iRAM, Height * Device->Width * 2 );
|
||||||
|
} else {
|
||||||
|
Device->WriteData( Device, Device->Framebuffer + r * Device->Width * 2, Height * Device->Width * 2 );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
static void Update24( struct GDS_Device* Device ) {
|
||||||
|
struct PrivateSpace *Private = (struct PrivateSpace*) Device->Private;
|
||||||
|
|
||||||
|
#ifdef SHADOW_BUFFER
|
||||||
|
uint16_t *optr = (uint16_t*) Private->Shadowbuffer, *iptr = (uint16_t*) Device->Framebuffer;
|
||||||
|
int FirstCol = (Device->Width * 3) / 2, LastCol = 0, FirstRow = -1, LastRow = 0;
|
||||||
|
|
||||||
|
for (int r = 0; r < Device->Height; r++) {
|
||||||
|
// look for change and update shadow (cheap optimization = width always / by 2)
|
||||||
|
for (int c = 0; c < (Device->Width * 3) / 2; c++, optr++, iptr++) {
|
||||||
|
if (*optr != *iptr) {
|
||||||
|
*optr = *iptr;
|
||||||
|
if (c < FirstCol) FirstCol = c;
|
||||||
|
if (c > LastCol) LastCol = c;
|
||||||
|
if (FirstRow < 0) FirstRow = r;
|
||||||
|
LastRow = r;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// do we have enough to send (cols are divided by 3/2)
|
||||||
|
if (FirstRow < 0 || ((((LastCol - FirstCol + 1) * 2 + 3 - 1) / 3) * (r - FirstRow + 1) * 3 < PAGE_BLOCK && r != Device->Height - 1)) continue;
|
||||||
|
|
||||||
|
FirstCol = (FirstCol * 2) / 3;
|
||||||
|
LastCol = (LastCol * 2 + 1) / 3;
|
||||||
|
SetRowAddress( Device, FirstRow, LastRow );
|
||||||
|
SetColumnAddress( Device, FirstCol, LastCol );
|
||||||
|
Device->WriteCommand( Device, ENABLE_WRITE );
|
||||||
|
|
||||||
|
int ChunkSize = (LastCol - FirstCol + 1) * 3;
|
||||||
|
|
||||||
|
// own use of IRAM has not proven to be much better than letting SPI do its copy
|
||||||
|
if (Private->iRAM) {
|
||||||
|
uint8_t *optr = Private->iRAM;
|
||||||
|
for (int i = FirstRow; i <= LastRow; i++) {
|
||||||
|
memcpy(optr, Private->Shadowbuffer + (i * Device->Width + FirstCol) * 3, ChunkSize);
|
||||||
|
optr += ChunkSize;
|
||||||
|
if (optr - Private->iRAM < PAGE_BLOCK && i < LastRow) continue;
|
||||||
|
Device->WriteData(Device, Private->iRAM, optr - Private->iRAM);
|
||||||
|
optr = Private->iRAM;
|
||||||
|
}
|
||||||
|
} else for (int i = FirstRow; i <= LastRow; i++) {
|
||||||
|
Device->WriteData( Device, Private->Shadowbuffer + (i * Device->Width + FirstCol) * 3, ChunkSize );
|
||||||
|
}
|
||||||
|
|
||||||
|
FirstCol = (Device->Width * 3) / 2; LastCol = 0;
|
||||||
|
FirstRow = -1;
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
// always update by full lines
|
||||||
|
SetColumnAddress( Device, 0, Device->Width - 1);
|
||||||
|
Device->WriteCommand(Device, ENABLE_WRITE);
|
||||||
|
|
||||||
|
for (int r = 0; r < Device->Height; r += Private->PageSize) {
|
||||||
|
SetRowAddress( Device, r, r + Private->PageSize - 1 );
|
||||||
|
if (Private->iRAM) {
|
||||||
|
memcpy(Private->iRAM, Device->Framebuffer + r * Device->Width * 3, Private->PageSize * Device->Width * 3 );
|
||||||
|
Device->WriteData( Device, Private->iRAM, Private->PageSize * Device->Width * 3 );
|
||||||
|
} else {
|
||||||
|
Device->WriteData( Device, Device->Framebuffer + r * Device->Width * 3, Private->PageSize * Device->Width * 3 );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
static void SetHFlip( struct GDS_Device* Device, bool On ) {
|
||||||
|
struct PrivateSpace *Private = (struct PrivateSpace*) Device->Private;
|
||||||
|
Private->MADCtl = On ? (Private->MADCtl & ~(1 << 7)) : (Private->MADCtl | (1 << 7));
|
||||||
|
Device->WriteCommand( Device, 0x36 );
|
||||||
|
WriteByte( Device, Private->MADCtl );
|
||||||
|
}
|
||||||
|
|
||||||
|
static void SetVFlip( struct GDS_Device *Device, bool On ) {
|
||||||
|
struct PrivateSpace *Private = (struct PrivateSpace*) Device->Private;
|
||||||
|
Private->MADCtl = On ? (Private->MADCtl | (1 << 6)) : (Private->MADCtl & ~(1 << 6));
|
||||||
|
Device->WriteCommand( Device, 0x36 );
|
||||||
|
WriteByte( Device, Private->MADCtl );
|
||||||
|
}
|
||||||
|
|
||||||
|
static void DisplayOn( struct GDS_Device* Device ) { Device->WriteCommand( Device, 0x29 ); }
|
||||||
|
static void DisplayOff( struct GDS_Device* Device ) { Device->WriteCommand( Device, 0x28 ); }
|
||||||
|
|
||||||
|
static void SetContrast( struct GDS_Device* Device, uint8_t Contrast ) {
|
||||||
|
Device->WriteCommand( Device, 0x51 );
|
||||||
|
WriteByte( Device, Contrast );
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool Init( struct GDS_Device* Device ) {
|
||||||
|
struct PrivateSpace *Private = (struct PrivateSpace*) Device->Private;
|
||||||
|
int Depth = (Device->Depth + 8 - 1) / 8;
|
||||||
|
|
||||||
|
Private->PageSize = min(8, PAGE_BLOCK / (Device->Width * Depth));
|
||||||
|
|
||||||
|
#ifdef SHADOW_BUFFER
|
||||||
|
Private->Shadowbuffer = malloc( Device->FramebufferSize );
|
||||||
|
memset(Private->Shadowbuffer, 0xFF, Device->FramebufferSize);
|
||||||
|
#endif
|
||||||
|
#ifdef USE_IRAM
|
||||||
|
Private->iRAM = heap_caps_malloc( (Private->PageSize + 1) * Device->Width * Depth, MALLOC_CAP_INTERNAL | MALLOC_CAP_DMA );
|
||||||
|
#endif
|
||||||
|
|
||||||
|
ESP_LOGI(TAG, "ST77xx with bit depth %u, page %u, iRAM %p", Device->Depth, Private->PageSize, Private->iRAM);
|
||||||
|
|
||||||
|
// Sleepout + Booster
|
||||||
|
Device->WriteCommand( Device, 0x11 );
|
||||||
|
|
||||||
|
// need BGR & Address Mode
|
||||||
|
Private->MADCtl = (1 << 3) | ((Device->Width > Device->Height ? 1 : 0) << 5);
|
||||||
|
Device->WriteCommand( Device, 0x36 );
|
||||||
|
WriteByte( Device, Private->MADCtl );
|
||||||
|
|
||||||
|
// set flip modes & contrast
|
||||||
|
GDS_SetContrast( Device, 0x7F );
|
||||||
|
Device->SetVFlip( Device, false );
|
||||||
|
Device->SetHFlip( Device, false );
|
||||||
|
|
||||||
|
// set screen depth (16/18)
|
||||||
|
Device->WriteCommand( Device, 0x3A );
|
||||||
|
WriteByte( Device, Device->Depth == 24 ? 0x06 : 0x05 );
|
||||||
|
|
||||||
|
// no Display Inversion
|
||||||
|
Device->WriteCommand( Device, 0x20 );
|
||||||
|
|
||||||
|
// gone with the wind
|
||||||
|
Device->DisplayOn( Device );
|
||||||
|
Device->Update( Device );
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct GDS_Device ST77xx = {
|
||||||
|
.DisplayOn = DisplayOn, .DisplayOff = DisplayOff,
|
||||||
|
.SetVFlip = SetVFlip, .SetHFlip = SetHFlip,
|
||||||
|
.Update = Update16, .Init = Init,
|
||||||
|
.Mode = GDS_RGB565, .Depth = 16,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct GDS_Device* ST77xx_Detect(char *Driver, struct GDS_Device* Device) {
|
||||||
|
uint8_t Model;
|
||||||
|
int Depth;
|
||||||
|
|
||||||
|
if (strcasestr(Driver, "ST7735")) Model = ST7735;
|
||||||
|
else if (strcasestr(Driver, "ST7789")) Model = ST7789;
|
||||||
|
else return NULL;
|
||||||
|
|
||||||
|
if (!Device) Device = calloc(1, sizeof(struct GDS_Device));
|
||||||
|
|
||||||
|
*Device = ST77xx;
|
||||||
|
((struct PrivateSpace*) Device->Private)->Model = Model;
|
||||||
|
sscanf(Driver, "%*[^:]:%u", &Depth);
|
||||||
|
|
||||||
|
if (Depth == 18) {
|
||||||
|
Device->Mode = GDS_RGB666;
|
||||||
|
Device->Depth = 24;
|
||||||
|
Device->Update = Update24;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Model == ST7789) Device->SetContrast = SetContrast;
|
||||||
|
|
||||||
|
return Device;
|
||||||
|
}
|
||||||
@@ -9,22 +9,37 @@
|
|||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <ctype.h>
|
#include <ctype.h>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
#include <math.h>
|
||||||
#include "freertos/FreeRTOS.h"
|
#include "freertos/FreeRTOS.h"
|
||||||
#include "freertos/task.h"
|
#include "freertos/task.h"
|
||||||
#include "driver/gpio.h"
|
#include "driver/gpio.h"
|
||||||
|
#include "driver/ledc.h"
|
||||||
#include "esp_log.h"
|
#include "esp_log.h"
|
||||||
|
|
||||||
#include "gds.h"
|
#include "gds.h"
|
||||||
#include "gds_private.h"
|
#include "gds_private.h"
|
||||||
|
|
||||||
static struct GDS_Device Display;
|
static struct GDS_Device Display;
|
||||||
|
static struct GDS_BacklightPWM PWMConfig;
|
||||||
|
|
||||||
static char TAG[] = "gds";
|
static char TAG[] = "gds";
|
||||||
|
|
||||||
struct GDS_Device* GDS_AutoDetect( char *Driver, GDS_DetectFunc* DetectFunc[] ) {
|
struct GDS_Device* GDS_AutoDetect( char *Driver, GDS_DetectFunc* DetectFunc[], struct GDS_BacklightPWM* PWM ) {
|
||||||
|
if (!Driver) return NULL;
|
||||||
|
if (PWM) PWMConfig = *PWM;
|
||||||
|
|
||||||
for (int i = 0; DetectFunc[i]; i++) {
|
for (int i = 0; DetectFunc[i]; i++) {
|
||||||
if (DetectFunc[i](Driver, &Display)) {
|
if (DetectFunc[i](Driver, &Display)) {
|
||||||
ESP_LOGD(TAG, "Detected driver %p", &Display);
|
if (PWM && PWM->Init) {
|
||||||
|
ledc_timer_config_t PWMTimer = {
|
||||||
|
.duty_resolution = LEDC_TIMER_13_BIT,
|
||||||
|
.freq_hz = 5000,
|
||||||
|
.speed_mode = LEDC_HIGH_SPEED_MODE,
|
||||||
|
.timer_num = PWMConfig.Timer,
|
||||||
|
};
|
||||||
|
ledc_timer_config(&PWMTimer);
|
||||||
|
}
|
||||||
|
ESP_LOGD(TAG, "Detected driver %p with PWM %d", &Display, PWM ? PWM->Init : 0);
|
||||||
return &Display;
|
return &Display;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -165,6 +180,22 @@ bool GDS_Init( struct GDS_Device* Device ) {
|
|||||||
NullCheck( Device->Framebuffer, return false );
|
NullCheck( Device->Framebuffer, return false );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (Device->Backlight.Pin >= 0) {
|
||||||
|
Device->Backlight.Channel = PWMConfig.Channel++;
|
||||||
|
Device->Backlight.PWM = PWMConfig.Max - 1;
|
||||||
|
|
||||||
|
ledc_channel_config_t PWMChannel = {
|
||||||
|
.channel = Device->Backlight.Channel,
|
||||||
|
.duty = Device->Backlight.PWM,
|
||||||
|
.gpio_num = Device->Backlight.Pin,
|
||||||
|
.speed_mode = LEDC_HIGH_SPEED_MODE,
|
||||||
|
.hpoint = 0,
|
||||||
|
.timer_sel = PWMConfig.Timer,
|
||||||
|
};
|
||||||
|
|
||||||
|
ledc_channel_config(&PWMChannel);
|
||||||
|
}
|
||||||
|
|
||||||
bool Res = Device->Init( Device );
|
bool Res = Device->Init( Device );
|
||||||
if (!Res && Device->Framebuffer) free(Device->Framebuffer);
|
if (!Res && Device->Framebuffer) free(Device->Framebuffer);
|
||||||
return Res;
|
return Res;
|
||||||
@@ -196,7 +227,15 @@ int GDS_GrayMap( struct GDS_Device* Device, uint8_t Level) {
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
void GDS_SetContrast( struct GDS_Device* Device, uint8_t Contrast ) { if (Device->SetContrast) Device->SetContrast( Device, Contrast); }
|
void GDS_SetContrast( struct GDS_Device* Device, uint8_t Contrast ) {
|
||||||
|
if (Device->SetContrast) Device->SetContrast( Device, Contrast );
|
||||||
|
else if (Device->Backlight.Pin >= 0) {
|
||||||
|
Device->Backlight.PWM = PWMConfig.Max * powf(Contrast / 255.0, 3);
|
||||||
|
ledc_set_duty( LEDC_HIGH_SPEED_MODE, Device->Backlight.Channel, Device->Backlight.PWM );
|
||||||
|
ledc_update_duty( LEDC_HIGH_SPEED_MODE, Device->Backlight.Channel );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void GDS_SetHFlip( struct GDS_Device* Device, bool On ) { if (Device->SetHFlip) Device->SetHFlip( Device, On ); }
|
void GDS_SetHFlip( struct GDS_Device* Device, bool On ) { if (Device->SetHFlip) Device->SetHFlip( Device, On ); }
|
||||||
void GDS_SetVFlip( struct GDS_Device* Device, bool On ) { if (Device->SetVFlip) Device->SetVFlip( Device, On ); }
|
void GDS_SetVFlip( struct GDS_Device* Device, bool On ) { if (Device->SetVFlip) Device->SetVFlip( Device, On ); }
|
||||||
void GDS_SetDirty( struct GDS_Device* Device ) { Device->Dirty = true; }
|
void GDS_SetDirty( struct GDS_Device* Device ) { Device->Dirty = true; }
|
||||||
|
|||||||
@@ -22,10 +22,14 @@ enum { GDS_MONO = 0, GDS_GRAYSCALE, GDS_RGB332, GDS_RGB444, GDS_RGB555, GDS_RGB5
|
|||||||
|
|
||||||
struct GDS_Device;
|
struct GDS_Device;
|
||||||
struct GDS_FontDef;
|
struct GDS_FontDef;
|
||||||
|
struct GDS_BacklightPWM {
|
||||||
|
int Channel, Timer, Max;
|
||||||
|
bool Init;
|
||||||
|
};
|
||||||
|
|
||||||
typedef struct GDS_Device* GDS_DetectFunc(char *Driver, struct GDS_Device *Device);
|
typedef struct GDS_Device* GDS_DetectFunc(char *Driver, struct GDS_Device *Device);
|
||||||
|
|
||||||
struct GDS_Device* GDS_AutoDetect( char *Driver, GDS_DetectFunc* DetectFunc[] );
|
struct GDS_Device* GDS_AutoDetect( char *Driver, GDS_DetectFunc* DetectFunc[], struct GDS_BacklightPWM *PWM );
|
||||||
|
|
||||||
void GDS_SetContrast( struct GDS_Device* Device, uint8_t Contrast );
|
void GDS_SetContrast( struct GDS_Device* Device, uint8_t Contrast );
|
||||||
void GDS_DisplayOn( struct GDS_Device* Device );
|
void GDS_DisplayOn( struct GDS_Device* Device );
|
||||||
|
|||||||
@@ -8,10 +8,10 @@ extern "C" {
|
|||||||
struct GDS_Device;
|
struct GDS_Device;
|
||||||
|
|
||||||
bool GDS_I2CInit( int PortNumber, int SDA, int SCL, int speed );
|
bool GDS_I2CInit( int PortNumber, int SDA, int SCL, int speed );
|
||||||
bool GDS_I2CAttachDevice( struct GDS_Device* Device, int Width, int Height, int I2CAddress, int RSTPin );
|
bool GDS_I2CAttachDevice( struct GDS_Device* Device, int Width, int Height, int I2CAddress, int RSTPin, int BacklightPin );
|
||||||
|
|
||||||
bool GDS_SPIInit( int SPI, int DC );
|
bool GDS_SPIInit( int SPI, int DC );
|
||||||
bool GDS_SPIAttachDevice( struct GDS_Device* Device, int Width, int Height, int CSPin, int RSTPin, int Speed );
|
bool GDS_SPIAttachDevice( struct GDS_Device* Device, int Width, int Height, int CSPin, int RSTPin, int Speed, int BacklightPin );
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -72,6 +72,11 @@ typedef struct spi_device_t* spi_device_handle_t;
|
|||||||
|
|
||||||
struct GDS_Device {
|
struct GDS_Device {
|
||||||
uint8_t IF;
|
uint8_t IF;
|
||||||
|
int8_t RSTPin;
|
||||||
|
struct {
|
||||||
|
int8_t Pin, Channel;
|
||||||
|
int PWM;
|
||||||
|
} Backlight;
|
||||||
union {
|
union {
|
||||||
// I2C Specific
|
// I2C Specific
|
||||||
struct {
|
struct {
|
||||||
@@ -80,7 +85,6 @@ struct GDS_Device {
|
|||||||
// SPI specific
|
// SPI specific
|
||||||
struct {
|
struct {
|
||||||
spi_device_handle_t SPIHandle;
|
spi_device_handle_t SPIHandle;
|
||||||
int8_t RSTPin;
|
|
||||||
int8_t CSPin;
|
int8_t CSPin;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -21,7 +21,6 @@ static int I2CWait;
|
|||||||
|
|
||||||
static const int GDS_I2C_COMMAND_MODE = 0x80;
|
static const int GDS_I2C_COMMAND_MODE = 0x80;
|
||||||
static const int GDS_I2C_DATA_MODE = 0x40;
|
static const int GDS_I2C_DATA_MODE = 0x40;
|
||||||
static const int i2c_timeout_value=2000;
|
|
||||||
|
|
||||||
static bool I2CDefaultWriteBytes( int Address, bool IsCommand, const uint8_t* Data, size_t DataLength );
|
static bool I2CDefaultWriteBytes( int Address, bool IsCommand, const uint8_t* Data, size_t DataLength );
|
||||||
static bool I2CDefaultWriteCommand( struct GDS_Device* Device, uint8_t Command );
|
static bool I2CDefaultWriteCommand( struct GDS_Device* Device, uint8_t Command );
|
||||||
@@ -50,9 +49,6 @@ bool GDS_I2CInit( int PortNumber, int SDA, int SCL, int Speed ) {
|
|||||||
|
|
||||||
ESP_ERROR_CHECK_NONFATAL( i2c_param_config( I2CPortNumber, &Config ), return false );
|
ESP_ERROR_CHECK_NONFATAL( i2c_param_config( I2CPortNumber, &Config ), return false );
|
||||||
ESP_ERROR_CHECK_NONFATAL( i2c_driver_install( I2CPortNumber, Config.mode, 0, 0, 0 ), return false );
|
ESP_ERROR_CHECK_NONFATAL( i2c_driver_install( I2CPortNumber, Config.mode, 0, 0, 0 ), return false );
|
||||||
printf("Setting timeout value to %d",i2c_timeout_value);
|
|
||||||
i2c_set_timeout(I2CPortNumber, (I2C_APB_CLK_FREQ /(2500000))* i2c_timeout_value);
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
@@ -70,13 +66,14 @@ bool GDS_I2CInit( int PortNumber, int SDA, int SCL, int Speed ) {
|
|||||||
*
|
*
|
||||||
* Returns true on successful init of display.
|
* Returns true on successful init of display.
|
||||||
*/
|
*/
|
||||||
bool GDS_I2CAttachDevice( struct GDS_Device* Device, int Width, int Height, int I2CAddress, int RSTPin ) {
|
bool GDS_I2CAttachDevice( struct GDS_Device* Device, int Width, int Height, int I2CAddress, int RSTPin, int BacklightPin ) {
|
||||||
NullCheck( Device, return false );
|
NullCheck( Device, return false );
|
||||||
|
|
||||||
Device->WriteCommand = I2CDefaultWriteCommand;
|
Device->WriteCommand = I2CDefaultWriteCommand;
|
||||||
Device->WriteData = I2CDefaultWriteData;
|
Device->WriteData = I2CDefaultWriteData;
|
||||||
Device->Address = I2CAddress;
|
Device->Address = I2CAddress;
|
||||||
Device->RSTPin = RSTPin;
|
Device->RSTPin = RSTPin;
|
||||||
|
Device->Backlight.Pin = BacklightPin;
|
||||||
Device->IF = GDS_IF_I2C;
|
Device->IF = GDS_IF_I2C;
|
||||||
Device->Width = Width;
|
Device->Width = Width;
|
||||||
Device->Height = Height;
|
Device->Height = Height;
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ bool GDS_SPIInit( int SPI, int DC ) {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool GDS_SPIAttachDevice( struct GDS_Device* Device, int Width, int Height, int CSPin, int RSTPin, int Speed ) {
|
bool GDS_SPIAttachDevice( struct GDS_Device* Device, int Width, int Height, int CSPin, int RSTPin, int BackLightPin, int Speed ) {
|
||||||
spi_device_interface_config_t SPIDeviceConfig;
|
spi_device_interface_config_t SPIDeviceConfig;
|
||||||
spi_device_handle_t SPIDevice;
|
spi_device_handle_t SPIDevice;
|
||||||
|
|
||||||
@@ -59,6 +59,7 @@ bool GDS_SPIAttachDevice( struct GDS_Device* Device, int Width, int Height, int
|
|||||||
Device->SPIHandle = SPIDevice;
|
Device->SPIHandle = SPIDevice;
|
||||||
Device->RSTPin = RSTPin;
|
Device->RSTPin = RSTPin;
|
||||||
Device->CSPin = CSPin;
|
Device->CSPin = CSPin;
|
||||||
|
Device->Backlight.Pin = BackLightPin;
|
||||||
Device->IF = GDS_IF_SPI;
|
Device->IF = GDS_IF_SPI;
|
||||||
Device->Width = Width;
|
Device->Width = Width;
|
||||||
Device->Height = Height;
|
Device->Height = Height;
|
||||||
|
|||||||
@@ -52,14 +52,15 @@ static const char *known_drivers[] = {"SH1106",
|
|||||||
"SSD1327",
|
"SSD1327",
|
||||||
"SSD1675",
|
"SSD1675",
|
||||||
"SSD1351",
|
"SSD1351",
|
||||||
|
"ST77xx",
|
||||||
"ILI9341",
|
"ILI9341",
|
||||||
NULL
|
NULL
|
||||||
};
|
};
|
||||||
static void displayer_task(void *args);
|
static void displayer_task(void *args);
|
||||||
|
|
||||||
struct GDS_Device *display;
|
struct GDS_Device *display;
|
||||||
extern GDS_DetectFunc SSD1306_Detect, SSD132x_Detect, SH1106_Detect, SSD1675_Detect, SSD1322_Detect, SSD1351_Detect, ILI9341_Detect;
|
extern GDS_DetectFunc SSD1306_Detect, SSD132x_Detect, SH1106_Detect, SSD1675_Detect, SSD1322_Detect, SSD1351_Detect, ST77xx_Detect, ILI9341_Detect;
|
||||||
GDS_DetectFunc *drivers[] = { SH1106_Detect, SSD1306_Detect, SSD132x_Detect, SSD1675_Detect, SSD1322_Detect, SSD1351_Detect, ILI9341_Detect, NULL };
|
GDS_DetectFunc *drivers[] = { SH1106_Detect, SSD1306_Detect, SSD132x_Detect, SSD1675_Detect, SSD1322_Detect, SSD1351_Detect, ST77xx_Detect, ILI9341_Detect, NULL };
|
||||||
|
|
||||||
/****************************************************************************************
|
/****************************************************************************************
|
||||||
*
|
*
|
||||||
@@ -73,15 +74,21 @@ void display_init(char *welcome) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
int width = -1, height = -1;
|
int width = -1, height = -1, backlight_pin = -1;
|
||||||
char *p, *drivername = strstr(config, "driver");
|
char *p, *drivername = strstr(config, "driver");
|
||||||
|
|
||||||
// query drivers to see if we have a match
|
|
||||||
ESP_LOGI(TAG, "Trying to configure display with %s", config);
|
|
||||||
display = GDS_AutoDetect(drivername ? drivername : "SSD1306", drivers);
|
|
||||||
|
|
||||||
if ((p = strcasestr(config, "width")) != NULL) width = atoi(strchr(p, '=') + 1);
|
if ((p = strcasestr(config, "width")) != NULL) width = atoi(strchr(p, '=') + 1);
|
||||||
if ((p = strcasestr(config, "height")) != NULL) height = atoi(strchr(p, '=') + 1);
|
if ((p = strcasestr(config, "height")) != NULL) height = atoi(strchr(p, '=') + 1);
|
||||||
|
if ((p = strcasestr(config, "back")) != NULL) backlight_pin = atoi(strchr(p, '=') + 1);
|
||||||
|
|
||||||
|
// query drivers to see if we have a match
|
||||||
|
ESP_LOGI(TAG, "Trying to configure display with %s", config);
|
||||||
|
if (backlight_pin >= 0) {
|
||||||
|
struct GDS_BacklightPWM PWMConfig = { .Channel = pwm_system.base_channel++, .Timer = pwm_system.timer, .Max = pwm_system.max, .Init = false };
|
||||||
|
display = GDS_AutoDetect(drivername, drivers, &PWMConfig);
|
||||||
|
} else {
|
||||||
|
display = GDS_AutoDetect(drivername, drivers, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
// so far so good
|
// so far so good
|
||||||
if (display && width > 0 && height > 0) {
|
if (display && width > 0 && height > 0) {
|
||||||
@@ -96,7 +103,7 @@ void display_init(char *welcome) {
|
|||||||
|
|
||||||
init = true;
|
init = true;
|
||||||
GDS_I2CInit( i2c_system_port, -1, -1, i2c_system_speed ) ;
|
GDS_I2CInit( i2c_system_port, -1, -1, i2c_system_speed ) ;
|
||||||
GDS_I2CAttachDevice( display, width, height, address, RST_pin );
|
GDS_I2CAttachDevice( display, width, height, address, RST_pin, backlight_pin );
|
||||||
|
|
||||||
ESP_LOGI(TAG, "Display is I2C on port %u", address);
|
ESP_LOGI(TAG, "Display is I2C on port %u", address);
|
||||||
} else if (strstr(config, "SPI") && spi_system_host != -1) {
|
} else if (strstr(config, "SPI") && spi_system_host != -1) {
|
||||||
@@ -107,7 +114,7 @@ void display_init(char *welcome) {
|
|||||||
|
|
||||||
init = true;
|
init = true;
|
||||||
GDS_SPIInit( spi_system_host, spi_system_dc_gpio );
|
GDS_SPIInit( spi_system_host, spi_system_dc_gpio );
|
||||||
GDS_SPIAttachDevice( display, width, height, CS_pin, RST_pin, speed );
|
GDS_SPIAttachDevice( display, width, height, CS_pin, RST_pin, backlight_pin, speed );
|
||||||
|
|
||||||
ESP_LOGI(TAG, "Display is SPI host %u with cs:%d", spi_system_host, CS_pin);
|
ESP_LOGI(TAG, "Display is SPI host %u with cs:%d", spi_system_host, CS_pin);
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -18,6 +18,10 @@ extern int i2c_system_speed;
|
|||||||
extern int spi_system_host;
|
extern int spi_system_host;
|
||||||
extern int spi_system_dc_gpio;
|
extern int spi_system_dc_gpio;
|
||||||
extern bool gpio36_39_used;
|
extern bool gpio36_39_used;
|
||||||
|
typedef struct {
|
||||||
|
int timer, base_channel, max;
|
||||||
|
} pwm_system_t;
|
||||||
|
extern pwm_system_t pwm_system;
|
||||||
|
|
||||||
#ifdef CONFIG_SQUEEZEAMP
|
#ifdef CONFIG_SQUEEZEAMP
|
||||||
#define ADAC dac_tas57xx
|
#define ADAC dac_tas57xx
|
||||||
|
|||||||
@@ -10,13 +10,16 @@
|
|||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
#include <math.h>
|
||||||
#include "freertos/FreeRTOS.h"
|
#include "freertos/FreeRTOS.h"
|
||||||
#include "freertos/task.h"
|
#include "freertos/task.h"
|
||||||
#include "freertos/timers.h"
|
#include "freertos/timers.h"
|
||||||
#include "esp_system.h"
|
#include "esp_system.h"
|
||||||
#include "esp_log.h"
|
#include "esp_log.h"
|
||||||
#include "driver/gpio.h"
|
#include "driver/gpio.h"
|
||||||
|
#include "driver/ledc.h"
|
||||||
#include "led.h"
|
#include "led.h"
|
||||||
|
#include "globdefs.h"
|
||||||
#include "accessors.h"
|
#include "accessors.h"
|
||||||
|
|
||||||
#define MAX_LED 8
|
#define MAX_LED 8
|
||||||
@@ -29,6 +32,8 @@ static struct led_s {
|
|||||||
bool on;
|
bool on;
|
||||||
int onstate;
|
int onstate;
|
||||||
int ontime, offtime;
|
int ontime, offtime;
|
||||||
|
int pwm;
|
||||||
|
int channel;
|
||||||
int pushedon, pushedoff;
|
int pushedon, pushedoff;
|
||||||
bool pushed;
|
bool pushed;
|
||||||
TimerHandle_t timer;
|
TimerHandle_t timer;
|
||||||
@@ -37,8 +42,22 @@ static struct led_s {
|
|||||||
static struct {
|
static struct {
|
||||||
int gpio;
|
int gpio;
|
||||||
int active;
|
int active;
|
||||||
} green = { CONFIG_LED_GREEN_GPIO, 0 },
|
int pwm;
|
||||||
red = { CONFIG_LED_RED_GPIO, 0 };
|
} green = { .gpio = CONFIG_LED_GREEN_GPIO, .active = 0, .pwm = -1 },
|
||||||
|
red = { .gpio = CONFIG_LED_RED_GPIO, .active = 0, .pwm = -1 };
|
||||||
|
|
||||||
|
static int led_max = 2;
|
||||||
|
|
||||||
|
/****************************************************************************************
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
static void set_level(struct led_s *led, bool on) {
|
||||||
|
if (led->pwm < 0) gpio_set_level(led->gpio, on ? led->onstate : !led->onstate);
|
||||||
|
else {
|
||||||
|
ledc_set_duty(LEDC_HIGH_SPEED_MODE, led->channel, on ? led->pwm : (led->onstate ? 0 : pwm_system.max));
|
||||||
|
ledc_update_duty(LEDC_HIGH_SPEED_MODE, led->channel);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/****************************************************************************************
|
/****************************************************************************************
|
||||||
*
|
*
|
||||||
@@ -49,8 +68,8 @@ static void vCallbackFunction( TimerHandle_t xTimer ) {
|
|||||||
if (!led->timer) return;
|
if (!led->timer) return;
|
||||||
|
|
||||||
led->on = !led->on;
|
led->on = !led->on;
|
||||||
ESP_LOGD(TAG,"led vCallbackFunction setting gpio %d level", led->gpio);
|
ESP_EARLY_LOGD(TAG,"led vCallbackFunction setting gpio %d level %d (pwm:%d)", led->gpio, led->on, led->pwm);
|
||||||
gpio_set_level(led->gpio, led->on ? led->onstate : !led->onstate);
|
set_level(led, led->on);
|
||||||
|
|
||||||
// was just on for a while
|
// was just on for a while
|
||||||
if (!led->on && led->offtime == -1) return;
|
if (!led->on && led->offtime == -1) return;
|
||||||
@@ -63,7 +82,7 @@ static void vCallbackFunction( TimerHandle_t xTimer ) {
|
|||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
bool led_blink_core(int idx, int ontime, int offtime, bool pushed) {
|
bool led_blink_core(int idx, int ontime, int offtime, bool pushed) {
|
||||||
if (!leds[idx].gpio || leds[idx].gpio<0 ) return false;
|
if (!leds[idx].gpio || leds[idx].gpio < 0 ) return false;
|
||||||
|
|
||||||
ESP_LOGD(TAG,"led_blink_core");
|
ESP_LOGD(TAG,"led_blink_core");
|
||||||
if (leds[idx].timer) {
|
if (leds[idx].timer) {
|
||||||
@@ -89,22 +108,37 @@ bool led_blink_core(int idx, int ontime, int offtime, bool pushed) {
|
|||||||
|
|
||||||
if (ontime == 0) {
|
if (ontime == 0) {
|
||||||
ESP_LOGD(TAG,"led %d, setting reverse level", idx);
|
ESP_LOGD(TAG,"led %d, setting reverse level", idx);
|
||||||
gpio_set_level(leds[idx].gpio, !leds[idx].onstate);
|
set_level(leds + idx, false);
|
||||||
} else if (offtime == 0) {
|
} else if (offtime == 0) {
|
||||||
ESP_LOGD(TAG,"led %d, setting level", idx);
|
ESP_LOGD(TAG,"led %d, setting level", idx);
|
||||||
gpio_set_level(leds[idx].gpio, leds[idx].onstate);
|
set_level(leds + idx, true);
|
||||||
} else {
|
} else {
|
||||||
if (!leds[idx].timer) {
|
if (!leds[idx].timer) {
|
||||||
ESP_LOGD(TAG,"led %d, Creating timer", idx);
|
ESP_LOGD(TAG,"led %d, Creating timer", idx);
|
||||||
leds[idx].timer = xTimerCreate("ledTimer", ontime / portTICK_RATE_MS, pdFALSE, (void *)&leds[idx], vCallbackFunction);
|
leds[idx].timer = xTimerCreate("ledTimer", ontime / portTICK_RATE_MS, pdFALSE, (void *)&leds[idx], vCallbackFunction);
|
||||||
}
|
}
|
||||||
leds[idx].on = true;
|
leds[idx].on = true;
|
||||||
ESP_LOGD(TAG,"led %d, Setting gpio %d", idx, leds[idx].gpio);
|
set_level(leds + idx, true);
|
||||||
gpio_set_level(leds[idx].gpio, leds[idx].onstate);
|
|
||||||
ESP_LOGD(TAG,"led %d, Starting timer.", idx);
|
ESP_LOGD(TAG,"led %d, Setting gpio %d and starting timer", idx, leds[idx].gpio);
|
||||||
if (xTimerStart(leds[idx].timer, BLOCKTIME) == pdFAIL) return false;
|
if (xTimerStart(leds[idx].timer, BLOCKTIME) == pdFAIL) return false;
|
||||||
}
|
}
|
||||||
ESP_LOGD(TAG,"led %d, led_blink_core_done", idx);
|
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/****************************************************************************************
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
bool led_brightness(int idx, int pwm) {
|
||||||
|
if (pwm > 100) pwm = 100;
|
||||||
|
leds[idx].pwm = pwm_system.max * powf(pwm / 100.0, 3);
|
||||||
|
if (!leds[idx].onstate) leds[idx].pwm = pwm_system.max - leds[idx].pwm;
|
||||||
|
|
||||||
|
ledc_set_duty(LEDC_HIGH_SPEED_MODE, leds[idx].channel, leds[idx].pwm);
|
||||||
|
ledc_update_duty(LEDC_HIGH_SPEED_MODE, leds[idx].channel);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -123,33 +157,49 @@ bool led_unpush(int idx) {
|
|||||||
/****************************************************************************************
|
/****************************************************************************************
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
bool led_config(int idx, gpio_num_t gpio, int onstate) {
|
int led_allocate(void) {
|
||||||
if(gpio<0){
|
if (led_max < MAX_LED) return led_max++;
|
||||||
ESP_LOGW(TAG,"LED GPIO not configured");
|
return -1;
|
||||||
return false;
|
|
||||||
}
|
|
||||||
ESP_LOGD(TAG,"Index %d, GPIO %d, on state %s", idx, gpio, onstate>0?"On":"Off");
|
|
||||||
if (idx >= MAX_LED) return false;
|
|
||||||
leds[idx].gpio = gpio;
|
|
||||||
leds[idx].onstate = onstate;
|
|
||||||
ESP_LOGD(TAG,"Index %d, GPIO %d, on state %s. Selecting GPIO pad", idx, gpio, onstate>0?"On":"Off");
|
|
||||||
gpio_pad_select_gpio(gpio);
|
|
||||||
ESP_LOGD(TAG,"Index %d, GPIO %d, on state %s. Setting direction to OUTPUT", idx, gpio, onstate>0?"On":"Off");
|
|
||||||
gpio_set_direction(gpio, GPIO_MODE_OUTPUT);
|
|
||||||
ESP_LOGD(TAG,"Index %d, GPIO %d, on state %s. Setting State to %d", idx, gpio, onstate>0?"On":"Off", onstate);
|
|
||||||
gpio_set_level(gpio, !onstate);
|
|
||||||
ESP_LOGD(TAG,"Done configuring the led");
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/****************************************************************************************
|
/****************************************************************************************
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
bool led_unconfig(int idx) {
|
bool led_config(int idx, gpio_num_t gpio, int onstate, int pwm) {
|
||||||
|
if (gpio < 0) {
|
||||||
|
ESP_LOGW(TAG,"LED GPIO not configured");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
ESP_LOGD(TAG,"Index %d, GPIO %d, on state %s", idx, gpio, onstate>0?"On":"Off");
|
||||||
if (idx >= MAX_LED) return false;
|
if (idx >= MAX_LED) return false;
|
||||||
|
|
||||||
if (leds[idx].timer) xTimerDelete(leds[idx].timer, BLOCKTIME);
|
leds[idx].gpio = gpio;
|
||||||
leds[idx].timer = NULL;
|
leds[idx].onstate = onstate;
|
||||||
|
leds[idx].pwm = -1;
|
||||||
|
|
||||||
|
if (pwm < 0) {
|
||||||
|
gpio_pad_select_gpio(gpio);
|
||||||
|
gpio_set_direction(gpio, GPIO_MODE_OUTPUT);
|
||||||
|
} else {
|
||||||
|
leds[idx].channel = pwm_system.base_channel++;
|
||||||
|
leds[idx].pwm = pwm_system.max * powf(pwm / 100.0, 3);
|
||||||
|
if (!onstate) leds[idx].pwm = pwm_system.max - leds[idx].pwm;
|
||||||
|
|
||||||
|
ledc_channel_config_t ledc_channel = {
|
||||||
|
.channel = leds[idx].channel,
|
||||||
|
.duty = leds[idx].pwm,
|
||||||
|
.gpio_num = gpio,
|
||||||
|
.speed_mode = LEDC_HIGH_SPEED_MODE,
|
||||||
|
.hpoint = 0,
|
||||||
|
.timer_sel = pwm_system.timer,
|
||||||
|
};
|
||||||
|
|
||||||
|
ledc_channel_config(&ledc_channel);
|
||||||
|
}
|
||||||
|
|
||||||
|
set_level(leds + idx, false);
|
||||||
|
ESP_LOGD(TAG,"PWM Index %d, GPIO %d, on state %s, pwm %d%%", idx, gpio, onstate > 0 ? "On" : "Off", pwm);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -170,7 +220,6 @@ void set_led_gpio(int gpio, char *value) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void led_svc_init(void) {
|
void led_svc_init(void) {
|
||||||
|
|
||||||
#ifdef CONFIG_LED_GREEN_GPIO_LEVEL
|
#ifdef CONFIG_LED_GREEN_GPIO_LEVEL
|
||||||
green.active = CONFIG_LED_GREEN_GPIO_LEVEL;
|
green.active = CONFIG_LED_GREEN_GPIO_LEVEL;
|
||||||
#endif
|
#endif
|
||||||
@@ -181,8 +230,15 @@ void led_svc_init(void) {
|
|||||||
#ifndef CONFIG_LED_LOCKED
|
#ifndef CONFIG_LED_LOCKED
|
||||||
parse_set_GPIO(set_led_gpio);
|
parse_set_GPIO(set_led_gpio);
|
||||||
#endif
|
#endif
|
||||||
ESP_LOGI(TAG,"Configuring LEDs green:%d (active:%d), red:%d (active:%d)", green.gpio, green.active, red.gpio, red.active);
|
ESP_LOGI(TAG,"Configuring LEDs green:%d (active:%d %d%%), red:%d (active:%d %d%%)", green.gpio, green.active, green.pwm, red.gpio, red.active, red.pwm);
|
||||||
|
|
||||||
led_config(LED_GREEN, green.gpio, green.active);
|
char *nvs_item = config_alloc_get(NVS_TYPE_STR, "led_brightness"), *p;
|
||||||
led_config(LED_RED, red.gpio, red.active);
|
if (nvs_item) {
|
||||||
|
if ((p = strcasestr(nvs_item, "green")) != NULL) green.pwm = atoi(strchr(p, '=') + 1);
|
||||||
|
if ((p = strcasestr(nvs_item, "red")) != NULL) red.pwm = atoi(strchr(p, '=') + 1);
|
||||||
|
free(nvs_item);
|
||||||
|
}
|
||||||
|
|
||||||
|
led_config(LED_GREEN, green.gpio, green.active, green.pwm);
|
||||||
|
led_config(LED_RED, red.gpio, red.active, red.pwm);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,9 +20,10 @@ enum { LED_GREEN = 0, LED_RED };
|
|||||||
#define led_blink(idx, on, off) led_blink_core(idx, on, off, false)
|
#define led_blink(idx, on, off) led_blink_core(idx, on, off, false)
|
||||||
#define led_blink_pushed(idx, on, off) led_blink_core(idx, on, off, true)
|
#define led_blink_pushed(idx, on, off) led_blink_core(idx, on, off, true)
|
||||||
|
|
||||||
bool led_config(int idx, gpio_num_t gpio, int onstate);
|
bool led_config(int idx, gpio_num_t gpio, int onstate, int pwm);
|
||||||
bool led_unconfig(int idx);
|
bool led_brightness(int idx, int percent);
|
||||||
bool led_blink_core(int idx, int ontime, int offtime, bool push);
|
bool led_blink_core(int idx, int ontime, int offtime, bool push);
|
||||||
bool led_unpush(int idx);
|
bool led_unpush(int idx);
|
||||||
|
int led_allocate(void);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -9,7 +9,8 @@
|
|||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include "esp_log.h"
|
#include "esp_log.h"
|
||||||
#include "driver/gpio.h"
|
#include "driver/gpio.h"
|
||||||
#include <driver/i2c.h>
|
#include "driver/ledc.h"
|
||||||
|
#include "driver/i2c.h"
|
||||||
#include "platform_config.h"
|
#include "platform_config.h"
|
||||||
#include "battery.h"
|
#include "battery.h"
|
||||||
#include "led.h"
|
#include "led.h"
|
||||||
@@ -26,6 +27,11 @@ int i2c_system_port = I2C_SYSTEM_PORT;
|
|||||||
int i2c_system_speed = 400000;
|
int i2c_system_speed = 400000;
|
||||||
int spi_system_host = SPI_SYSTEM_HOST;
|
int spi_system_host = SPI_SYSTEM_HOST;
|
||||||
int spi_system_dc_gpio = -1;
|
int spi_system_dc_gpio = -1;
|
||||||
|
pwm_system_t pwm_system = {
|
||||||
|
.timer = LEDC_TIMER_0,
|
||||||
|
.base_channel = LEDC_CHANNEL_0,
|
||||||
|
.max = (1 << LEDC_TIMER_13_BIT),
|
||||||
|
};
|
||||||
|
|
||||||
static const char *TAG = "services";
|
static const char *TAG = "services";
|
||||||
|
|
||||||
@@ -94,6 +100,16 @@ void services_init(void) {
|
|||||||
ESP_LOGW(TAG, "no SPI configured");
|
ESP_LOGW(TAG, "no SPI configured");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// system-wide PWM timer configuration
|
||||||
|
ledc_timer_config_t pwm_timer = {
|
||||||
|
.duty_resolution = LEDC_TIMER_13_BIT,
|
||||||
|
.freq_hz = 5000,
|
||||||
|
.speed_mode = LEDC_HIGH_SPEED_MODE,
|
||||||
|
.timer_num = pwm_system.timer,
|
||||||
|
};
|
||||||
|
|
||||||
|
ledc_timer_config(&pwm_timer);
|
||||||
|
|
||||||
led_svc_init();
|
led_svc_init();
|
||||||
battery_svc_init();
|
battery_svc_init();
|
||||||
monitor_svc_init();
|
monitor_svc_init();
|
||||||
|
|||||||
@@ -350,6 +350,9 @@ void register_default_nvs(){
|
|||||||
ESP_LOGD(TAG,"Registering default value for key %s, value %s", "set_GPIO", CONFIG_SET_GPIO);
|
ESP_LOGD(TAG,"Registering default value for key %s, value %s", "set_GPIO", CONFIG_SET_GPIO);
|
||||||
config_set_default(NVS_TYPE_STR, "set_GPIO", CONFIG_SET_GPIO, 0);
|
config_set_default(NVS_TYPE_STR, "set_GPIO", CONFIG_SET_GPIO, 0);
|
||||||
|
|
||||||
|
ESP_LOGD(TAG,"Registering default value for key %s", "led_brightness");
|
||||||
|
config_set_default(NVS_TYPE_STR, "led_brightness", "", 0);
|
||||||
|
|
||||||
ESP_LOGD(TAG,"Registering default value for key %s", "spdif_config");
|
ESP_LOGD(TAG,"Registering default value for key %s", "spdif_config");
|
||||||
config_set_default(NVS_TYPE_STR, "spdif_config", "", 0);
|
config_set_default(NVS_TYPE_STR, "spdif_config", "", 0);
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user