display refactor

This commit is contained in:
philippe44
2020-02-22 16:09:25 -08:00
parent 5e5a8241f3
commit a3d0b67670
43 changed files with 1561 additions and 1686 deletions

145
components/display/SH1106.c Normal file
View File

@@ -0,0 +1,145 @@
/**
* 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
static char TAG[] = "SH1106";
// Functions are not declared to minimize # of lines
static void SetColumnAddress( struct GDS_Device* Device, uint8_t Start, uint8_t End ) {
// well, unfortunately this driver is 132 colums but most displays are 128...
if (Device->Width != 132) Start += 2;
Device->WriteCommand( Device, 0x10 | (Start >> 4) );
Device->WriteCommand( Device, 0x00 | (Start & 0x0f) );
}
static void SetPageAddress( struct GDS_Device* Device, uint8_t Start, uint8_t End ) {
Device->WriteCommand( Device, 0xB0 | Start );
}
static void Update( struct GDS_Device* Device ) {
#ifdef SHADOW_BUFFER
// 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;
uint8_t *optr = Device->Shadowbuffer, *iptr = Device->Framebuffer;
// by row, find first and last columns that have been updated
for (int r = 0; r < rows; r++) {
uint8_t first = 0, last;
for (int c = 0; c < width; c++) {
if (*iptr != *optr) {
if (!first) first = c + 1;
last = c ;
}
*optr++ = *iptr++;
}
// now update the display by "byte rows"
if (first--) {
SetColumnAddress( Device, first, last );
SetPageAddress( Device, r, r);
Device->WriteData( Device, Device->Shadowbuffer + r*width + first, last - first + 1);
}
}
#else
// SH1106 requires a page-by-page update and has no end Page/Column
for (int i = 0; i < Device->Height / 8 ; i++) {
SH1106_SetPageAddress( Device, i, 0);
SH1106_SetColumnAddress( Device, 0, 0);
SH1106_WriteData( Device, Device->Framebuffer + i*Device->Width, Device->Width );
}
#endif
}
static void SetHFlip( struct GDS_Device* Device, bool On ) { Device->WriteCommand( Device, On ? 0xA1 : 0xA0 ); }
static void SetVFlip( struct GDS_Device *Device, bool On ) { Device->WriteCommand( Device, On ? 0xC8 : 0xC0 ); }
static void DisplayOn( struct GDS_Device* Device ) { Device->WriteCommand( Device, 0xAF ); }
static void DisplayOff( struct GDS_Device* Device ) { Device->WriteCommand( Device, 0xAE ); }
static void SetContrast( struct GDS_Device* Device, uint8_t Contrast ) {
Device->WriteCommand( Device, 0x81 );
Device->WriteCommand( Device, Contrast );
}
static bool Init( struct GDS_Device* Device ) {
Device->FramebufferSize = ( Device->Width * Device->Height ) / 8;
Device->Framebuffer = calloc( 1, Device->FramebufferSize );
NullCheck( Device->Framebuffer, return false );
#ifdef SHADOW_BUFFER
if (Device->IF == IF_I2C) Device->Shadowbuffer = malloc( Device->FramebufferSize );
else Device->Shadowbuffer = heap_caps_malloc( Device->FramebufferSize, MALLOC_CAP_INTERNAL | MALLOC_CAP_DMA );
NullCheck( Device->Shadowbuffer, return false );
memset(Device->Shadowbuffer, 0xFF, Device->FramebufferSize);
#endif
// need to be off and disable display RAM
Device->DisplayOff( Device );
Device->WriteCommand( Device, 0xA5 );
// charge pump regulator, do direct init
Device->WriteCommand( Device, 0xAD );
Device->WriteCommand( Device, 0x8B );
// COM pins HW config (alternative:EN) - some display might need something difference
Device->WriteCommand( Device, 0xDA );
Device->WriteCommand( Device, 1 << 4);
// MUX Ratio
Device->WriteCommand( Device, 0xA8 );
Device->WriteCommand( Device, Device->Height - 1);
// Display Offset
Device->WriteCommand( Device, 0xD3 );
Device->WriteCommand( Device, 0 );
// Display Start Line
Device->WriteCommand( Device, 0x40 + 0x00 );
Device->SetContrast( Device, 0x7F );
// set flip modes
Device->SetVFlip( Device, false );
Device->SetHFlip( Device, false );
// no Display Inversion
Device->WriteCommand( Device, 0xA6 );
// set Clocks
Device->WriteCommand( Device, 0xD5 );
Device->WriteCommand( Device, ( 0x08 << 4 ) | 0x00 );
// gone with the wind
Device->WriteCommand( Device, 0xA4 );
Device->DisplayOn( Device );
Device->Update( Device );
return true;
}
static const struct GDS_Device SH1106 = {
.DisplayOn = DisplayOn, .DisplayOff = DisplayOff, .SetContrast = SetContrast,
.SetVFlip = SetVFlip, .SetHFlip = SetHFlip,
.DrawPixel = GDS_DrawPixel, .DrawPixelFast = GDS_DrawPixelFast,
.Update = Update, .Init = Init,
};
struct GDS_Device* SH1106_Detect(char *Driver, struct GDS_Device* Device) {
if (!strcasestr(Driver, "SH1106")) return NULL;
if (!Device) Device = calloc(1, sizeof(struct GDS_Device));
*Device = SH1106;
ESP_LOGI(TAG, "SH1106 driver");
return Device;
}

View File

@@ -0,0 +1,146 @@
/**
* 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
static char TAG[] = "SSD1306";
// Functions are not deckared to minimize # of lines
static void SetColumnAddress( struct GDS_Device* Device, uint8_t Start, uint8_t End ) {
Device->WriteCommand( Device, 0x21 );
Device->WriteCommand( Device, Start );
Device->WriteCommand( Device, End );
}
static void SetPageAddress( struct GDS_Device* Device, uint8_t Start, uint8_t End ) {
Device->WriteCommand( Device, 0x22 );
Device->WriteCommand( Device, Start );
Device->WriteCommand( Device, End );
}
static void Update( struct GDS_Device* Device ) {
#ifdef SHADOW_BUFFER
// 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;
uint8_t *optr = Device->Shadowbuffer, *iptr = Device->Framebuffer;
// by row, find first and last columns that have been updated
for (int r = 0; r < rows; r++) {
uint8_t first = 0, last;
for (int c = 0; c < width; c++) {
if (*iptr != *optr) {
if (!first) first = c + 1;
last = c ;
}
*optr++ = *iptr++;
}
// now update the display by "byte rows"
if (first--) {
SetColumnAddress( Device, first, last );
SetPageAddress( Device, r, r);
Device->WriteData( Device, Device->Shadowbuffer + r*width + first, last - first + 1);
}
}
#else
// automatic counter and end Page/Column
SetColumnAddress( Device, 0, Device->Width - 1);
SetPageAddress( Device, 0, Device->Height / 8 - 1);
Device->WriteData( Device, Device->Framebuffer, Device->FramebufferSize );
#endif
}
static void SetHFlip( struct GDS_Device* Device, bool On ) { Device->WriteCommand( Device, On ? 0xA1 : 0xA0 ); }
static void SetVFlip( struct GDS_Device *Device, bool On ) { Device->WriteCommand( Device, On ? 0xC8 : 0xC0 ); }
static void DisplayOn( struct GDS_Device* Device ) { Device->WriteCommand( Device, 0xAF ); }
static void DisplayOff( struct GDS_Device* Device ) { Device->WriteCommand( Device, 0xAE ); }
static void SetContrast( struct GDS_Device* Device, uint8_t Contrast ) {
Device->WriteCommand( Device, 0x81 );
Device->WriteCommand( Device, Contrast );
}
static bool Init( struct GDS_Device* Device ) {
Device->FramebufferSize = ( Device->Width * Device->Height ) / 8;
Device->Framebuffer = calloc( 1, Device->FramebufferSize );
NullCheck( Device->Framebuffer, return false );
#ifdef SHADOW_BUFFER
if (Device->IF == IF_I2C) Device->Shadowbuffer = malloc( Device->FramebufferSize );
else Device->Shadowbuffer = heap_caps_malloc( Device->FramebufferSize, MALLOC_CAP_INTERNAL | MALLOC_CAP_DMA );
NullCheck( Device->Shadowbuffer, return false );
memset(Device->Shadowbuffer, 0xFF, Device->FramebufferSize);
#endif
// need to be off and disable display RAM
Device->DisplayOff( Device );
Device->WriteCommand( Device, 0xA5 );
// charge pump regulator, do direct init
Device->WriteCommand( Device, 0x8D );
Device->WriteCommand( Device, 0x14 );
// COM pins HW config (alternative:EN if 64, DIS if 32, remap:DIS) - some display might need something difference
Device->WriteCommand( Device, 0xDA );
Device->WriteCommand( Device, ((Device->Height == 64 ? 1 : 0) << 4) | (0 < 5) );
// MUX Ratio
Device->WriteCommand( Device, 0xA8 );
Device->WriteCommand( Device, Device->Height - 1);
// Display Offset
Device->WriteCommand( Device, 0xD3 );
Device->WriteCommand( Device, 0 );
// Display Start Line
Device->WriteCommand( Device, 0x40 + 0x00 );
Device->SetContrast( Device, 0x7F );
// set flip modes
Device->SetVFlip( Device, false );
Device->SetHFlip( Device, false );
// no Display Inversion
Device->WriteCommand( Device, 0xA6 );
// set Clocks
Device->WriteCommand( Device, 0xD5 );
Device->WriteCommand( Device, ( 0x08 << 4 ) | 0x00 );
// set Adressing Mode Horizontal
Device->WriteCommand( Device, 0x20 );
Device->WriteCommand( Device, 0 );
// gone with the wind
Device->WriteCommand( Device, 0xA4 );
Device->DisplayOn( Device );
Device->Update( Device );
return true;
}
static const struct GDS_Device SSD1306 = {
.DisplayOn = DisplayOn, .DisplayOff = DisplayOff, .SetContrast = SetContrast,
.SetVFlip = SetVFlip, .SetHFlip = SetHFlip,
.DrawPixel = GDS_DrawPixel, .DrawPixelFast = GDS_DrawPixelFast,
.Update = Update, .Init = Init,
};
struct GDS_Device* SSD1306_Detect(char *Driver, struct GDS_Device* Device) {
if (!strcasestr(Driver, "SSD1306")) return NULL;
if (!Device) Device = calloc(1, sizeof(struct GDS_Device));
*Device = SSD1306;
ESP_LOGI(TAG, "SSD1306 driver");
return Device;
}

View File

@@ -7,6 +7,6 @@
# please read the SDK documents if you need to do this.
#
COMPONENT_SRCDIRS := . tarablessd13x6 tarablessd13x6/fonts tarablessd13x6/ifaces
COMPONENT_SRCDIRS := . core core/ifaces fonts
COMPONENT_ADD_INCLUDEDIRS := .
COMPONENT_ADD_INCLUDEDIRS += ./tarablessd13x6
COMPONENT_ADD_INCLUDEDIRS += ./core

View File

@@ -0,0 +1,92 @@
/**
* Copyright (c) 2017-2018 Tara Keeling
* 2020 Philippe G.
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#include <string.h>
#include <ctype.h>
#include <stdint.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 struct GDS_Device Display;
static char TAG[] = "gds";
struct GDS_Device* GDS_AutoDetect( char *Driver, GDS_DetectFunc DetectFunc[] ) {
for (int i = 0; DetectFunc[i]; i++) {
if (DetectFunc[i](Driver, &Display)) {
ESP_LOGD(TAG, "Detected driver %p", &Display);
return &Display;
}
}
return NULL;
}
void GDS_ClearExt(struct GDS_Device* Device, bool full, ...) {
bool commit = true;
if (full) {
GDS_Clear( Device, GDS_COLOR_BLACK );
} else {
va_list args;
va_start(args, full);
commit = va_arg(args, int);
int x1 = va_arg(args, int), y1 = va_arg(args, int), x2 = va_arg(args, int), y2 = va_arg(args, int);
if (x2 < 0) x2 = Device->Width - 1;
if (y2 < 0) y2 = Device->Height - 1;
GDS_ClearWindow( Device, x1, y1, x2, y2, GDS_COLOR_BLACK );
va_end(args);
}
Device->Dirty = true;
if (commit) GDS_Update(Device);
}
void GDS_Clear( struct GDS_Device* Device, int Color ) {
memset( Device->Framebuffer, Color, Device->FramebufferSize );
}
void GDS_ClearWindow( struct GDS_Device* Device, int x1, int y1, int x2, int y2, int Color ) {
// cheap optimization on boundaries
if (x1 == 0 && x2 == Device->Width - 1 && y1 % 8 == 0 && (y2 + 1) % 8 == 0) {
memset( Device->Framebuffer + (y1 / 8) * Device->Width, 0, (y2 - y1 + 1) / 8 * Device->Width );
} else {
for (int y = y1; y <= y2; y++) {
for (int x = x1; x <= x2; x++) {
Device->DrawPixelFast( Device, x, y, Color);
}
}
}
}
void GDS_Update( struct GDS_Device* Device ) {
if (Device->Dirty) Device->Update( Device );
Device->Dirty = false;
}
bool GDS_Reset( struct GDS_Device* Device ) {
if ( Device->RSTPin >= 0 ) {
gpio_set_level( Device->RSTPin, 0 );
vTaskDelay( pdMS_TO_TICKS( 100 ) );
gpio_set_level( Device->RSTPin, 1 );
}
return true;
}
void GDS_SetContrast( struct GDS_Device* Device, uint8_t Contrast ) { Device->SetContrast( Device, Contrast); }
void GDS_SetHFlip( struct GDS_Device* Device, bool On ) { Device->SetHFlip( Device, On ); }
void GDS_SetVFlip( struct GDS_Device* Device, bool On ) { Device->SetVFlip( Device, On ); }
int GDS_GetWidth( struct GDS_Device* Device ) { return Device->Width; }
int GDS_GetHeight( struct GDS_Device* Device ) { return Device->Height; }
void GDS_DisplayOn( struct GDS_Device* Device ) { Device->DisplayOn( Device ); }
void GDS_DisplayOff( struct GDS_Device* Device ) { Device->DisplayOff( Device ); }

View File

@@ -0,0 +1,30 @@
#ifndef _GDS_H_
#define _GDS_H_
#include <stdint.h>
#include <stdbool.h>
#define GDS_COLOR_BLACK 0
#define GDS_COLOR_WHITE 1
#define GDS_COLOR_XOR 2
struct GDS_Device;
struct GDS_FontDef;
typedef struct GDS_Device* (*GDS_DetectFunc)(char *Driver, struct GDS_Device *Device);
struct GDS_Device* GDS_AutoDetect( char *Driver, GDS_DetectFunc[] );
void GDS_SetContrast( struct GDS_Device* Device, uint8_t Contrast );
void GDS_DisplayOn( struct GDS_Device* Device );
void GDS_DisplayOff( struct GDS_Device* Device );
void GDS_Update( struct GDS_Device* Device );
void GDS_SetHFlip( struct GDS_Device* Device, bool On );
void GDS_SetVFlip( struct GDS_Device* Device, bool On );
int GDS_GetWidth( struct GDS_Device* Device );
int GDS_GetHeight( struct GDS_Device* Device );
void GDS_ClearExt( struct GDS_Device* Device, bool full, ...);
void GDS_Clear( struct GDS_Device* Device, int Color );
void GDS_ClearWindow( struct GDS_Device* Device, int x1, int y1, int x2, int y2, int Color );
#endif

View File

@@ -0,0 +1,20 @@
#ifndef _GDS_DEFAULT_IF_H_
#define _GDS_DEFAULT_IF_H_
#ifdef __cplusplus
extern "C" {
#endif
struct GDS_Device;
bool GDS_I2CInit( int PortNumber, int SDA, int SCL );
bool GDS_I2CAttachDevice( struct GDS_Device* Device, int Width, int Height, int I2CAddress, int RSTPin );
bool GDS_SPIInit( int SPI, int DC );
bool GDS_SPIAttachDevice( struct GDS_Device* Device, int Width, int Height, int CSPin, int RSTPin, int Speed );
#ifdef __cplusplus
}
#endif
#endif

View File

@@ -0,0 +1,321 @@
/**
* 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 <stdlib.h>
#include <math.h>
#include <esp_attr.h>
#include "gds.h"
#include "gds_private.h"
#include "gds_draw.h"
static const unsigned char BitReverseTable256[] =
{
0x00, 0x80, 0x40, 0xC0, 0x20, 0xA0, 0x60, 0xE0, 0x10, 0x90, 0x50, 0xD0, 0x30, 0xB0, 0x70, 0xF0,
0x08, 0x88, 0x48, 0xC8, 0x28, 0xA8, 0x68, 0xE8, 0x18, 0x98, 0x58, 0xD8, 0x38, 0xB8, 0x78, 0xF8,
0x04, 0x84, 0x44, 0xC4, 0x24, 0xA4, 0x64, 0xE4, 0x14, 0x94, 0x54, 0xD4, 0x34, 0xB4, 0x74, 0xF4,
0x0C, 0x8C, 0x4C, 0xCC, 0x2C, 0xAC, 0x6C, 0xEC, 0x1C, 0x9C, 0x5C, 0xDC, 0x3C, 0xBC, 0x7C, 0xFC,
0x02, 0x82, 0x42, 0xC2, 0x22, 0xA2, 0x62, 0xE2, 0x12, 0x92, 0x52, 0xD2, 0x32, 0xB2, 0x72, 0xF2,
0x0A, 0x8A, 0x4A, 0xCA, 0x2A, 0xAA, 0x6A, 0xEA, 0x1A, 0x9A, 0x5A, 0xDA, 0x3A, 0xBA, 0x7A, 0xFA,
0x06, 0x86, 0x46, 0xC6, 0x26, 0xA6, 0x66, 0xE6, 0x16, 0x96, 0x56, 0xD6, 0x36, 0xB6, 0x76, 0xF6,
0x0E, 0x8E, 0x4E, 0xCE, 0x2E, 0xAE, 0x6E, 0xEE, 0x1E, 0x9E, 0x5E, 0xDE, 0x3E, 0xBE, 0x7E, 0xFE,
0x01, 0x81, 0x41, 0xC1, 0x21, 0xA1, 0x61, 0xE1, 0x11, 0x91, 0x51, 0xD1, 0x31, 0xB1, 0x71, 0xF1,
0x09, 0x89, 0x49, 0xC9, 0x29, 0xA9, 0x69, 0xE9, 0x19, 0x99, 0x59, 0xD9, 0x39, 0xB9, 0x79, 0xF9,
0x05, 0x85, 0x45, 0xC5, 0x25, 0xA5, 0x65, 0xE5, 0x15, 0x95, 0x55, 0xD5, 0x35, 0xB5, 0x75, 0xF5,
0x0D, 0x8D, 0x4D, 0xCD, 0x2D, 0xAD, 0x6D, 0xED, 0x1D, 0x9D, 0x5D, 0xDD, 0x3D, 0xBD, 0x7D, 0xFD,
0x03, 0x83, 0x43, 0xC3, 0x23, 0xA3, 0x63, 0xE3, 0x13, 0x93, 0x53, 0xD3, 0x33, 0xB3, 0x73, 0xF3,
0x0B, 0x8B, 0x4B, 0xCB, 0x2B, 0xAB, 0x6B, 0xEB, 0x1B, 0x9B, 0x5B, 0xDB, 0x3B, 0xBB, 0x7B, 0xFB,
0x07, 0x87, 0x47, 0xC7, 0x27, 0xA7, 0x67, 0xE7, 0x17, 0x97, 0x57, 0xD7, 0x37, 0xB7, 0x77, 0xF7,
0x0F, 0x8F, 0x4F, 0xCF, 0x2F, 0xAF, 0x6F, 0xEF, 0x1F, 0x9F, 0x5F, 0xDF, 0x3F, 0xBF, 0x7F, 0xFF
};
__attribute__( ( always_inline ) ) static inline bool IsPixelVisible( struct GDS_Device* Device, int x, int y ) {
bool Result = (
( x >= 0 ) &&
( x < Device->Width ) &&
( y >= 0 ) &&
( y < Device->Height )
) ? true : false;
#if CONFIG_GDS_CLIPDEBUG > 0
if ( Result == false ) {
ClipDebug( x, y );
}
#endif
return Result;
}
__attribute__( ( always_inline ) ) static inline void SwapInt( int* a, int* b ) {
int Temp = *b;
*b = *a;
*a = Temp;
}
inline void IRAM_ATTR GDS_DrawPixelFast( struct GDS_Device* Device, int X, int Y, int Color ) {
uint32_t YBit = ( Y & 0x07 );
uint8_t* FBOffset = NULL;
/*
* We only need to modify the Y coordinate since the pitch
* of the screen is the same as the width.
* Dividing Y by 8 gives us which row the pixel is in but not
* the bit position.
*/
Y>>= 3;
FBOffset = Device->Framebuffer + ( ( Y * Device->Width ) + X );
if ( Color == GDS_COLOR_XOR ) {
*FBOffset ^= BIT( YBit );
} else {
*FBOffset = ( Color == GDS_COLOR_WHITE ) ? *FBOffset | BIT( YBit ) : *FBOffset & ~BIT( YBit );
}
}
void IRAM_ATTR GDS_DrawPixel( struct GDS_Device* Device, int x, int y, int Color ) {
if ( IsPixelVisible( Device, x, y ) == true ) {
Device->DrawPixelFast( Device, x, y, Color );
}
}
inline void IRAM_ATTR GDS_DrawPixel4Fast( struct GDS_Device* Device, int X, int Y, int Color ) {
uint32_t YBit = ( Y & 0x07 );
uint8_t* FBOffset = NULL;
/*
* We only need to modify the Y coordinate since the pitch
* of the screen is the same as the width.
* Dividing Y by 8 gives us which row the pixel is in but not
* the bit position.
*/
Y>>= 3;
FBOffset = Device->Framebuffer + ( ( Y * Device->Width ) + X );
if ( Color == GDS_COLOR_XOR ) {
*FBOffset ^= BIT( YBit );
} else {
*FBOffset = ( Color == GDS_COLOR_WHITE ) ? *FBOffset | BIT( YBit ) : *FBOffset & ~BIT( YBit );
}
}
void IRAM_ATTR GDS_DrawPixel4( struct GDS_Device* Device, int x, int y, int Color ) {
if ( IsPixelVisible( Device, x, y ) == true ) {
Device->DrawPixelFast( Device, x, y, Color );
}
}
void IRAM_ATTR GDS_DrawHLine( struct GDS_Device* Device, int x, int y, int Width, int Color ) {
int XEnd = x + Width;
Device->Dirty = true;
if (x < 0) x = 0;
if (XEnd >= Device->Width) XEnd = Device->Width - 1;
if (y < 0) y = 0;
else if (y >= Device->Height) x = Device->Height - 1;
for ( ; x < XEnd; x++ ) {
// if ( IsPixelVisible( Device, x, y ) == true ) {
Device->DrawPixelFast( Device, x, y, Color );
// } else {
// break;
// }
}
}
void IRAM_ATTR GDS_DrawVLine( struct GDS_Device* Device, int x, int y, int Height, int Color ) {
int YEnd = y + Height;
Device->Dirty = true;
for ( ; y < YEnd; y++ ) {
if ( IsPixelVisible( Device, x, y ) == true ) {
Device->DrawPixel( Device, x, y, Color );
} else {
break;
}
}
}
static inline void IRAM_ATTR DrawWideLine( struct GDS_Device* Device, int x0, int y0, int x1, int y1, int Color ) {
int dx = ( x1 - x0 );
int dy = ( y1 - y0 );
int Error = 0;
int Incr = 1;
int x = x0;
int y = y0;
if ( dy < 0 ) {
Incr = -1;
dy = -dy;
}
Error = ( dy * 2 ) - dx;
for ( ; x < x1; x++ ) {
if ( IsPixelVisible( Device, x, y ) == true ) {
Device->DrawPixelFast( Device, x, y, Color );
}
if ( Error > 0 ) {
Error-= ( dx * 2 );
y+= Incr;
}
Error+= ( dy * 2 );
}
}
static inline void IRAM_ATTR DrawTallLine( struct GDS_Device* Device, int x0, int y0, int x1, int y1, int Color ) {
int dx = ( x1 - x0 );
int dy = ( y1 - y0 );
int Error = 0;
int Incr = 1;
int x = x0;
int y = y0;
if ( dx < 0 ) {
Incr = -1;
dx = -dx;
}
Error = ( dx * 2 ) - dy;
for ( ; y < y1; y++ ) {
if ( IsPixelVisible( Device, x, y ) == true ) {
Device->DrawPixelFast( Device, x, y, Color );
}
if ( Error > 0 ) {
Error-= ( dy * 2 );
x+= Incr;
}
Error+= ( dx * 2 );
}
}
void IRAM_ATTR GDS_DrawLine( struct GDS_Device* Device, int x0, int y0, int x1, int y1, int Color ) {
if ( x0 == x1 ) {
GDS_DrawVLine( Device, x0, y0, ( y1 - y0 ), Color );
} else if ( y0 == y1 ) {
GDS_DrawHLine( Device, x0, y0, ( x1 - x0 ), Color );
} else {
Device->Dirty = true;
if ( abs( x1 - x0 ) > abs( y1 - y0 ) ) {
/* Wide ( run > rise ) */
if ( x0 > x1 ) {
SwapInt( &x0, &x1 );
SwapInt( &y0, &y1 );
}
DrawWideLine( Device, x0, y0, x1, y1, Color );
} else {
/* Tall ( rise > run ) */
if ( y0 > y1 ) {
SwapInt( &y0, &y1 );
SwapInt( &x0, &x1 );
}
DrawTallLine( Device, x0, y0, x1, y1, Color );
}
}
}
void IRAM_ATTR GDS_DrawBox( struct GDS_Device* Device, int x1, int y1, int x2, int y2, int Color, bool Fill ) {
int Width = ( x2 - x1 );
int Height = ( y2 - y1 );
Device->Dirty = true;
if ( Fill == false ) {
/* Top side */
GDS_DrawHLine( Device, x1, y1, Width, Color );
/* Bottom side */
GDS_DrawHLine( Device, x1, y1 + Height, Width, Color );
/* Left side */
GDS_DrawVLine( Device, x1, y1, Height, Color );
/* Right side */
GDS_DrawVLine( Device, x1 + Width, y1, Height, Color );
} else {
/* Fill the box by drawing horizontal lines */
for ( ; y1 <= y2; y1++ ) {
GDS_DrawHLine( Device, x1, y1, Width, Color );
}
}
}
/****************************************************************************************
* Process graphic display data from column-oriented data (MSbit first)
*/
void GDS_DrawBitmapCBR(struct GDS_Device* Device, uint8_t *Data, int Width, int Height) {
if (!Height) Height = Device->Height;
if (!Width) Width = Device->Width;
// need to do row/col swap and bit-reverse
int Rows = Height / 8;
for (int r = 0; r < Rows; r++) {
uint8_t *optr = Device->Framebuffer + r*Device->Width, *iptr = Data + r;
for (int c = Width; --c >= 0;) {
*optr++ = BitReverseTable256[*iptr];;
iptr += Rows;
}
}
Device->Dirty = true;
}
/****************************************************************************************
* Process graphic display data MSBit first
* WARNING: this has not been tested yet
*/
/*
static void draw_raw(int x1, int y1, int x2, int y2, bool by_column, bool MSb, u8_t *data) {
// default end point to display size
if (x2 == -1) x2 = Display.Width - 1;
if (y2 == -1) y2 = Display.Height - 1;
display->dirty = true;
// not a boundary draw
// same comment about bit depth
if (y1 % 8 || y2 % 8 || x1 % 8 | x2 % 8) {
ESP_LOGW(TAG, "can't write on non cols/rows boundaries for now");
} else {
// set addressing mode to match data
if (by_column) {
// copy the window and do row/col exchange
for (int r = y1/8; r <= y2/8; r++) {
uint8_t *optr = Display.Framebuffer + r*Display.Width + x1, *iptr = data + r;
for (int c = x1; c <= x2; c++) {
*optr++ = MSb ? BitReverseTable256[*iptr] : *iptr;
iptr += (y2-y1)/8 + 1;
}
}
} else {
// just copy the window inside the frame buffer
for (int r = y1/8; r <= y2/8; r++) {
uint8_t *optr = Display.Framebuffer + r*Display.Width + x1, *iptr = data + r*(x2-x1+1);
for (int c = x1; c <= x2; c++) *optr++ = *iptr++;
}
}
}
}
*/

View File

@@ -0,0 +1,26 @@
#ifndef _GDS_DRAW_H_
#define _GDS_DRAW_H_
#include <stdint.h>
#include <stdbool.h>
#include "esp_attr.h"
#ifdef __cplusplus
extern "C" {
#endif
struct GDS_Device;
void IRAM_ATTR GDS_DrawHLine( struct GDS_Device* Device, int x, int y, int Width, int Color );
void IRAM_ATTR GDS_DrawVLine( struct GDS_Device* Device, int x, int y, int Height, int Color );
void IRAM_ATTR GDS_DrawLine( struct GDS_Device* Device, int x0, int y0, int x1, int y1, int Color );
void IRAM_ATTR GDS_DrawBox( struct GDS_Device* Device, int x1, int y1, int x2, int y2, int Color, bool Fill );
// draw a bitmap with source 1-bit depth organized in column and col0 = bit7 of byte 0
void IRAM_ATTR GDS_DrawBitmapCBR( struct GDS_Device* Device, uint8_t *Data, int Width, int Height);
#ifdef __cplusplus
}
#endif
#endif

View File

@@ -1,15 +1,15 @@
#ifndef _SSD13x6_ERR_H_
#define _SSD13x6_ERR_H_
#ifndef _GDS_ERR_H_
#define _GDS_ERR_H_
#include <esp_log.h>
#define SSD13x6_DoAbort( )
#define GDS_DoAbort( )
#if ! defined NullCheck
#define NullCheck( ptr, retexpr ) { \
if ( ptr == NULL ) { \
ESP_LOGE( __FUNCTION__, "%s == NULL", #ptr ); \
SSD13x6_DoAbort( ); \
GDS_DoAbort( ); \
retexpr; \
} \
}
@@ -20,7 +20,7 @@
esp_err_t __err_rc = ( expr ); \
if ( __err_rc != ESP_OK ) { \
ESP_LOGE( __FUNCTION__, "%s != ESP_OK, result: %d", #expr, __err_rc ); \
SSD13x6_DoAbort( ); \
GDS_DoAbort( ); \
retexpr; \
} \
}
@@ -30,7 +30,7 @@
#define CheckBounds( expr, retexpr ) { \
if ( expr ) { \
ESP_LOGE( __FUNCTION__, "Line %d: %s", __LINE__, #expr ); \
SSD13x6_DoAbort( ); \
GDS_DoAbort( ); \
retexpr; \
} \
}

View File

@@ -9,11 +9,13 @@
#include <string.h>
#include <stdint.h>
#include <math.h>
#include "ssd13x6.h"
#include "ssd13x6_draw.h"
#include "ssd13x6_font.h"
#include "gds.h"
#include "gds_draw.h"
#include "gds_font.h"
#include "gds_private.h"
#include "gds_err.h"
static int RoundUpFontHeight( const struct SSD13x6_FontDef* Font ) {
static int RoundUpFontHeight( const struct GDS_FontDef* Font ) {
int Height = Font->Height;
if ( ( Height % 8 ) != 0 ) {
@@ -23,11 +25,11 @@ static int RoundUpFontHeight( const struct SSD13x6_FontDef* Font ) {
return Height;
}
static const uint8_t* GetCharPtr( const struct SSD13x6_FontDef* Font, char Character ) {
static const uint8_t* GetCharPtr( const struct GDS_FontDef* Font, char Character ) {
return &Font->FontData[ ( Character - Font->StartChar ) * ( ( Font->Width * ( RoundUpFontHeight( Font ) / 8 ) ) + 1 ) ];
}
void SSD13x6_FontDrawChar( struct SSD13x6_Device* DisplayHandle, char Character, int x, int y, int Color ) {
void GDS_FontDrawChar( struct GDS_Device* Device, char Character, int x, int y, int Color ) {
const uint8_t* GlyphData = NULL;
int GlyphColumnLen = 0;
int CharStartX = 0;
@@ -42,18 +44,15 @@ void SSD13x6_FontDrawChar( struct SSD13x6_Device* DisplayHandle, char Character,
int YBit = 0;
int i = 0;
NullCheck( DisplayHandle, return );
NullCheck( DisplayHandle->Font, return );
NullCheck( ( GlyphData = GetCharPtr( DisplayHandle->Font, Character ) ), return );
NullCheck( ( GlyphData = GetCharPtr( Device->Font, Character ) ), return );
if ( Character >= DisplayHandle->Font->StartChar && Character <= DisplayHandle->Font->EndChar ) {
if ( Character >= Device->Font->StartChar && Character <= Device->Font->EndChar ) {
/* The first byte in the glyph data is the width of the character in pixels, skip over */
GlyphData++;
GlyphColumnLen = RoundUpFontHeight( DisplayHandle->Font ) / 8;
GlyphColumnLen = RoundUpFontHeight( Device->Font ) / 8;
CharWidth = SSD13x6_FontGetCharWidth( DisplayHandle, Character );
CharHeight = SSD13x6_FontGetHeight( DisplayHandle );
CharWidth = GDS_FontGetCharWidth( Device, Character );
CharHeight = GDS_FontGetHeight( Device );
CharStartX = x;
CharStartY = y;
@@ -74,14 +73,15 @@ void SSD13x6_FontDrawChar( struct SSD13x6_Device* DisplayHandle, char Character,
CharStartY+= OffsetY;
/* Do not attempt to draw if this character is entirely offscreen */
if ( CharEndX < 0 || CharStartX >= DisplayHandle->Width || CharEndY < 0 || CharStartY >= DisplayHandle->Height ) {
if ( CharEndX < 0 || CharStartX >= Device->Width || CharEndY < 0 || CharStartY >= Device->Height ) {
ClipDebug( x, y );
return;
}
/* Do not attempt to draw past the end of the screen */
CharEndX = ( CharEndX >= DisplayHandle->Width ) ? DisplayHandle->Width - 1 : CharEndX;
CharEndY = ( CharEndY >= DisplayHandle->Height ) ? DisplayHandle->Height - 1 : CharEndY;
CharEndX = ( CharEndX >= Device->Width ) ? Device->Width - 1 : CharEndX;
CharEndY = ( CharEndY >= Device->Height ) ? Device->Height - 1 : CharEndY;
Device->Dirty = true;
for ( x = CharStartX; x < CharEndX; x++ ) {
for ( y = CharStartY, i = 0; y < CharEndY && i < CharHeight; y++, i++ ) {
@@ -89,7 +89,7 @@ void SSD13x6_FontDrawChar( struct SSD13x6_Device* DisplayHandle, char Character,
YBit = ( i + OffsetY ) & 0x07;
if ( GlyphData[ YByte ] & BIT( YBit ) ) {
SSD13x6_DrawPixel( DisplayHandle, x, y, Color );
Device->DrawPixel( Device, x, y, Color );
}
}
@@ -98,10 +98,7 @@ void SSD13x6_FontDrawChar( struct SSD13x6_Device* DisplayHandle, char Character,
}
}
bool SSD13x6_SetFont( struct SSD13x6_Device* Display, const struct SSD13x6_FontDef* Font ) {
NullCheck( Display, return false );
NullCheck( Font, return false );
bool GDS_SetFont( struct GDS_Device* Display, const struct GDS_FontDef* Font ) {
Display->FontForceProportional = false;
Display->FontForceMonospace = false;
Display->Font = Font;
@@ -109,41 +106,26 @@ bool SSD13x6_SetFont( struct SSD13x6_Device* Display, const struct SSD13x6_FontD
return true;
}
void SSD13x6_FontForceProportional( struct SSD13x6_Device* Display, bool Force ) {
NullCheck( Display, return );
NullCheck( Display->Font, return );
void GDS_FontForceProportional( struct GDS_Device* Display, bool Force ) {
Display->FontForceProportional = Force;
}
void SSD13x6_FontForceMonospace( struct SSD13x6_Device* Display, bool Force ) {
NullCheck( Display, return );
NullCheck( Display->Font, return );
void GDS_FontForceMonospace( struct GDS_Device* Display, bool Force ) {
Display->FontForceMonospace = Force;
}
int SSD13x6_FontGetWidth( struct SSD13x6_Device* Display ) {
NullCheck( Display, return 0 );
NullCheck( Display->Font, return 0 );
int GDS_FontGetWidth( struct GDS_Device* Display ) {
return Display->Font->Width;
}
int SSD13x6_FontGetHeight( struct SSD13x6_Device* Display ) {
NullCheck( Display, return 0 );
NullCheck( Display->Font, return 0 );
int GDS_FontGetHeight( struct GDS_Device* Display ) {
return Display->Font->Height;
}
int SSD13x6_FontGetCharWidth( struct SSD13x6_Device* Display, char Character ) {
int GDS_FontGetCharWidth( struct GDS_Device* Display, char Character ) {
const uint8_t* CharPtr = NULL;
int Width = 0;
NullCheck( Display, return 0 );
NullCheck( Display->Font, return 0 );
if ( Character >= Display->Font->StartChar && Character <= Display->Font->EndChar ) {
CharPtr = GetCharPtr( Display->Font, Character );
@@ -161,82 +143,67 @@ int SSD13x6_FontGetCharWidth( struct SSD13x6_Device* Display, char Character ) {
return Width;
}
int SSD13x6_FontGetMaxCharsPerRow( struct SSD13x6_Device* Display ) {
NullCheck( Display, return 0 );
NullCheck( Display->Font, return 0 );
int GDS_FontGetMaxCharsPerRow( struct GDS_Device* Display ) {
return Display->Width / Display->Font->Width;
}
int SSD13x6_FontGetMaxCharsPerColumn( struct SSD13x6_Device* Display ) {
NullCheck( Display, return 0 );
NullCheck( Display->Font, return 0 );
int GDS_FontGetMaxCharsPerColumn( struct GDS_Device* Display ) {
return Display->Height / Display->Font->Height;
}
int SSD13x6_FontGetCharHeight( struct SSD13x6_Device* Display ) {
NullCheck( Display, return 0 );
NullCheck( Display->Font, return 0 );
int GDS_FontGetCharHeight( struct GDS_Device* Display ) {
return Display->Font->Height;
}
int SSD13x6_FontMeasureString( struct SSD13x6_Device* Display, const char* Text ) {
int GDS_FontMeasureString( struct GDS_Device* Display, const char* Text ) {
int Width = 0;
int Len = 0;
NullCheck( Display, return 0 );
NullCheck( Display->Font, return 0 );
NullCheck( Text, return 0 );
for ( Len = strlen( Text ); Len >= 0; Len--, Text++ ) {
if ( *Text >= Display->Font->StartChar && *Text <= Display->Font->EndChar ) {
Width+= SSD13x6_FontGetCharWidth( Display, *Text );
Width+= GDS_FontGetCharWidth( Display, *Text );
}
}
return Width;
}
void SSD13x6_FontDrawString( struct SSD13x6_Device* Display, int x, int y, const char* Text, int Color ) {
void GDS_FontDrawString( struct GDS_Device* Display, int x, int y, const char* Text, int Color ) {
int Len = 0;
int i = 0;
NullCheck( Display, return );
NullCheck( Display->Font, return );
NullCheck( Text, return );
for ( Len = strlen( Text ), i = 0; i < Len; i++ ) {
SSD13x6_FontDrawChar( Display, *Text, x, y, Color );
GDS_FontDrawChar( Display, *Text, x, y, Color );
x+= SSD13x6_FontGetCharWidth( Display, *Text );
x+= GDS_FontGetCharWidth( Display, *Text );
Text++;
}
}
void SSD13x6_FontDrawAnchoredString( struct SSD13x6_Device* Display, TextAnchor Anchor, const char* Text, int Color ) {
void GDS_FontDrawAnchoredString( struct GDS_Device* Display, TextAnchor Anchor, const char* Text, int Color ) {
int x = 0;
int y = 0;
NullCheck( Display, return );
NullCheck( Text, return );
SSD13x6_FontGetAnchoredStringCoords( Display, &x, &y, Anchor, Text );
SSD13x6_FontDrawString( Display, x, y, Text, Color );
GDS_FontGetAnchoredStringCoords( Display, &x, &y, Anchor, Text );
GDS_FontDrawString( Display, x, y, Text, Color );
}
void SSD13x6_FontGetAnchoredStringCoords( struct SSD13x6_Device* Display, int* OutX, int* OutY, TextAnchor Anchor, const char* Text ) {
void GDS_FontGetAnchoredStringCoords( struct GDS_Device* Display, int* OutX, int* OutY, TextAnchor Anchor, const char* Text ) {
int StringWidth = 0;
int StringHeight = 0;
NullCheck( Display, return );
NullCheck( OutX, return );
NullCheck( OutY, return );
NullCheck( Text, return );
StringWidth = SSD13x6_FontMeasureString( Display, Text );
StringHeight = SSD13x6_FontGetCharHeight( Display );
StringWidth = GDS_FontMeasureString( Display, Text );
StringHeight = GDS_FontGetCharHeight( Display );
switch ( Anchor ) {
case TextAnchor_East: {

View File

@@ -0,0 +1,91 @@
#ifndef _GDS_FONT_H_
#define _GDS_FONT_H_
#include <stdint.h>
#include <stdbool.h>
#ifdef __cplusplus
extern "C" {
#endif
struct GDS_Device;
/*
* X-GLCD Font format:
*
* First byte of glyph is it's width in pixels.
* Each data byte represents 8 pixels going down from top to bottom.
*
* Example glyph layout for a 16x16 font
* 'a': [Glyph width][Pixel column 0][Pixel column 1] where the number of pixel columns is the font height divided by 8
* 'b': [Glyph width][Pixel column 0][Pixel column 1]...
* 'c': And so on...
*/
struct GDS_FontDef {
const uint8_t* FontData;
int Width;
int Height;
int StartChar;
int EndChar;
bool Monospace;
};
typedef enum {
TextAnchor_East = 0,
TextAnchor_West,
TextAnchor_North,
TextAnchor_South,
TextAnchor_NorthEast,
TextAnchor_NorthWest,
TextAnchor_SouthEast,
TextAnchor_SouthWest,
TextAnchor_Center
} TextAnchor;
bool GDS_SetFont( struct GDS_Device* Display, const struct GDS_FontDef* Font );
void GDS_FontForceProportional( struct GDS_Device* Display, bool Force );
void GDS_FontForceMonospace( struct GDS_Device* Display, bool Force );
int GDS_FontGetWidth( struct GDS_Device* Display );
int GDS_FontGetHeight( struct GDS_Device* Display );
int GDS_FontGetMaxCharsPerRow( struct GDS_Device* Display );
int GDS_FontGetMaxCharsPerColumn( struct GDS_Device* Display );
int GDS_FontGetCharWidth( struct GDS_Device* Display, char Character );
int GDS_FontGetCharHeight( struct GDS_Device* Display );
int GDS_FontMeasureString( struct GDS_Device* Display, const char* Text );\
void GDS_FontDrawChar( struct GDS_Device* Display, char Character, int x, int y, int Color );
void GDS_FontDrawString( struct GDS_Device* Display, int x, int y, const char* Text, int Color );
void GDS_FontDrawAnchoredString( struct GDS_Device* Display, TextAnchor Anchor, const char* Text, int Color );
void GDS_FontGetAnchoredStringCoords( struct GDS_Device* Display, int* OutX, int* OutY, TextAnchor Anchor, const char* Text );
extern const struct GDS_FontDef Font_droid_sans_fallback_11x13;
extern const struct GDS_FontDef Font_droid_sans_fallback_15x17;
extern const struct GDS_FontDef Font_droid_sans_fallback_24x28;
extern const struct GDS_FontDef Font_droid_sans_mono_7x13;
extern const struct GDS_FontDef Font_droid_sans_mono_13x24;
extern const struct GDS_FontDef Font_droid_sans_mono_16x31;
extern const struct GDS_FontDef Font_liberation_mono_9x15;
extern const struct GDS_FontDef Font_liberation_mono_13x21;
extern const struct GDS_FontDef Font_liberation_mono_17x30;
extern const struct GDS_FontDef Font_Tarable7Seg_16x32;
extern const struct GDS_FontDef Font_Tarable7Seg_32x64;
extern const struct GDS_FontDef Font_line_1;
extern const struct GDS_FontDef Font_line_2;
#ifdef __cplusplus
}
#endif
#endif

View File

@@ -0,0 +1,130 @@
#pragma once
#include <stdint.h>
#include <stdbool.h>
#include "esp_attr.h"
#include "gds.h"
#include "gds_err.h"
#define GDS_CLIPDEBUG_NONE 0
#define GDS_CLIPDEBUG_WARNING 1
#define GDS_CLIPDEBUG_ERROR 2
#define SHADOW_BUFFER
#if CONFIG_GDS_CLIPDEBUG == GDS_CLIPDEBUG_NONE
/*
* Clip silently with no console output.
*/
#define ClipDebug( x, y )
#elif CONFIG_GDS_CLIPDEBUG == GDS_CLIPDEBUG_WARNING
/*
* Log clipping to the console as a warning.
*/
#define ClipDebug( x, y ) { \
ESP_LOGW( __FUNCTION__, "Line %d: Pixel at %d, %d CLIPPED", __LINE__, x, y ); \
}
#elif CONFIG_GDS_CLIPDEBUG == GDS_CLIPDEBUG_ERROR
/*
* Log clipping as an error to the console.
* Also invokes an abort with stack trace.
*/
#define ClipDebug( x, y ) { \
ESP_LOGE( __FUNCTION__, "Line %d: Pixel at %d, %d CLIPPED, ABORT", __LINE__, x, y ); \
abort( ); \
}
#endif
#define GDS_ALWAYS_INLINE __attribute__( ( always_inline ) )
#define MAX_LINES 8
#if ! defined BIT
#define BIT( n ) ( 1 << n )
#endif
typedef enum {
AddressMode_Horizontal = 0,
AddressMode_Vertical,
AddressMode_Page,
AddressMode_Invalid
} GDS_AddressMode;
struct GDS_Device;
struct GDS_FontDef;
/*
* These can optionally return a succeed/fail but are as of yet unused in the driver.
*/
typedef bool ( *WriteCommandProc ) ( struct GDS_Device* Device, uint8_t Command );
typedef bool ( *WriteDataProc ) ( struct GDS_Device* Device, const uint8_t* Data, size_t DataLength );
struct spi_device_t;
typedef struct spi_device_t* spi_device_handle_t;
#define IF_SPI 0
#define IF_I2C 1
struct GDS_Device {
uint8_t IF;
union {
// I2C Specific
struct {
uint8_t Address;
};
// SPI specific
struct {
spi_device_handle_t SPIHandle;
int8_t RSTPin;
int8_t CSPin;
};
};
// cooked text mode
struct {
int16_t Y, Space;
const struct GDS_FontDef* Font;
} Lines[MAX_LINES];
uint16_t Width;
uint16_t Height;
uint8_t Depth;
uint8_t* Framebuffer, *Shadowbuffer;
uint16_t FramebufferSize;
bool Dirty;
// default fonts when using direct draw
const struct GDS_FontDef* Font;
bool FontForceProportional;
bool FontForceMonospace;
// various driver-specific method
bool (*Init)( struct GDS_Device* Device);
void (*SetContrast)( struct GDS_Device* Device, uint8_t Contrast );
void (*DisplayOn)( struct GDS_Device* Device );
void (*DisplayOff)( struct GDS_Device* Device );
void (*Update)( struct GDS_Device* Device );
void (*DrawPixel)( struct GDS_Device* Device, int X, int Y, int Color );
void (*DrawPixelFast)( struct GDS_Device* Device, int X, int Y, int Color );
void (*SetHFlip)( struct GDS_Device* Device, bool On );
void (*SetVFlip)( struct GDS_Device* Device, bool On );
// interface-specific methods
WriteCommandProc WriteCommand;
WriteDataProc WriteData;
};
bool GDS_Reset( struct GDS_Device* Device );
/*
inline void IRAM_ATTR GDS_DrawPixelFast( struct GDS_Device* Device, int X, int Y, int Color );
void IRAM_ATTR GDS_DrawPixel( struct GDS_Device* Device, int x, int y, int Color );
inline void IRAM_ATTR GDS_DrawPixel4Fast( struct GDS_Device* Device, int X, int Y, int Color );
void IRAM_ATTR GDS_DrawPixel4( struct GDS_Device* Device, int x, int y, int Color );
*/
void IRAM_ATTR GDS_DrawPixelFast( struct GDS_Device* Device, int X, int Y, int Color );
void IRAM_ATTR GDS_DrawPixel( struct GDS_Device* Device, int x, int y, int Color );
void IRAM_ATTR GDS_DrawPixel4Fast( struct GDS_Device* Device, int X, int Y, int Color );
void IRAM_ATTR GDS_DrawPixel4( struct GDS_Device* Device, int x, int y, int Color );

View File

@@ -0,0 +1,195 @@
/*
* (c) 2004,2006 Richard Titmuss for SlimProtoLib
* (c) Philippe G. 2019, philippe_44@outlook.com
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include <string.h>
#include <ctype.h>
#include <stdint.h>
#include <arpa/inet.h>
#include "esp_log.h"
#include "gds.h"
#include "gds_draw.h"
#include "gds_text.h"
#include "gds_private.h"
#define max(a,b) (((a) > (b)) ? (a) : (b))
static char TAG[] = "gds";
/****************************************************************************************
* Set fonts for each line in text mode
*/
static const struct GDS_FontDef *GuessFont( struct GDS_Device *Device, int FontType) {
switch(FontType) {
case GDS_FONT_LINE_1:
return &Font_line_1;
case GDS_FONT_LINE_2:
return &Font_line_2;
case GDS_FONT_SMALL:
return &Font_droid_sans_fallback_11x13;
case GDS_FONT_MEDIUM:
default:
return &Font_droid_sans_fallback_15x17;
case GDS_FONT_LARGE:
return &Font_droid_sans_fallback_24x28;
break;
case GDS_FONT_SEGMENT:
if (Device->Height == 32) return &Font_Tarable7Seg_16x32;
else return &Font_Tarable7Seg_32x64;
}
}
/****************************************************************************************
* Set fonts for each line in text mode
*/
bool GDS_TextSetFontAuto(struct GDS_Device* Device, int N, int FontType, int Space) {
const struct GDS_FontDef *Font = GuessFont( Device, FontType );
return GDS_TextSetFont( Device, N, Font, Space );
}
/****************************************************************************************
* Set fonts for each line in text mode
*/
bool GDS_TextSetFont(struct GDS_Device* Device, int N, const struct GDS_FontDef *Font, int Space) {
if (--N >= MAX_LINES) return false;
Device->Lines[N].Font = Font;
// re-calculate lines absolute position
Device->Lines[N].Space = Space;
Device->Lines[0].Y = Device->Lines[0].Space;
for (int i = 1; i <= N; i++) Device->Lines[i].Y = Device->Lines[i-1].Y + Device->Lines[i-1].Font->Height + Device->Lines[i].Space;
ESP_LOGI(TAG, "Adding line %u at %d (height:%u)", N + 1, Device->Lines[N].Y, Device->Lines[N].Font->Height);
if (Device->Lines[N].Y + Device->Lines[N].Font->Height > Device->Height) {
ESP_LOGW(TAG, "line does not fit display");
return false;
}
return true;
}
/****************************************************************************************
*
*/
bool GDS_TextLine(struct GDS_Device* Device, int N, int Pos, int Attr, char *Text) {
int Width, X = Pos;
// counting 1..n
N--;
GDS_SetFont( Device, Device->Lines[N].Font );
if (Attr & GDS_TEXT_MONOSPACE) GDS_FontForceMonospace( Device, true );
Width = GDS_FontMeasureString( Device, Text );
// adjusting position, erase only EoL for rigth-justified
if (Pos == GDS_TEXT_RIGHT) X = Device->Width - Width - 1;
else if (Pos == GDS_TEXT_CENTER) X = (Device->Width - Width) / 2;
// erase if requested
if (Attr & GDS_TEXT_CLEAR) {
int Y_min = max(0, Device->Lines[N].Y), Y_max = max(0, Device->Lines[N].Y + Device->Lines[N].Font->Height);
for (int c = (Attr & GDS_TEXT_CLEAR_EOL) ? X : 0; c < Device->Width; c++)
for (int y = Y_min; y < Y_max; y++)
Device->DrawPixelFast( Device, c, y, GDS_COLOR_BLACK );
}
GDS_FontDrawString( Device, X, Device->Lines[N].Y, Text, GDS_COLOR_WHITE );
ESP_LOGD(TAG, "displaying %s line %u (x:%d, attr:%u)", Text, N+1, X, Attr);
// update whole display if requested
Device->Dirty = true;
if (Attr & GDS_TEXT_UPDATE) GDS_Update( Device );
return Width + X < Device->Width;
}
/****************************************************************************************
* Try to align string for better scrolling visual. there is probably much better to do
*/
int GDS_TextStretch(struct GDS_Device* Device, int N, char *String, int Max) {
char Space[] = " ";
int Len = strlen(String), Extra = 0, Boundary;
N--;
// we might already fit
GDS_SetFont( Device, Device->Lines[N].Font );
if (GDS_FontMeasureString( Device, String ) <= Device->Width) return 0;
// add some space for better visual
strncat(String, Space, Max-Len);
String[Max] = '\0';
Len = strlen(String);
// mark the end of the extended string
Boundary = GDS_FontMeasureString( Device, String );
// add a full display width
while (Len < Max && GDS_FontMeasureString( Device, String ) - Boundary < Device->Width) {
String[Len++] = String[Extra++];
String[Len] = '\0';
}
return Boundary;
}
/****************************************************************************************
*
*/
void GDS_TextPos(struct GDS_Device* Device, int FontType, int Where, int Attr, char *Text, ...) {
va_list args;
TextAnchor Anchor = TextAnchor_Center;
if (Attr & GDS_TEXT_CLEAR) GDS_Clear( Device, GDS_COLOR_BLACK );
if (!Text) return;
va_start(args, Text);
switch(Where) {
case GDS_TEXT_TOP_LEFT:
default:
Anchor = TextAnchor_NorthWest;
break;
case GDS_TEXT_MIDDLE_LEFT:
Anchor = TextAnchor_West;
break;
case GDS_TEXT_BOTTOM_LEFT:
Anchor = TextAnchor_SouthWest;
break;
case GDS_TEXT_CENTERED:
Anchor = TextAnchor_Center;
break;
}
ESP_LOGD(TAG, "Displaying %s at %u with attribute %u", Text, Anchor, Attr);
GDS_SetFont( Device, GuessFont( Device, FontType ) );
GDS_FontDrawAnchoredString( Device, Anchor, Text, GDS_COLOR_WHITE );
Device->Dirty = true;
if (Attr & GDS_TEXT_UPDATE) GDS_Update( Device );
va_end(args);
}

View File

@@ -0,0 +1,45 @@
/*
* (c) Philippe G. 2019, philippe_44@outlook.com
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#pragma once
#include "gds_font.h"
#define GDS_TEXT_CLEAR 0x01
#define GDS_TEXT_CLEAR_EOL 0x02
#define GDS_TEXT_UPDATE 0x04
#define GDS_TEXT_MONOSPACE 0x08
// these ones are for 'Pos' parameter of TextLine
#define GDS_TEXT_LEFT 0
#define GDS_TEXT_RIGHT 0xff00
#define GDS_TEXT_CENTER 0xff01
// these ones are for the 'Where' parameter of TextPos
enum { GDS_TEXT_TOP_LEFT, GDS_TEXT_MIDDLE_LEFT, GDS_TEXT_BOTTOM_LEFT, GDS_TEXT_CENTERED };
enum { GDS_FONT_DEFAULT, GDS_FONT_LINE_1, GDS_FONT_LINE_2, GDS_FONT_SEGMENT,
GDS_FONT_TINY, GDS_FONT_SMALL, GDS_FONT_MEDIUM, GDS_FONT_LARGE, GDS_FONT_FONT_HUGE };
struct GDS_Device;
bool GDS_TextSetFontAuto(struct GDS_Device* Device, int N, int FontType, int Space);
bool GDS_TextSetFont(struct GDS_Device* Device, int N, const struct GDS_FontDef *Font, int Space);
bool GDS_TextLine(struct GDS_Device* Device, int N, int Pos, int Attr, char *Text);
int GDS_TextStretch(struct GDS_Device* Device, int N, char *String, int Max);
void GDS_TextPos(struct GDS_Device* Device, int FontType, int Where, int Attr, char *Text, ...);

View File

@@ -11,18 +11,19 @@
#include <string.h>
#include <driver/i2c.h>
#include <driver/gpio.h>
#include "ssd13x6.h"
#include "ssd13x6_default_if.h"
#include "gds.h"
#include "gds_err.h"
#include "gds_private.h"
#include "gds_default_if.h"
static int I2CPortNumber;
static const int SSD13x6_I2C_COMMAND_MODE = 0x80;
static const int SSD13x6_I2C_DATA_MODE = 0x40;
static const int GDS_I2C_COMMAND_MODE = 0x80;
static const int GDS_I2C_DATA_MODE = 0x40;
static bool I2CDefaultWriteBytes( int Address, bool IsCommand, const uint8_t* Data, size_t DataLength );
static bool I2CDefaultWriteCommand( struct SSD13x6_Device* Display, SSDCmd Command );
static bool I2CDefaultWriteData( struct SSD13x6_Device* Display, const uint8_t* Data, size_t DataLength );
static bool I2CDefaultReset( struct SSD13x6_Device* Display );
static bool I2CDefaultWriteCommand( struct GDS_Device* Device, uint8_t Command );
static bool I2CDefaultWriteData( struct GDS_Device* Device, const uint8_t* Data, size_t DataLength );
/*
* Initializes the i2c master with the parameters specified
@@ -30,7 +31,7 @@ static bool I2CDefaultReset( struct SSD13x6_Device* Display );
*
* Returns true on successful init of the i2c bus.
*/
bool SSD13x6_I2CMasterInitDefault( int PortNumber, int SDA, int SCL ) {
bool GDS_I2CInit( int PortNumber, int SDA, int SCL ) {
I2CPortNumber = PortNumber;
if (SDA != -1 && SCL != -1) {
@@ -54,7 +55,7 @@ bool SSD13x6_I2CMasterInitDefault( int PortNumber, int SDA, int SCL ) {
* Attaches a display to the I2C bus using default communication functions.
*
* Params:
* DisplayHandle: Pointer to your SSD13x6_Device object
* Device: Pointer to your GDS_Device object
* Width: Width of display
* Height: Height of display
* I2CAddress: Address of your display
@@ -62,26 +63,24 @@ bool SSD13x6_I2CMasterInitDefault( int PortNumber, int SDA, int SCL ) {
*
* Returns true on successful init of display.
*/
bool SSD13x6_I2CMasterAttachDisplayDefault( struct SSD13x6_Device* DeviceHandle, int Model, int Width, int Height, int I2CAddress, int RSTPin ) {
NullCheck( DeviceHandle, return false );
bool GDS_I2CAttachDevice( struct GDS_Device* Device, int Width, int Height, int I2CAddress, int RSTPin ) {
NullCheck( Device, return false );
if ( RSTPin >= 0 ) {
Device->WriteCommand = I2CDefaultWriteCommand;
Device->WriteData = I2CDefaultWriteData;
Device->Address = I2CAddress;
Device->RSTPin = RSTPin;
Device->IF = IF_I2C;
Device->Width = Width;
Device->Height = Height;
if ( RSTPin >= 0 ) {
ESP_ERROR_CHECK_NONFATAL( gpio_set_direction( RSTPin, GPIO_MODE_OUTPUT ), return false );
ESP_ERROR_CHECK_NONFATAL( gpio_set_level( RSTPin, 1 ), return false );
GDS_Reset( Device );
}
memset( DeviceHandle, 0, sizeof( struct SSD13x6_Device ) );
DeviceHandle->Model = Model;
return SSD13x6_Init_I2C( DeviceHandle,
Width,
Height,
I2CAddress,
RSTPin,
I2CDefaultWriteCommand,
I2CDefaultWriteData,
I2CDefaultReset
);
return Device->Init( Device );
}
static bool I2CDefaultWriteBytes( int Address, bool IsCommand, const uint8_t* Data, size_t DataLength ) {
@@ -91,7 +90,7 @@ static bool I2CDefaultWriteBytes( int Address, bool IsCommand, const uint8_t* Da
NullCheck( Data, return false );
if ( ( CommandHandle = i2c_cmd_link_create( ) ) != NULL ) {
ModeByte = ( IsCommand == true ) ? SSD13x6_I2C_COMMAND_MODE: SSD13x6_I2C_DATA_MODE;
ModeByte = ( IsCommand == true ) ? GDS_I2C_COMMAND_MODE: GDS_I2C_DATA_MODE;
ESP_ERROR_CHECK_NONFATAL( i2c_master_start( CommandHandle ), goto error );
ESP_ERROR_CHECK_NONFATAL( i2c_master_write_byte( CommandHandle, ( Address << 1 ) | I2C_MASTER_WRITE, true ), goto error );
@@ -110,29 +109,16 @@ error:
return false;
}
static bool I2CDefaultWriteCommand( struct SSD13x6_Device* Display, SSDCmd Command ) {
static bool I2CDefaultWriteCommand( struct GDS_Device* Device, uint8_t Command ) {
uint8_t CommandByte = ( uint8_t ) Command;
NullCheck( Display, return false );
return I2CDefaultWriteBytes( Display->Address, true, ( const uint8_t* ) &CommandByte, 1 );
NullCheck( Device, return false );
return I2CDefaultWriteBytes( Device->Address, true, ( const uint8_t* ) &CommandByte, 1 );
}
static bool I2CDefaultWriteData( struct SSD13x6_Device* Display, const uint8_t* Data, size_t DataLength ) {
NullCheck( Display, return false );
static bool I2CDefaultWriteData( struct GDS_Device* Device, const uint8_t* Data, size_t DataLength ) {
NullCheck( Device, return false );
NullCheck( Data, return false );
return I2CDefaultWriteBytes( Display->Address, false, Data, DataLength );
return I2CDefaultWriteBytes( Device->Address, false, Data, DataLength );
}
static bool I2CDefaultReset( struct SSD13x6_Device* Display ) {
NullCheck( Display, return false );
if ( Display->RSTPin >= 0 ) {
ESP_ERROR_CHECK_NONFATAL( gpio_set_level( Display->RSTPin, 0 ), return true );
vTaskDelay( pdMS_TO_TICKS( 100 ) );
ESP_ERROR_CHECK_NONFATAL( gpio_set_level( Display->RSTPin, 1 ), return true );
}
return true;
}

View File

@@ -0,0 +1,109 @@
/**
* Copyright (c) 2017-2018 Tara Keeling
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#include <stdio.h>
#include <stdint.h>
#include <stdbool.h>
#include <string.h>
#include <driver/spi_master.h>
#include <driver/gpio.h>
#include <freertos/task.h>
#include "gds.h"
#include "gds_err.h"
#include "gds_private.h"
#include "gds_default_if.h"
static const int GDS_SPI_Command_Mode = 0;
static const int GDS_SPI_Data_Mode = 1;
static spi_host_device_t SPIHost;
static int DCPin;
static bool SPIDefaultWriteBytes( spi_device_handle_t SPIHandle, int WriteMode, const uint8_t* Data, size_t DataLength );
static bool SPIDefaultWriteCommand( struct GDS_Device* Device, uint8_t Command );
static bool SPIDefaultWriteData( struct GDS_Device* Device, const uint8_t* Data, size_t DataLength );
bool GDS_SPIInit( int SPI, int DC ) {
SPIHost = SPI;
DCPin = DC;
return true;
}
bool GDS_SPIAttachDevice( struct GDS_Device* Device, int Width, int Height, int CSPin, int RSTPin, int Speed ) {
spi_device_interface_config_t SPIDeviceConfig;
spi_device_handle_t SPIDevice;
NullCheck( Device, return false );
if (CSPin >= 0) {
ESP_ERROR_CHECK_NONFATAL( gpio_set_direction( CSPin, GPIO_MODE_OUTPUT ), return false );
ESP_ERROR_CHECK_NONFATAL( gpio_set_level( CSPin, 0 ), return false );
}
memset( &SPIDeviceConfig, 0, sizeof( spi_device_interface_config_t ) );
SPIDeviceConfig.clock_speed_hz = Speed > 0 ? Speed : SPI_MASTER_FREQ_8M;
SPIDeviceConfig.spics_io_num = CSPin;
SPIDeviceConfig.queue_size = 1;
ESP_ERROR_CHECK_NONFATAL( spi_bus_add_device( SPIHost, &SPIDeviceConfig, &SPIDevice ), return false );
Device->WriteCommand = SPIDefaultWriteCommand;
Device->WriteData = SPIDefaultWriteData;
Device->SPIHandle = SPIDevice;
Device->RSTPin = RSTPin;
Device->CSPin = CSPin;
Device->IF = IF_SPI;
Device->Width = Width;
Device->Height = Height;
if ( RSTPin >= 0 ) {
ESP_ERROR_CHECK_NONFATAL( gpio_set_direction( RSTPin, GPIO_MODE_OUTPUT ), return false );
ESP_ERROR_CHECK_NONFATAL( gpio_set_level( RSTPin, 0 ), return false );
GDS_Reset( Device );
}
return Device->Init( Device );
}
static bool SPIDefaultWriteBytes( spi_device_handle_t SPIHandle, int WriteMode, const uint8_t* Data, size_t DataLength ) {
spi_transaction_t SPITransaction = { 0 };
NullCheck( SPIHandle, return false );
NullCheck( Data, return false );
if ( DataLength > 0 ) {
gpio_set_level( DCPin, WriteMode );
SPITransaction.length = DataLength * 8;
SPITransaction.tx_buffer = Data;
// only do polling as we don't have contention on SPI (otherwise DMA for transfers > 16 bytes)
ESP_ERROR_CHECK_NONFATAL( spi_device_polling_transmit(SPIHandle, &SPITransaction), return false );
}
return true;
}
static bool SPIDefaultWriteCommand( struct GDS_Device* Device, uint8_t Command ) {
static uint8_t CommandByte = 0;
NullCheck( Device, return false );
NullCheck( Device->SPIHandle, return false );
CommandByte = Command;
return SPIDefaultWriteBytes( Device->SPIHandle, GDS_SPI_Command_Mode, &CommandByte, 1 );
}
static bool SPIDefaultWriteData( struct GDS_Device* Device, const uint8_t* Data, size_t DataLength ) {
NullCheck( Device, return false );
NullCheck( Device->SPIHandle, return false );
return SPIDefaultWriteBytes( Device->SPIHandle, GDS_SPI_Data_Mode, Data, DataLength );
}

View File

@@ -22,14 +22,15 @@
#include <stdint.h>
#include <arpa/inet.h>
#include "esp_log.h"
#include "globdefs.h"
#include "config.h"
#include "tools.h"
#include "display.h"
// here we should include all possible drivers
extern struct display_s SSD13x6_display;
struct display_s *display = NULL;
#include "gds.h"
#include "gds_default_if.h"
#include "gds_draw.h"
#include "gds_text.h"
#include "gds_font.h"
static const char *TAG = "display";
@@ -57,34 +58,74 @@ static EXT_RAM_ATTR struct {
static void displayer_task(void *args);
struct GDS_Device *display;
struct GDS_Device* SSD1306_Detect(char *Driver, struct GDS_Device* Device);
struct GDS_Device* SH1106_Detect(char *Driver, struct GDS_Device* Device);
GDS_DetectFunc drivers[] = { SH1106_Detect, SSD1306_Detect, NULL };
/****************************************************************************************
*
*/
void display_init(char *welcome) {
bool init = false;
char *item = config_alloc_get(NVS_TYPE_STR, "display_config");
char *config = config_alloc_get(NVS_TYPE_STR, "display_config");
if (item && *item) {
char * drivername=strstr(item,"driver");
if (!drivername || (drivername && (strcasestr(drivername,"SSD1306") || strcasestr(drivername,"SSD1326") || strcasestr(drivername,"SH1106")))) {
display = &SSD13x6_display;
if (display->init(item, welcome)) {
init = true;
ESP_LOGI(TAG, "Display initialization successful");
} else {
display = NULL;
ESP_LOGW(TAG, "Display initialization failed");
}
if (!config) {
ESP_LOGI(TAG, "no display");
return false;
}
int width = -1, height = -1;
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, "height")) != NULL) height = atoi(strchr(p, '=') + 1);
// so far so good
if (display && width > 0 && height > 0) {
// Detect driver interface
if (strstr(config, "I2C") && i2c_system_port != -1) {
int address = 0x3C;
if ((p = strcasestr(config, "address")) != NULL) address = atoi(strchr(p, '=') + 1);
init = true;
GDS_I2CInit( i2c_system_port, -1, -1 ) ;
GDS_I2CAttachDevice( display, width, height, address, -1 );
ESP_LOGI(TAG, "Display is I2C on port %u", address);
} else if (strstr(config, "SPI") && spi_system_host != -1) {
int CS_pin = -1, speed = 0;
if ((p = strcasestr(config, "cs")) != NULL) CS_pin = atoi(strchr(p, '=') + 1);
if ((p = strcasestr(config, "speed")) != NULL) speed = atoi(strchr(p, '=') + 1);
init = true;
GDS_SPIInit( spi_system_host, spi_system_dc_gpio );
GDS_SPIAttachDevice( display, width, height, CS_pin, -1, speed );
ESP_LOGI(TAG, "Display is SPI host %u with cs:%d", spi_system_host, CS_pin);
} else {
ESP_LOGE(TAG,"Unknown display driver name in display config: %s",item);
display = NULL;
ESP_LOGI(TAG, "Unsupported display interface or serial link not configured");
}
} else {
ESP_LOGI(TAG, "no display");
}
display = NULL;
ESP_LOGW(TAG, "No display driver");
}
if (init) {
static DRAM_ATTR StaticTask_t xTaskBuffer __attribute__ ((aligned (4)));
static EXT_RAM_ATTR StackType_t xStack[DISPLAYER_STACK_SIZE] __attribute__ ((aligned (4)));
GDS_SetHFlip(display, strcasestr(config, "HFlip") ? true : false);
GDS_SetVFlip(display, strcasestr(config, "VFlip") ? true : false);
GDS_SetFont(display, &Font_droid_sans_fallback_15x17 );
GDS_TextPos(display, GDS_FONT_MEDIUM, GDS_TEXT_CENTERED, GDS_TEXT_CLEAR | GDS_TEXT_UPDATE, welcome);
// start the task that will handle scrolling & counting
displayer.mutex = xSemaphoreCreateMutex();
@@ -94,13 +135,13 @@ void display_init(char *welcome) {
displayer.task = xTaskCreateStatic( (TaskFunction_t) displayer_task, "displayer_thread", DISPLAYER_STACK_SIZE, NULL, ESP_TASK_PRIO_MIN + 1, xStack, &xTaskBuffer);
// set lines for "fixed" text mode
display->set_font(1, DISPLAY_FONT_LINE_1, -3);
display->set_font(2, DISPLAY_FONT_LINE_2, -3);
GDS_TextSetFontAuto(display, 1, GDS_FONT_LINE_1, -3);
GDS_TextSetFontAuto(display, 2, GDS_FONT_LINE_2, -3);
displayer.metadata_config = config_alloc_get(NVS_TYPE_STR, "metadata_config");
}
if (item) free(item);
free(config);
}
/****************************************************************************************
@@ -114,14 +155,14 @@ static void displayer_task(void *args) {
while (1) {
// suspend ourselves if nothing to do
if (displayer.state < DISPLAYER_ACTIVE) {
if (displayer.state == DISPLAYER_IDLE) display->line(2, 0, DISPLAY_CLEAR | DISPLAY_UPDATE, displayer.string);
if (displayer.state == DISPLAYER_IDLE) GDS_TextLine(display, 2, 0, GDS_TEXT_CLEAR | GDS_TEXT_UPDATE, displayer.string);
vTaskSuspend(NULL);
scroll_sleep = 0;
display->clear(true);
display->line(1, DISPLAY_LEFT, DISPLAY_UPDATE, displayer.header);
GDS_ClearExt(display, true);
GDS_TextLine(display, 1, GDS_TEXT_LEFT, GDS_TEXT_UPDATE, displayer.header);
} else if (displayer.refresh) {
// little trick when switching master while in IDLE and missing it
display->line(1, DISPLAY_LEFT, DISPLAY_CLEAR | DISPLAY_UPDATE, displayer.header);
GDS_TextLine(display, 1, GDS_TEXT_LEFT, GDS_TEXT_CLEAR | GDS_TEXT_UPDATE, displayer.header);
displayer.refresh = false;
}
@@ -140,7 +181,7 @@ static void displayer_task(void *args) {
xSemaphoreGive(displayer.mutex);
// now display using safe copies, can be lengthy
display->line(2, offset, DISPLAY_CLEAR | DISPLAY_UPDATE, string);
GDS_TextLine(display, 2, offset, GDS_TEXT_CLEAR | GDS_TEXT_UPDATE, string);
free(string);
} else {
scroll_sleep = DEFAULT_SLEEP;
@@ -160,7 +201,7 @@ static void displayer_task(void *args) {
xSemaphoreGive(displayer.mutex);
if (displayer.elapsed < 3600) sprintf(counter, "%5u:%02u", displayer.elapsed / 60, displayer.elapsed % 60);
else sprintf(counter, "%2u:%02u:%02u", displayer.elapsed / 3600, (displayer.elapsed % 3600) / 60, displayer.elapsed % 60);
display->line(1, DISPLAY_RIGHT, (DISPLAY_CLEAR | DISPLAY_ONLY_EOL) | DISPLAY_UPDATE, counter);
GDS_TextLine(display, 1, GDS_TEXT_RIGHT, (GDS_TEXT_CLEAR | GDS_TEXT_CLEAR_EOL) | GDS_TEXT_UPDATE, counter);
timer_sleep = 1000;
} else timer_sleep = max(1000 - elapsed, 0);
} else timer_sleep = DEFAULT_SLEEP;
@@ -240,7 +281,7 @@ void displayer_metadata(char *artist, char *album, char *title) {
displayer.offset = 0;
utf8_decode(displayer.string);
ESP_LOGI(TAG, "playing %s", displayer.string);
displayer.boundary = display->stretch(2, displayer.string, SCROLLABLE_SIZE);
displayer.boundary = GDS_TextStretch(display, 2, displayer.string, SCROLLABLE_SIZE);
xSemaphoreGive(displayer.mutex);
}
@@ -258,7 +299,7 @@ void displayer_scroll(char *string, int speed) {
displayer.offset = 0;
strncpy(displayer.string, string, SCROLLABLE_SIZE);
displayer.string[SCROLLABLE_SIZE] = '\0';
displayer.boundary = display->stretch(2, displayer.string, SCROLLABLE_SIZE);
displayer.boundary = GDS_TextStretch(display, 2, displayer.string, SCROLLABLE_SIZE);
xSemaphoreGive(displayer.mutex);
}

View File

@@ -18,6 +18,8 @@
#pragma once
#include "gds.h"
/*
The displayer is not thread-safe and the caller must ensure use its own
mutexes if it wants something better. Especially, text() line() and draw()
@@ -32,44 +34,11 @@
when this one (the main) wants to take control over display, it can signal
that to others
*/
extern struct GDS_Device *display;
#define DISPLAY_CLEAR 0x01
#define DISPLAY_ONLY_EOL 0x02
#define DISPLAY_UPDATE 0x04
#define DISPLAY_MONOSPACE 0x08
// these ones are for 'x' parameter of line() function
#define DISPLAY_LEFT 0
#define DISPLAY_RIGHT 0xff00
#define DISPLAY_CENTER 0xff01
enum display_pos_e { DISPLAY_TOP_LEFT, DISPLAY_MIDDLE_LEFT, DISPLAY_BOTTOM_LEFT, DISPLAY_CENTERED };
enum display_font_e { DISPLAY_FONT_DEFAULT,
DISPLAY_FONT_LINE_1, DISPLAY_FONT_LINE_2, DISPLAY_FONT_SEGMENT,
DISPLAY_FONT_TINY, DISPLAY_FONT_SMALL, DISPLAY_FONT_MEDIUM, DISPLAY_FONT_LARGE, DISPLAY_FONT_HUGE };
enum displayer_cmd_e { DISPLAYER_SHUTDOWN, DISPLAYER_ACTIVATE, DISPLAYER_SUSPEND, DISPLAYER_TIMER_PAUSE, DISPLAYER_TIMER_RUN };
enum displayer_time_e { DISPLAYER_ELAPSED, DISPLAYER_REMAINING };
// don't change anything there w/o changing all drivers init code
extern struct display_s {
int width, height;
bool dirty;
bool (*init)(char *config, char *welcome);
void (*clear)(bool full, ...);
bool (*set_font)(int num, enum display_font_e font, int space);
void (*on)(bool state);
void (*brightness)(uint8_t level);
void (*text)(enum display_font_e font, enum display_pos_e pos, int attribute, char *msg, ...);
bool (*line)(int num, int x, int attribute, char *text);
int (*stretch)(int num, char *string, int max);
void (*update)(void);
void (*draw_raw)(int x1, int y1, int x2, int y2, bool by_column, bool MSb, uint8_t *data);
void (*draw_cbr)(uint8_t *data, int width, int height); // width and height is the # of rows/columns in data, as opposed to display height (0 = display width, 0 = display height)
void (*draw_line)(int x1, int y1, int x2, int y2);
void (*draw_box)( int x1, int y1, int x2, int y2, bool fill);
} *display;
enum display_bus_cmd_e { DISPLAY_BUS_TAKE, DISPLAY_BUS_GIVE };
bool (*display_bus)(void *from, enum display_bus_cmd_e cmd);

View File

@@ -1,446 +0,0 @@
/*
* (c) 2004,2006 Richard Titmuss for SlimProtoLib
* (c) Philippe G. 2019, philippe_44@outlook.com
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include <string.h>
#include <ctype.h>
#include <stdint.h>
#include <arpa/inet.h>
#include "esp_log.h"
#include "display.h"
#include "globdefs.h"
#include "ssd13x6.h"
#include "ssd13x6_draw.h"
#include "ssd13x6_font.h"
#include "ssd13x6_default_if.h"
#define I2C_ADDRESS 0x3C
static const char *TAG = "display";
#define max(a,b) (((a) > (b)) ? (a) : (b))
// handlers
static bool init(char *config, char *welcome);
static void clear(bool full, ...);
static bool set_font(int num, enum display_font_e font, int space);
static void text(enum display_font_e font, enum display_pos_e pos, int attribute, char *text, ...);
static bool line(int num, int x, int attribute, char *text);
static int stretch(int num, char *string, int max);
static void draw_cbr(u8_t *data, int width, int height);
static void draw_raw(int x1, int y1, int x2, int y2, bool by_column, bool MSb, u8_t *data);
static void draw_line(int x1, int y1, int x2, int y2);
static void draw_box( int x1, int y1, int x2, int y2, bool fill);
static void brightness(u8_t level);
static void on(bool state);
static void update(void);
// display structure for others to use
struct display_s SSD13x6_display = { 0, 0, true,
init, clear, set_font, on, brightness,
text, line, stretch, update, draw_raw, draw_cbr, draw_line, draw_box };
// SSD13x6 specific function
static struct SSD13x6_Device Display;
static const unsigned char BitReverseTable256[] =
{
0x00, 0x80, 0x40, 0xC0, 0x20, 0xA0, 0x60, 0xE0, 0x10, 0x90, 0x50, 0xD0, 0x30, 0xB0, 0x70, 0xF0,
0x08, 0x88, 0x48, 0xC8, 0x28, 0xA8, 0x68, 0xE8, 0x18, 0x98, 0x58, 0xD8, 0x38, 0xB8, 0x78, 0xF8,
0x04, 0x84, 0x44, 0xC4, 0x24, 0xA4, 0x64, 0xE4, 0x14, 0x94, 0x54, 0xD4, 0x34, 0xB4, 0x74, 0xF4,
0x0C, 0x8C, 0x4C, 0xCC, 0x2C, 0xAC, 0x6C, 0xEC, 0x1C, 0x9C, 0x5C, 0xDC, 0x3C, 0xBC, 0x7C, 0xFC,
0x02, 0x82, 0x42, 0xC2, 0x22, 0xA2, 0x62, 0xE2, 0x12, 0x92, 0x52, 0xD2, 0x32, 0xB2, 0x72, 0xF2,
0x0A, 0x8A, 0x4A, 0xCA, 0x2A, 0xAA, 0x6A, 0xEA, 0x1A, 0x9A, 0x5A, 0xDA, 0x3A, 0xBA, 0x7A, 0xFA,
0x06, 0x86, 0x46, 0xC6, 0x26, 0xA6, 0x66, 0xE6, 0x16, 0x96, 0x56, 0xD6, 0x36, 0xB6, 0x76, 0xF6,
0x0E, 0x8E, 0x4E, 0xCE, 0x2E, 0xAE, 0x6E, 0xEE, 0x1E, 0x9E, 0x5E, 0xDE, 0x3E, 0xBE, 0x7E, 0xFE,
0x01, 0x81, 0x41, 0xC1, 0x21, 0xA1, 0x61, 0xE1, 0x11, 0x91, 0x51, 0xD1, 0x31, 0xB1, 0x71, 0xF1,
0x09, 0x89, 0x49, 0xC9, 0x29, 0xA9, 0x69, 0xE9, 0x19, 0x99, 0x59, 0xD9, 0x39, 0xB9, 0x79, 0xF9,
0x05, 0x85, 0x45, 0xC5, 0x25, 0xA5, 0x65, 0xE5, 0x15, 0x95, 0x55, 0xD5, 0x35, 0xB5, 0x75, 0xF5,
0x0D, 0x8D, 0x4D, 0xCD, 0x2D, 0xAD, 0x6D, 0xED, 0x1D, 0x9D, 0x5D, 0xDD, 0x3D, 0xBD, 0x7D, 0xFD,
0x03, 0x83, 0x43, 0xC3, 0x23, 0xA3, 0x63, 0xE3, 0x13, 0x93, 0x53, 0xD3, 0x33, 0xB3, 0x73, 0xF3,
0x0B, 0x8B, 0x4B, 0xCB, 0x2B, 0xAB, 0x6B, 0xEB, 0x1B, 0x9B, 0x5B, 0xDB, 0x3B, 0xBB, 0x7B, 0xFB,
0x07, 0x87, 0x47, 0xC7, 0x27, 0xA7, 0x67, 0xE7, 0x17, 0x97, 0x57, 0xD7, 0x37, 0xB7, 0x77, 0xF7,
0x0F, 0x8F, 0x4F, 0xCF, 0x2F, 0xAF, 0x6F, 0xEF, 0x1F, 0x9F, 0x5F, 0xDF, 0x3F, 0xBF, 0x7F, 0xFF
};
#define MAX_LINES 8
static struct {
int y, space;
const struct SSD13x6_FontDef *font;
} lines[MAX_LINES];
/****************************************************************************************
*
*/
static bool init(char *config, char *welcome) {
bool res = true;
int width = -1, height = -1, model = SSD1306;
char *p;
ESP_LOGI(TAG, "Initializing display with config: %s",config);
// no time for smart parsing - this is for tinkerers
if ((p = strcasestr(config, "width")) != NULL) width = atoi(strchr(p, '=') + 1);
if ((p = strcasestr(config, "height")) != NULL) height = atoi(strchr(p, '=') + 1);
if (strcasestr(config, "ssd1326")) model = SSD1326;
else if (strcasestr(config, "sh1106")) model = SH1106;
if (width == -1 || height == -1) {
ESP_LOGW(TAG, "No display configured %s [%d x %d]", config, width, height);
return false;
}
if (strstr(config, "I2C") && i2c_system_port != -1) {
int address = I2C_ADDRESS;
if ((p = strcasestr(config, "address")) != NULL) address = atoi(strchr(p, '=') + 1);
SSD13x6_I2CMasterInitDefault( i2c_system_port, -1, -1 ) ;
SSD13x6_I2CMasterAttachDisplayDefault( &Display, model, width, height, address, -1 );
SSD13x6_SetHFlip( &Display, strcasestr(config, "HFlip") ? true : false);
SSD13x6_SetVFlip( &Display, strcasestr(config, "VFlip") ? true : false);
SSD13x6_SetFont( &Display, &Font_droid_sans_fallback_15x17 );
SSD13x6_display.width = width;
SSD13x6_display.height = height;
text(DISPLAY_FONT_MEDIUM, DISPLAY_CENTERED, DISPLAY_CLEAR | DISPLAY_UPDATE, welcome);
ESP_LOGI(TAG, "Display is I2C on port %u", address);
} else if (strstr(config, "SPI") && spi_system_host != -1) {
int CS_pin = -1, speed = 0;
if ((p = strcasestr(config, "cs")) != NULL) CS_pin = atoi(strchr(p, '=') + 1);
if ((p = strcasestr(config, "speed")) != NULL) speed = atoi(strchr(p, '=') + 1);
SSD13x6_SPIMasterInitDefault( spi_system_host, spi_system_dc_gpio );
SSD13x6_SPIMasterAttachDisplayDefault( &Display, model, width, height, CS_pin, -1, speed );
SSD13x6_SetFont( &Display, &Font_droid_sans_fallback_15x17 );
SSD13x6_display.width = width;
SSD13x6_display.height = height;
text(DISPLAY_FONT_MEDIUM, DISPLAY_CENTERED, DISPLAY_CLEAR | DISPLAY_UPDATE, welcome);
ESP_LOGI(TAG, "Display is SPI host %u with cs:%d", spi_system_host, CS_pin);
} else {
res = false;
ESP_LOGW(TAG, "Unknown display type or no serial interface configured");
}
return res;
}
/****************************************************************************************
*
*/
static void clear(bool full, ...) {
bool commit = true;
if (full) {
SSD13x6_Clear( &Display, SSD_COLOR_BLACK );
} else {
va_list args;
va_start(args, full);
commit = va_arg(args, int);
int x1 = va_arg(args, int), y1 = va_arg(args, int), x2 = va_arg(args, int), y2 = va_arg(args, int);
if (x2 < 0) x2 = display->width - 1;
if (y2 < 0) y2 = display->height - 1;
SSD13x6_ClearWindow( &Display, x1, y1, x2, y2, SSD_COLOR_BLACK );
va_end(args);
}
SSD13x6_display.dirty = true;
if (commit) update();
}
/****************************************************************************************
* Set fonts for each line in text mode
*/
static bool set_font(int num, enum display_font_e font, int space) {
if (--num >= MAX_LINES) return false;
switch(font) {
case DISPLAY_FONT_LINE_1:
lines[num].font = &Font_line_1;
break;
case DISPLAY_FONT_LINE_2:
lines[num].font = &Font_line_2;
break;
case DISPLAY_FONT_SMALL:
lines[num].font = &Font_droid_sans_fallback_11x13;
break;
case DISPLAY_FONT_MEDIUM:
case DISPLAY_FONT_DEFAULT:
default:
lines[num].font = &Font_droid_sans_fallback_15x17;
break;
case DISPLAY_FONT_LARGE:
lines[num].font = &Font_droid_sans_fallback_24x28;
break;
case DISPLAY_FONT_SEGMENT:
if (Display.Height == 32) lines[num].font = &Font_Tarable7Seg_16x32;
else lines[num].font = &Font_Tarable7Seg_32x64;
break;
}
// re-calculate lines absolute position
lines[num].space = space;
lines[0].y = lines[0].space;
for (int i = 1; i <= num; i++) lines[i].y = lines[i-1].y + lines[i-1].font->Height + lines[i].space;
ESP_LOGI(TAG, "Adding line %u at %d (height:%u)", num + 1, lines[num].y, lines[num].font->Height);
if (lines[num].y + lines[num].font->Height > Display.Height) {
ESP_LOGW(TAG, "line does not fit display");
return false;
}
return true;
}
/****************************************************************************************
*
*/
static bool line(int num, int x, int attribute, char *text) {
int width;
// counting 1..n
num--;
SSD13x6_SetFont( &Display, lines[num].font );
if (attribute & DISPLAY_MONOSPACE) SSD13x6_FontForceMonospace( &Display, true );
width = SSD13x6_FontMeasureString( &Display, text );
// adjusting position, erase only EoL for rigth-justified
if (x == DISPLAY_RIGHT) x = Display.Width - width - 1;
else if (x == DISPLAY_CENTER) x = (Display.Width - width) / 2;
// erase if requested
if (attribute & DISPLAY_CLEAR) {
int y_min = max(0, lines[num].y), y_max = max(0, lines[num].y + lines[num].font->Height);
for (int c = (attribute & DISPLAY_ONLY_EOL) ? x : 0; c < Display.Width; c++)
for (int y = y_min; y < y_max; y++)
SSD13x6_DrawPixelFast( &Display, c, y, SSD_COLOR_BLACK );
}
SSD13x6_FontDrawString( &Display, x, lines[num].y, text, SSD_COLOR_WHITE );
ESP_LOGD(TAG, "displaying %s line %u (x:%d, attr:%u)", text, num+1, x, attribute);
// update whole display if requested
SSD13x6_display.dirty = true;
if (attribute & DISPLAY_UPDATE) update();
return width + x < Display.Width;
}
/****************************************************************************************
* Try to align string for better scrolling visual. there is probably much better to do
*/
static int stretch(int num, char *string, int max) {
char space[] = " ";
int len = strlen(string), extra = 0, boundary;
num--;
// we might already fit
SSD13x6_SetFont( &Display, lines[num].font );
if (SSD13x6_FontMeasureString( &Display, string ) <= Display.Width) return 0;
// add some space for better visual
strncat(string, space, max-len);
string[max] = '\0';
len = strlen(string);
// mark the end of the extended string
boundary = SSD13x6_FontMeasureString( &Display, string );
// add a full display width
while (len < max && SSD13x6_FontMeasureString( &Display, string ) - boundary < Display.Width) {
string[len++] = string[extra++];
string[len] = '\0';
}
return boundary;
}
/****************************************************************************************
*
*/
static void text(enum display_font_e font, enum display_pos_e pos, int attribute, char *text, ...) {
va_list args;
TextAnchor Anchor = TextAnchor_Center;
if (attribute & DISPLAY_CLEAR) SSD13x6_Clear( &Display, SSD_COLOR_BLACK );
if (!text) return;
va_start(args, text);
switch(font) {
case DISPLAY_FONT_LINE_1:
SSD13x6_SetFont( &Display, &Font_line_1 );
break;
case DISPLAY_FONT_LINE_2:
SSD13x6_SetFont( &Display, &Font_line_2 );
break;
case DISPLAY_FONT_SMALL:
SSD13x6_SetFont( &Display, &Font_droid_sans_fallback_11x13 );
break;
case DISPLAY_FONT_MEDIUM:
case DISPLAY_FONT_DEFAULT:
default:
SSD13x6_SetFont( &Display, &Font_droid_sans_fallback_15x17 );
break;
case DISPLAY_FONT_LARGE:
SSD13x6_SetFont( &Display, &Font_droid_sans_fallback_24x28 );
break;
case DISPLAY_FONT_SEGMENT:
if (Display.Height == 32) SSD13x6_SetFont( &Display, &Font_Tarable7Seg_16x32 );
else SSD13x6_SetFont( &Display, &Font_Tarable7Seg_32x64 );
break;
}
switch(pos) {
case DISPLAY_TOP_LEFT:
default:
Anchor = TextAnchor_NorthWest;
break;
case DISPLAY_MIDDLE_LEFT:
Anchor = TextAnchor_West;
break;
case DISPLAY_BOTTOM_LEFT:
Anchor = TextAnchor_SouthWest;
break;
case DISPLAY_CENTERED:
Anchor = TextAnchor_Center;
break;
}
ESP_LOGD(TAG, "SSDD13x6 displaying %s at %u with attribute %u", text, Anchor, attribute);
SSD13x6_FontDrawAnchoredString( &Display, Anchor, text, SSD_COLOR_WHITE );
SSD13x6_display.dirty = true;
if (attribute & DISPLAY_UPDATE) update();
va_end(args);
}
/****************************************************************************************
* Process graphic display data from column-oriented data (MSbit first)
*/
static void draw_cbr(u8_t *data, int width, int height) {
if (!height) height = Display.Height;
if (!width) width = Display.Width;
// need to do row/col swap and bit-reverse
int rows = height / 8;
for (int r = 0; r < rows; r++) {
uint8_t *optr = Display.Framebuffer + r*Display.Width, *iptr = data + r;
for (int c = width; --c >= 0;) {
*optr++ = BitReverseTable256[*iptr];;
iptr += rows;
}
}
SSD13x6_display.dirty = true;
}
/****************************************************************************************
* Process graphic display data MSBit first
* WARNING: this has not been tested yet
*/
static void draw_raw(int x1, int y1, int x2, int y2, bool by_column, bool MSb, u8_t *data) {
// default end point to display size
if (x2 == -1) x2 = Display.Width - 1;
if (y2 == -1) y2 = Display.Height - 1;
display->dirty = true;
// not a boundary draw
if (y1 % 8 || y2 % 8 || x1 % 8 | x2 % 8) {
ESP_LOGW(TAG, "can't write on non cols/rows boundaries for now");
} else {
// set addressing mode to match data
if (by_column) {
// copy the window and do row/col exchange
for (int r = y1/8; r <= y2/8; r++) {
uint8_t *optr = Display.Framebuffer + r*Display.Width + x1, *iptr = data + r;
for (int c = x1; c <= x2; c++) {
*optr++ = MSb ? BitReverseTable256[*iptr] : *iptr;
iptr += (y2-y1)/8 + 1;
}
}
} else {
// just copy the window inside the frame buffer
for (int r = y1/8; r <= y2/8; r++) {
uint8_t *optr = Display.Framebuffer + r*Display.Width + x1, *iptr = data + r*(x2-x1+1);
for (int c = x1; c <= x2; c++) *optr++ = *iptr++;
}
}
}
}
/****************************************************************************************
* Draw line
*/
static void draw_line( int x1, int y1, int x2, int y2) {
SSD13x6_DrawLine( &Display, x1, y1, x2, y2, SSD_COLOR_WHITE );
SSD13x6_display.dirty = true;
}
/****************************************************************************************
* Draw Box
*/
static void draw_box( int x1, int y1, int x2, int y2, bool fill) {
SSD13x6_DrawBox( &Display, x1, y1, x2, y2, SSD_COLOR_WHITE, fill );
SSD13x6_display.dirty = true;
}
/****************************************************************************************
* Brightness
*/
static void brightness(u8_t level) {
SSD13x6_DisplayOn( &Display );
SSD13x6_SetContrast( &Display, (uint8_t) level);
}
/****************************************************************************************
* Display On/Off
*/
static void on(bool state) {
if (state) SSD13x6_DisplayOn( &Display );
else SSD13x6_DisplayOff( &Display );
}
/****************************************************************************************
* Update
*/
static void update(void) {
if (SSD13x6_display.dirty) SSD13x6_Update( &Display );
SSD13x6_display.dirty = false;
}

View File

@@ -1,4 +1,4 @@
#include <ssd13x6_font.h>
#include <gds_font.h>
//WARNING: This Font Require X-GLCD Lib.
// You can not use it with MikroE GLCD Lib.
@@ -237,7 +237,7 @@ static const uint8_t Droid_Sans_Fallback11x13[ ] = {
0x05, 0x00, 0x10, 0xE4, 0x11, 0x00, 0x0E, 0x00, 0x02, 0xE4, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 // Code for char ÿ
};
const struct SSD13x6_FontDef Font_droid_sans_fallback_11x13 = {
const struct GDS_FontDef Font_droid_sans_fallback_11x13 = {
Droid_Sans_Fallback11x13,
11,
13,

View File

@@ -1,4 +1,4 @@
#include <ssd13x6_font.h>
#include <gds_font.h>
//WARNING: This Font Require X-GLCD Lib.
// You can not use it with MikroE GLCD Lib.
@@ -237,7 +237,7 @@ static const uint8_t Droid_Sans_Fallback15x17[ ] = {
0x07, 0xC0, 0x00, 0x01, 0x00, 0x03, 0x01, 0x10, 0x8C, 0x00, 0x00, 0x70, 0x00, 0x00, 0x0C, 0x00, 0x10, 0x03, 0x00, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 // Code for char ÿ
};
const struct SSD13x6_FontDef Font_droid_sans_fallback_15x17 = {
const struct GDS_FontDef Font_droid_sans_fallback_15x17 = {
Droid_Sans_Fallback15x17,
15,
17,

View File

@@ -1,4 +1,4 @@
#include <ssd13x6_font.h>
#include <gds_font.h>
//WARNING: This Font Require X-GLCD Lib.
// You can not use it with MikroE GLCD Lib.
@@ -237,7 +237,7 @@ static const uint8_t Droid_Sans_Fallback24x28[ ] = {
0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1C, 0x00, 0x00, 0x00, 0x7C, 0x00, 0x0C, 0xE0, 0xF0, 0x03, 0x0C, 0xE0, 0x80, 0x0F, 0x0E, 0x00, 0x00, 0xFE, 0x07, 0x00, 0x00, 0xF0, 0x01, 0x00, 0x00, 0x7E, 0x00, 0xE0, 0x80, 0x0F, 0x00, 0xE0, 0xF0, 0x03, 0x00, 0x00, 0x7C, 0x00, 0x00, 0x00, 0x1C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 // Code for char ÿ
};
const struct SSD13x6_FontDef Font_droid_sans_fallback_24x28 = {
const struct GDS_FontDef Font_droid_sans_fallback_24x28 = {
Droid_Sans_Fallback24x28,
24,
28,

View File

@@ -1,4 +1,4 @@
#include <ssd13x6_font.h>
#include <gds_font.h>
//WARNING: This Font Require X-GLCD Lib.
// You can not use it with MikroE GLCD Lib.
@@ -237,7 +237,7 @@ static const uint8_t Droid_Sans_Mono13x24[ ] = {
0x0C, 0x00, 0x00, 0x00, 0x80, 0x00, 0xC0, 0x80, 0x07, 0xC0, 0x00, 0x1E, 0xC0, 0x18, 0xF8, 0xC0, 0x18, 0xC0, 0x7F, 0x00, 0x00, 0x1E, 0x00, 0xC0, 0x07, 0x18, 0xF8, 0x00, 0x18, 0x3E, 0x00, 0x80, 0x07, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00 // Code for char ÿ
};
const struct SSD13x6_FontDef Font_droid_sans_mono_13x24 = {
const struct GDS_FontDef Font_droid_sans_mono_13x24 = {
Droid_Sans_Mono13x24,
13,
24,

View File

@@ -1,4 +1,4 @@
#include <ssd13x6_font.h>
#include <gds_font.h>
//WARNING: This Font Require X-GLCD Lib.
// You can not use it with MikroE GLCD Lib.
@@ -237,7 +237,7 @@ static const uint8_t Droid_Sans_Mono16x31[ ] = {
0x0F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x60, 0x00, 0x78, 0x00, 0x60, 0x00, 0xF8, 0x01, 0x60, 0x00, 0xE0, 0x07, 0x60, 0xE0, 0x00, 0x3F, 0x70, 0xE0, 0x00, 0xFC, 0x3C, 0x40, 0x00, 0xE0, 0x1F, 0x00, 0x00, 0xE0, 0x07, 0x40, 0x00, 0xF8, 0x01, 0xE0, 0x00, 0x3F, 0x00, 0xE0, 0xC0, 0x0F, 0x00, 0x00, 0xF8, 0x01, 0x00, 0x00, 0x78, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 // Code for char ÿ
};
const struct SSD13x6_FontDef Font_droid_sans_mono_16x31 = {
const struct GDS_FontDef Font_droid_sans_mono_16x31 = {
Droid_Sans_Mono16x31,
16,
31,

View File

@@ -1,4 +1,4 @@
#include <ssd13x6_font.h>
#include <gds_font.h>
//WARNING: This Font Require X-GLCD Lib.
// You can not use it with MikroE GLCD Lib.
@@ -237,7 +237,7 @@ static const uint8_t Droid_Sans_Mono7x13[ ] = {
0x06, 0x00, 0x00, 0x30, 0x10, 0xC4, 0x10, 0x00, 0x0F, 0xC4, 0x01, 0x30, 0x00, 0x00, 0x00 // Code for char ÿ
};
const struct SSD13x6_FontDef Font_droid_sans_mono_7x13 = {
const struct GDS_FontDef Font_droid_sans_mono_7x13 = {
Droid_Sans_Mono7x13,
7,
13,

View File

@@ -1,4 +1,4 @@
#include <ssd13x6_font.h>
#include <gds_font.h>
//WARNING: This Font Require X-GLCD Lib.
// You can not use it with MikroE GLCD Lib.
@@ -237,7 +237,7 @@ static const uint8_t Liberation_Mono13x21[ ] = {
0x0C, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0xC0, 0x03, 0x10, 0x00, 0x0F, 0x10, 0x0C, 0x3C, 0x18, 0x00, 0xE0, 0x0D, 0x00, 0x80, 0x07, 0x00, 0xE0, 0x01, 0x0C, 0x3C, 0x00, 0x00, 0x0F, 0x00, 0xC0, 0x03, 0x00, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00 // Code for char ÿ
};
const struct SSD13x6_FontDef Font_liberation_mono_13x21 = {
const struct GDS_FontDef Font_liberation_mono_13x21 = {
Liberation_Mono13x21,
13,
21,

View File

@@ -1,4 +1,4 @@
#include <ssd13x6_font.h>
#include <gds_font.h>
//WARNING: This Font Require X-GLCD Lib.
// You can not use it with MikroE GLCD Lib.
@@ -237,7 +237,7 @@ static const uint8_t Liberation_Mono17x30[] = {
0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x30, 0x00, 0x3F, 0x00, 0x30, 0x00, 0xFC, 0x01, 0x30, 0x38, 0xE0, 0x07, 0x38, 0x38, 0x00, 0x3F, 0x1C, 0x00, 0x00, 0xFC, 0x0F, 0x00, 0x00, 0xE0, 0x07, 0x00, 0x00, 0xF8, 0x01, 0x38, 0x00, 0x3F, 0x00, 0x38, 0xE0, 0x07, 0x00, 0x38, 0xFC, 0x01, 0x00, 0x00, 0x3F, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 // Code for char ÿ
};
const struct SSD13x6_FontDef Font_liberation_mono_17x30 = {
const struct GDS_FontDef Font_liberation_mono_17x30 = {
Liberation_Mono17x30,
17,
30,

View File

@@ -1,4 +1,4 @@
#include <ssd13x6_font.h>
#include <gds_font.h>
//WARNING: This Font Require X-GLCD Lib.
// You can not use it with MikroE GLCD Lib.
@@ -237,7 +237,7 @@ static const uint8_t Liberation_Mono9x15[ ] = {
0x08, 0x00, 0x00, 0x30, 0x40, 0xC6, 0x41, 0x06, 0x67, 0x00, 0x18, 0x06, 0x07, 0xC6, 0x01, 0x30, 0x00, 0x00, 0x00 // Code for char ÿ
};
const struct SSD13x6_FontDef Font_liberation_mono_9x15 = {
const struct GDS_FontDef Font_liberation_mono_9x15 = {
Liberation_Mono9x15,
9,
15,

View File

@@ -1,4 +1,4 @@
#include <ssd13x6_font.h>
#include <gds_font.h>
//WARNING: This Font Require X-GLCD Lib.
// You can not use it with MikroE GLCD Lib.
@@ -214,7 +214,7 @@ static const uint8_t Square721_BT11x14[] = {
0x06, 0x00, 0x00, 0xE8, 0x03, 0xB0, 0x04, 0xA0, 0x04, 0xA0, 0x04, 0xE0, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 // Code for char è
};
const struct SSD13x6_FontDef Font_line_1 = {
const struct GDS_FontDef Font_line_1 = {
Square721_BT11x14,
11,
14,

View File

@@ -1,4 +1,4 @@
#include <ssd13x6_font.h>
#include <gds_font.h>
//WARNING: This Font Require X-GLCD Lib.
// You can not use it with MikroE GLCD Lib.
@@ -237,7 +237,7 @@ static const uint8_t Archivo_Narrow18x24[] = {
0x0A, 0x00, 0x01, 0x00, 0x00, 0x1F, 0xE0, 0x38, 0xFF, 0xE0, 0x38, 0xFC, 0xF7, 0x00, 0xE0, 0x7F, 0x00, 0x00, 0x3F, 0x38, 0xF0, 0x0F, 0x38, 0xFF, 0x01, 0x00, 0x1F, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 // Code for char ÿ
};
const struct SSD13x6_FontDef Font_line_2 = {
const struct GDS_FontDef Font_line_2 = {
Archivo_Narrow18x24,
18,
24,

View File

@@ -1,4 +1,4 @@
#include <ssd13x6_font.h>
#include <gds_font.h>
//WARNING: This Font Require X-GLCD Lib.
// You can not use it with MikroE GLCD Lib.
@@ -109,7 +109,7 @@ static const uint8_t Tarable7Seg_16x32[ ] = {
0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 // Code for char 
};
const struct SSD13x6_FontDef Font_Tarable7Seg_16x32 = {
const struct GDS_FontDef Font_Tarable7Seg_16x32 = {
Tarable7Seg_16x32,
16,
32,

View File

@@ -1,4 +1,4 @@
#include <ssd13x6_font.h>
#include <gds_font.h>
//WARNING: This Font Require X-GLCD Lib.
// You can not use it with MikroE GLCD Lib.
@@ -109,7 +109,7 @@ static const uint8_t Tarable7Seg_32x64[ ] = {
0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 // Code for char 
};
const struct SSD13x6_FontDef Font_Tarable7Seg_32x64 = {
const struct GDS_FontDef Font_Tarable7Seg_32x64 = {
Tarable7Seg_32x64,
32,
64,

View File

@@ -1,123 +0,0 @@
/**
* Copyright (c) 2017-2018 Tara Keeling
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#include <stdio.h>
#include <stdint.h>
#include <stdbool.h>
#include <string.h>
#include <driver/spi_master.h>
#include <driver/gpio.h>
#include <freertos/task.h>
#include "ssd13x6.h"
#include "ssd13x6_default_if.h"
static const int SSD13x6_SPI_Command_Mode = 0;
static const int SSD13x6_SPI_Data_Mode = 1;
static spi_host_device_t SPIHost;
static int DCPin;
static bool SPIDefaultWriteBytes( spi_device_handle_t SPIHandle, int WriteMode, const uint8_t* Data, size_t DataLength );
static bool SPIDefaultWriteCommand( struct SSD13x6_Device* DeviceHandle, SSDCmd Command );
static bool SPIDefaultWriteData( struct SSD13x6_Device* DeviceHandle, const uint8_t* Data, size_t DataLength );
static bool SPIDefaultReset( struct SSD13x6_Device* DeviceHandle );
bool SSD13x6_SPIMasterInitDefault( int SPI, int DC ) {
SPIHost = SPI;
DCPin = DC;
return true;
}
bool SSD13x6_SPIMasterAttachDisplayDefault( struct SSD13x6_Device* DeviceHandle, int Model, int Width, int Height, int CSPin, int RSTPin, int Speed ) {
spi_device_interface_config_t SPIDeviceConfig;
spi_device_handle_t SPIDeviceHandle;
NullCheck( DeviceHandle, return false );
if (CSPin >= 0) {
ESP_ERROR_CHECK_NONFATAL( gpio_set_direction( CSPin, GPIO_MODE_OUTPUT ), return false );
ESP_ERROR_CHECK_NONFATAL( gpio_set_level( CSPin, 0 ), return false );
}
memset( &SPIDeviceConfig, 0, sizeof( spi_device_interface_config_t ) );
SPIDeviceConfig.clock_speed_hz = Speed > 0 ? Speed : SPI_MASTER_FREQ_8M;
SPIDeviceConfig.spics_io_num = CSPin;
SPIDeviceConfig.queue_size = 1;
if ( RSTPin >= 0 ) {
ESP_ERROR_CHECK_NONFATAL( gpio_set_direction( RSTPin, GPIO_MODE_OUTPUT ), return false );
ESP_ERROR_CHECK_NONFATAL( gpio_set_level( RSTPin, 0 ), return false );
}
ESP_ERROR_CHECK_NONFATAL( spi_bus_add_device( SPIHost, &SPIDeviceConfig, &SPIDeviceHandle ), return false );
memset( DeviceHandle, 0, sizeof( struct SSD13x6_Device ) );
DeviceHandle->Model = Model;
return SSD13x6_Init_SPI( DeviceHandle,
Width,
Height,
RSTPin,
CSPin,
SPIDeviceHandle,
SPIDefaultWriteCommand,
SPIDefaultWriteData,
SPIDefaultReset
);
}
static bool SPIDefaultWriteBytes( spi_device_handle_t SPIHandle, int WriteMode, const uint8_t* Data, size_t DataLength ) {
spi_transaction_t SPITransaction = { 0 };
NullCheck( SPIHandle, return false );
NullCheck( Data, return false );
if ( DataLength > 0 ) {
gpio_set_level( DCPin, WriteMode );
SPITransaction.length = DataLength * 8;
SPITransaction.tx_buffer = Data;
// only do polling as we don't have contention on SPI (otherwise DMA for transfers > 16 bytes)
ESP_ERROR_CHECK_NONFATAL( spi_device_polling_transmit(SPIHandle, &SPITransaction), return false );
}
return true;
}
static bool SPIDefaultWriteCommand( struct SSD13x6_Device* DeviceHandle, SSDCmd Command ) {
static uint8_t CommandByte = 0;
NullCheck( DeviceHandle, return false );
NullCheck( DeviceHandle->SPIHandle, return false );
CommandByte = Command;
return SPIDefaultWriteBytes( DeviceHandle->SPIHandle, SSD13x6_SPI_Command_Mode, &CommandByte, 1 );
}
static bool SPIDefaultWriteData( struct SSD13x6_Device* DeviceHandle, const uint8_t* Data, size_t DataLength ) {
NullCheck( DeviceHandle, return false );
NullCheck( DeviceHandle->SPIHandle, return false );
return SPIDefaultWriteBytes( DeviceHandle->SPIHandle, SSD13x6_SPI_Data_Mode, Data, DataLength );
}
static bool SPIDefaultReset( struct SSD13x6_Device* DeviceHandle ) {
NullCheck( DeviceHandle, return false );
if ( DeviceHandle->RSTPin >= 0 ) {
ESP_ERROR_CHECK_NONFATAL( gpio_set_level( DeviceHandle->RSTPin, 0 ), return false );
vTaskDelay( pdMS_TO_TICKS( 100 ) );
ESP_ERROR_CHECK_NONFATAL( gpio_set_level( DeviceHandle->RSTPin, 1 ), return false );
}
return true;
}

View File

@@ -1,351 +0,0 @@
/**
* Copyright (c) 2017-2018 Tara Keeling
*
* 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 <stdlib.h>
#include <math.h>
#include <esp_heap_caps.h>
#include "ssd13x6.h"
#define SHADOW_BUFFER
// used by both but different
static uint8_t SSDCmd_Set_Display_Start_Line;
static uint8_t SSDCmd_Set_Display_Offset;
static uint8_t SSDCmd_Set_Column_Address;
static uint8_t SSDCmd_Set_Display_CLK;
static uint8_t SSDCmd_Set_Page_Address;
// misc boundaries
static uint8_t SSD13x6_Max_Col;
static const uint8_t SSD13x6_Max_Row = 7;
static bool SSD13x6_Init( struct SSD13x6_Device* DeviceHandle, int Width, int Height );
int SSD13x6_GetCaps( struct SSD13x6_Device* DeviceHandle ) {
if (DeviceHandle->Model == SH1106) return 0;
else return CAPS_COLUMN_RANGE | CAPS_PAGE_RANGE | CAPS_ADDRESS_VERTICAL;
}
bool SSD13x6_WriteCommand( struct SSD13x6_Device* DeviceHandle, SSDCmd SSDCommand ) {
NullCheck( DeviceHandle->WriteCommand, return false );
return ( DeviceHandle->WriteCommand ) ( DeviceHandle, SSDCommand );
}
bool SSD13x6_WriteData( struct SSD13x6_Device* DeviceHandle, uint8_t* Data, size_t DataLength ) {
NullCheck( DeviceHandle->WriteData, return false );
return ( DeviceHandle->WriteData ) ( DeviceHandle, Data, DataLength );
}
void SSD13x6_SetMuxRatio( struct SSD13x6_Device* DeviceHandle, uint8_t Ratio ) {
SSD13x6_WriteCommand( DeviceHandle, 0xA8 );
SSD13x6_WriteCommand( DeviceHandle, Ratio );
}
void SSD13x6_SetDisplayOffset( struct SSD13x6_Device* DeviceHandle, uint8_t Offset ) {
SSD13x6_WriteCommand( DeviceHandle, SSDCmd_Set_Display_Offset );
SSD13x6_WriteCommand( DeviceHandle, Offset );
}
void SSD13x6_SetDisplayStartLine( struct SSD13x6_Device* DeviceHandle, int Line ) {
SSD13x6_WriteCommand( DeviceHandle, SSDCmd_Set_Display_Start_Line + ( uint32_t ) ( Line & 0x1F ) );
}
void SSD13x6_SetContrast( struct SSD13x6_Device* DeviceHandle, uint8_t Contrast ) {
SSD13x6_WriteCommand( DeviceHandle, 0x81 );
SSD13x6_WriteCommand( DeviceHandle, Contrast );
}
void SSD13x6_EnableDisplayRAM( struct SSD13x6_Device* DeviceHandle ) {
SSD13x6_WriteCommand( DeviceHandle, 0xA4 );
}
void SSD13x6_DisableDisplayRAM( struct SSD13x6_Device* DeviceHandle ) {
SSD13x6_WriteCommand( DeviceHandle, 0xA5 );
}
void SSD13x6_SetInverted( struct SSD13x6_Device* DeviceHandle, bool Inverted ) {
SSD13x6_WriteCommand( DeviceHandle, Inverted ? 0xA7 : 0xA6 );
}
void SSD13x6_SetDisplayClocks( struct SSD13x6_Device* DeviceHandle, uint32_t DisplayClockDivider, uint32_t OSCFrequency ) {
DisplayClockDivider&= 0x0F;
OSCFrequency&= 0x0F;
SSD13x6_WriteCommand( DeviceHandle, SSDCmd_Set_Display_CLK );
SSD13x6_WriteCommand( DeviceHandle, ( ( OSCFrequency << 4 ) | DisplayClockDivider ) );
}
void SSD13x6_DisplayOn( struct SSD13x6_Device* DeviceHandle ) {
SSD13x6_WriteCommand( DeviceHandle, 0xAF );
}
void SSD13x6_DisplayOff( struct SSD13x6_Device* DeviceHandle ) {
SSD13x6_WriteCommand( DeviceHandle, 0xAE );
}
void SSD132x_ReMap( struct SSD13x6_Device* DeviceHandle ) {
SSD13x6_WriteCommand( DeviceHandle, 0xA0 );
SSD13x6_WriteCommand( DeviceHandle, DeviceHandle->ReMap );
}
void SSD13x6_SetDisplayAddressMode( struct SSD13x6_Device* DeviceHandle, SSD13x6_AddressMode AddressMode ) {
switch (DeviceHandle->Model) {
case SH1106:
// does not exist on SH1106
break;
case SSD1306:
SSD13x6_WriteCommand( DeviceHandle, 0x20 );
SSD13x6_WriteCommand( DeviceHandle, AddressMode );
break;
case SSD1326:
DeviceHandle->ReMap = (AddressMode == AddressMode_Horizontal) ? (DeviceHandle->ReMap & ~0x80) : (DeviceHandle->ReMap | 0x80);
SSD132x_ReMap(DeviceHandle);
break;
}
}
void SSD13x6_Update( struct SSD13x6_Device* DeviceHandle ) {
#ifdef SHADOW_BUFFER
// not sure the compiler does not have to redo all calculation in for loops, so local it is
int width = DeviceHandle->Width, rows = DeviceHandle->Height / 8;
uint8_t *optr = DeviceHandle->Shadowbuffer, *iptr = DeviceHandle->Framebuffer;
// by row, find first and last columns that have been updated
for (int r = 0; r < rows; r++) {
uint8_t first = 0, last;
for (int c = 0; c < width; c++) {
if (*iptr != *optr) {
if (!first) first = c + 1;
last = c ;
}
*optr++ = *iptr++;
}
// now update the display by "byte rows"
if (first--) {
SSD13x6_SetColumnAddress( DeviceHandle, first, last );
SSD13x6_SetPageAddress( DeviceHandle, r, r);
SSD13x6_WriteData( DeviceHandle, DeviceHandle->Shadowbuffer + r*width + first, last - first + 1);
}
}
#else
if (DeviceHandle->Model == SH1106) {
// SH1106 requires a page-by-page update and has no end Page/Column
for (int i = 0; i < DeviceHandle->Height / 8 ; i++) {
SSD13x6_SetPageAddress( DeviceHandle, i, 0);
SSD13x6_SetColumnAddress( DeviceHandle, 0, 0);
SSD13x6_WriteData( DeviceHandle, DeviceHandle->Framebuffer + i*DeviceHandle->Width, DeviceHandle->Width );
}
} else {
// others have an automatic counter and end Page/Column
SSD13x6_SetColumnAddress( DeviceHandle, 0, DeviceHandle->Width - 1);
SSD13x6_SetPageAddress( DeviceHandle, 0, DeviceHandle->Height / 8 - 1);
SSD13x6_WriteData( DeviceHandle, DeviceHandle->Framebuffer, DeviceHandle->FramebufferSize );
}
#endif
}
void SSD13x6_WriteRawData( struct SSD13x6_Device* DeviceHandle, uint8_t* Data, size_t DataLength ) {
NullCheck( Data, return );
DataLength = DataLength > DeviceHandle->FramebufferSize ? DeviceHandle->FramebufferSize : DataLength;
if ( DataLength > 0 ) SSD13x6_WriteData( DeviceHandle, Data, DataLength );
}
void SSD13x6_SetHFlip( struct SSD13x6_Device* DeviceHandle, bool On ) {
switch (DeviceHandle->Model) {
case SH1106:
case SSD1306:
SSD13x6_WriteCommand( DeviceHandle, On ? 0xA1 : 0xA0 );
break;
case SSD1326:
DeviceHandle->ReMap = On ? (DeviceHandle->ReMap | 0x01) : (DeviceHandle->ReMap & ~0x01);
SSD132x_ReMap(DeviceHandle);
break;
}
}
void SSD13x6_SetVFlip( struct SSD13x6_Device* DeviceHandle, bool On ) {
switch (DeviceHandle->Model) {
case SH1106:
case SSD1306:
SSD13x6_WriteCommand( DeviceHandle, On ? 0xC8 : 0xC0 );
break;
case SSD1326:
DeviceHandle->ReMap = On ? (DeviceHandle->ReMap | 0x05) : (DeviceHandle->ReMap & ~0x05);
SSD132x_ReMap( DeviceHandle );
break;
}
}
void SSD13x6_SetColumnAddress( struct SSD13x6_Device* DeviceHandle, uint8_t Start, uint8_t End ) {
CheckBounds( Start > SSD13x6_Max_Col, return );
CheckBounds( End > SSD13x6_Max_Col, return );
// on SH1106, there is no "end column"
if (DeviceHandle->Model == SH1106) {
// well, unfortunately this driver is 132 colums but most displays are 128...
if (DeviceHandle->Width != 132) Start += 2;
SSD13x6_WriteCommand( DeviceHandle, 0x10 | (Start >> 4) );
SSD13x6_WriteCommand( DeviceHandle, 0x00 | (Start & 0x0f) );
} else {
SSD13x6_WriteCommand( DeviceHandle, SSDCmd_Set_Column_Address );
SSD13x6_WriteCommand( DeviceHandle, Start );
SSD13x6_WriteCommand( DeviceHandle, End );
}
}
void SSD13x6_SetPageAddress( struct SSD13x6_Device* DeviceHandle, uint8_t Start, uint8_t End ) {
NullCheck( DeviceHandle, return );
CheckBounds( Start > SSD13x6_Max_Row, return );
CheckBounds( End > SSD13x6_Max_Row, return );
// on SH1106, there is no "end page"
if (DeviceHandle->Model == SH1106) {
SSD13x6_WriteCommand( DeviceHandle, 0xB0 | Start );
} else {
// in case of SSD1326, this is sub-optimal as it can address by line, not by page
if (DeviceHandle->Model != SSD1306) {
Start *= 8;
End = (End + 1) * 8 - 1;
}
SSD13x6_WriteCommand( DeviceHandle, SSDCmd_Set_Page_Address );
SSD13x6_WriteCommand( DeviceHandle, Start );
SSD13x6_WriteCommand( DeviceHandle, End );
}
}
bool SSD13x6_HWReset( struct SSD13x6_Device* DeviceHandle ) {
NullCheck( DeviceHandle, return 0 );
if ( DeviceHandle->Reset != NULL ) {
return ( DeviceHandle->Reset ) ( DeviceHandle );
}
/* This should always return true if there is no reset callback as
* no error would have occurred during the non existant reset.
*/
return true;
}
static bool SSD13x6_Init( struct SSD13x6_Device* DeviceHandle, int Width, int Height ) {
DeviceHandle->Width = Width;
DeviceHandle->Height = Height;
#ifdef SHADOW_BUFFER
DeviceHandle->Shadowbuffer = heap_caps_malloc( DeviceHandle->FramebufferSize, MALLOC_CAP_INTERNAL | MALLOC_CAP_DMA );
memset( DeviceHandle->Shadowbuffer, 0xFF, DeviceHandle->FramebufferSize );
#endif
SSD13x6_HWReset( DeviceHandle );
SSD13x6_DisplayOff( DeviceHandle );
if (DeviceHandle->Model == SSD1306 || DeviceHandle->Model == SH1106) {
SSDCmd_Set_Display_Start_Line = 0x40;
SSDCmd_Set_Display_Offset = 0xD3;
SSDCmd_Set_Column_Address = 0x21,
SSDCmd_Set_Display_CLK = 0xD5;
SSDCmd_Set_Page_Address = 0x22;
SSD13x6_Max_Col = 127;
if (DeviceHandle->Model == SSD1306) {
// charge pump regulator, do direct init
SSD13x6_WriteCommand( DeviceHandle, 0x8D );
SSD13x6_WriteCommand( DeviceHandle, 0x14 );
// COM pins HW config (alternative:EN if 64, DIS if 32, remap:DIS) - some display might need something difference
SSD13x6_WriteCommand( DeviceHandle, 0xDA );
SSD13x6_WriteCommand( DeviceHandle, ((Height == 64 ? 1 : 0) << 4) | (0 < 5) );
} else {
// charge pump regulator, do direct init
SSD13x6_WriteCommand( DeviceHandle, 0xAD );
SSD13x6_WriteCommand( DeviceHandle, 0x8B );
// COM pins HW config (alternative:EN) - some display might need something difference
SSD13x6_WriteCommand( DeviceHandle, 0xDA );
SSD13x6_WriteCommand( DeviceHandle, 1 << 4);
}
} else if (DeviceHandle->Model == SSD1326) {
SSDCmd_Set_Display_Start_Line = 0xA1;
SSDCmd_Set_Display_Offset = 0xA2;
SSDCmd_Set_Column_Address = 0x15;
SSDCmd_Set_Display_CLK = 0xB3;
SSDCmd_Set_Page_Address = 0x75; // not really a page but a row
SSD13x6_Max_Col = 255;
// no gray scale
DeviceHandle->ReMap |= 0x10;
SSD132x_ReMap( DeviceHandle );
}
SSD13x6_SetMuxRatio( DeviceHandle, Height - 1 );
SSD13x6_SetDisplayOffset( DeviceHandle, 0x00 );
SSD13x6_SetDisplayStartLine( DeviceHandle, 0 );
SSD13x6_SetContrast( DeviceHandle, 0x7F );
SSD13x6_DisableDisplayRAM( DeviceHandle );
SSD13x6_SetVFlip( DeviceHandle, false );
SSD13x6_SetHFlip( DeviceHandle, false );
SSD13x6_SetInverted( DeviceHandle, false );
SSD13x6_SetDisplayClocks( DeviceHandle, 0, 8 );
SSD13x6_SetDisplayAddressMode( DeviceHandle, AddressMode_Horizontal );
SSD13x6_SetColumnAddress( DeviceHandle, 0, DeviceHandle->Width - 1 );
SSD13x6_SetPageAddress( DeviceHandle, 0, ( DeviceHandle->Height / 8 ) - 1 );
SSD13x6_EnableDisplayRAM( DeviceHandle );
SSD13x6_DisplayOn( DeviceHandle );
SSD13x6_Update( DeviceHandle );
return true;
}
bool SSD13x6_Init_I2C( struct SSD13x6_Device* DeviceHandle, int Width, int Height, int I2CAddress, int ResetPin, WriteCommandProc WriteCommand, WriteDataProc WriteData, ResetProc Reset ) {
NullCheck( DeviceHandle, return false );
NullCheck( WriteCommand, return false );
NullCheck( WriteData, return false );
DeviceHandle->WriteCommand = WriteCommand;
DeviceHandle->WriteData = WriteData;
DeviceHandle->Reset = Reset;
DeviceHandle->Address = I2CAddress;
DeviceHandle->RSTPin = ResetPin;
DeviceHandle->FramebufferSize = ( Width * Height ) / 8;
DeviceHandle->Framebuffer = calloc( 1, DeviceHandle->FramebufferSize );
NullCheck( DeviceHandle->Framebuffer, return false );
return SSD13x6_Init( DeviceHandle, Width, Height );
}
bool SSD13x6_Init_SPI( struct SSD13x6_Device* DeviceHandle, int Width, int Height, int ResetPin, int CSPin, spi_device_handle_t SPIHandle, WriteCommandProc WriteCommand, WriteDataProc WriteData, ResetProc Reset ) {
NullCheck( DeviceHandle, return false );
NullCheck( WriteCommand, return false );
NullCheck( WriteData, return false );
DeviceHandle->WriteCommand = WriteCommand;
DeviceHandle->WriteData = WriteData;
DeviceHandle->Reset = Reset;
DeviceHandle->SPIHandle = SPIHandle;
DeviceHandle->RSTPin = ResetPin;
DeviceHandle->CSPin = CSPin;
DeviceHandle->FramebufferSize = ( Width * Height ) / 8;
#ifdef SHADOW_BUFFER
DeviceHandle->Framebuffer = calloc( 1, DeviceHandle->FramebufferSize );
#else
DeviceHandle->Framebuffer = heap_caps_calloc( 1, DeviceHandle->FramebufferSize, MALLOC_CAP_INTERNAL | MALLOC_CAP_DMA );
#endif
NullCheck( DeviceHandle->Framebuffer, return false );
return SSD13x6_Init( DeviceHandle, Width, Height );
}

View File

@@ -1,99 +0,0 @@
#ifndef _SSD13X6_H_
#define _SSD13X6_H_
/* For uint(X)_t */
#include <stdint.h>
/* For booooool */
#include <stdbool.h>
#include "sdkconfig.h"
#include "ssd13x6_err.h"
#define SSD_ALWAYS_INLINE __attribute__( ( always_inline ) )
#define CAPS_COLUMN_RANGE 0x01
#define CAPS_PAGE_RANGE 0x02
#define CAPS_ADDRESS_VERTICAL 0x04
#if ! defined BIT
#define BIT( n ) ( 1 << n )
#endif
typedef uint8_t SSDCmd;
typedef enum {
AddressMode_Horizontal = 0,
AddressMode_Vertical,
AddressMode_Page,
AddressMode_Invalid
} SSD13x6_AddressMode;
struct SSD13x6_Device;
/*
* These can optionally return a succeed/fail but are as of yet unused in the driver.
*/
typedef bool ( *WriteCommandProc ) ( struct SSD13x6_Device* DeviceHandle, SSDCmd Command );
typedef bool ( *WriteDataProc ) ( struct SSD13x6_Device* DeviceHandle, const uint8_t* Data, size_t DataLength );
typedef bool ( *ResetProc ) ( struct SSD13x6_Device* DeviceHandle );
struct spi_device_t;
typedef struct spi_device_t* spi_device_handle_t;
struct SSD13x6_FontDef;
struct SSD13x6_Device {
/* I2C Specific */
int Address;
/* SPI Specific */
spi_device_handle_t SPIHandle;
int RSTPin;
int CSPin;
/* Everything else */
int Width;
int Height;
enum { SSD1306, SSD1326, SH1106 } Model;
uint8_t ReMap;
uint8_t* Framebuffer, *Shadowbuffer;
int FramebufferSize;
WriteCommandProc WriteCommand;
WriteDataProc WriteData;
ResetProc Reset;
const struct SSD13x6_FontDef* Font;
bool FontForceProportional;
bool FontForceMonospace;
};
void SSD13x6_SetMuxRatio( struct SSD13x6_Device* DeviceHandle, uint8_t Ratio );
void SSD13x6_SetDisplayOffset( struct SSD13x6_Device* DeviceHandle, uint8_t Offset );
void SSD13x6_SetDisplayStartLines( struct SSD13x6_Device* DeviceHandle );
void SSD13x6_SetSegmentRemap( struct SSD13x6_Device* DeviceHandle, bool Remap );
void SSD13x6_SetContrast( struct SSD13x6_Device* DeviceHandle, uint8_t Contrast );
void SSD13x6_EnableDisplayRAM( struct SSD13x6_Device* DeviceHandle );
void SSD13x6_DisableDisplayRAM( struct SSD13x6_Device* DeviceHandle );
void SSD13x6_SetInverted( struct SSD13x6_Device* DeviceHandle, bool Inverted );
void SSD13x6_SetHFlip( struct SSD13x6_Device* DeviceHandle, bool On );
void SSD13x6_SetVFlip( struct SSD13x6_Device* DeviceHandle, bool On );
void SSD13x6_DisplayOn( struct SSD13x6_Device* DeviceHandle );
void SSD13x6_DisplayOff( struct SSD13x6_Device* DeviceHandle );
void SSD13x6_SetDisplayAddressMode( struct SSD13x6_Device* DeviceHandle, SSD13x6_AddressMode AddressMode );
void SSD13x6_Update( struct SSD13x6_Device* DeviceHandle );
void SSD13x6_SetDisplayClocks( struct SSD13x6_Device* DeviceHandle, uint32_t DisplayClockDivider, uint32_t OSCFrequency );
void SSD13x6_SetColumnAddress( struct SSD13x6_Device* DeviceHandle, uint8_t Start, uint8_t End );
void SSD13x6_SetPageAddress( struct SSD13x6_Device* DeviceHandle, uint8_t Start, uint8_t End );
bool SSD13x6_HWReset( struct SSD13x6_Device* DeviceHandle );
bool SSD13x6_Init_I2C( struct SSD13x6_Device* DeviceHandle, int Width, int Height, int I2CAddress, int ResetPin, WriteCommandProc WriteCommand, WriteDataProc WriteData, ResetProc Reset );
bool SSD13x6_Init_SPI( struct SSD13x6_Device* DeviceHandle, int Width, int Height, int ResetPin, int CSPin, spi_device_handle_t SPIHandle, WriteCommandProc WriteCommand, WriteDataProc WriteData, ResetProc Reset );
int SSD13x6_GetCaps( struct SSD13x6_Device* DeviceHandle );
void SSD13x6_WriteRawData( struct SSD13x6_Device* DeviceHandle, uint8_t* Data, size_t DataLength );
#endif

View File

@@ -1,18 +0,0 @@
#ifndef _SSD13x6_DEFAULT_IF_H_
#define _SSD13x6_DEFAULT_IF_H_
#ifdef __cplusplus
extern "C" {
#endif
bool SSD13x6_I2CMasterInitDefault( int PortNumber, int SDA, int SCL );
bool SSD13x6_I2CMasterAttachDisplayDefault( struct SSD13x6_Device* DisplayHandle, int Model, int Width, int Height, int I2CAddress, int RSTPin );
bool SSD13x6_SPIMasterInitDefault( int SPI, int DC);
bool SSD13x6_SPIMasterAttachDisplayDefault( struct SSD13x6_Device* DeviceHandle, int Model, int Width, int Height, int CSPin, int RSTPin, int Speed );
#ifdef __cplusplus
}
#endif
#endif

View File

@@ -1,253 +0,0 @@
/**
* Copyright (c) 2017-2018 Tara Keeling
*
* 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 <stdlib.h>
#include <math.h>
#include <esp_attr.h>
#include "ssd13x6.h"
#include "ssd13x6_draw.h"
#undef NullCheck
#define NullCheck(X,Y)
__attribute__( ( always_inline ) ) static inline bool IsPixelVisible( struct SSD13x6_Device* DeviceHandle, int x, int y ) {
bool Result = (
( x >= 0 ) &&
( x < DeviceHandle->Width ) &&
( y >= 0 ) &&
( y < DeviceHandle->Height )
) ? true : false;
#if CONFIG_SSD13x6_CLIPDEBUG > 0
if ( Result == false ) {
ClipDebug( x, y );
}
#endif
return Result;
}
__attribute__( ( always_inline ) ) static inline void SwapInt( int* a, int* b ) {
int Temp = *b;
*b = *a;
*a = Temp;
}
inline void IRAM_ATTR SSD13x6_DrawPixelFast( struct SSD13x6_Device* DeviceHandle, int X, int Y, int Color ) {
uint32_t YBit = ( Y & 0x07 );
uint8_t* FBOffset = NULL;
/*
* We only need to modify the Y coordinate since the pitch
* of the screen is the same as the width.
* Dividing Y by 8 gives us which row the pixel is in but not
* the bit position.
*/
Y>>= 3;
FBOffset = DeviceHandle->Framebuffer + ( ( Y * DeviceHandle->Width ) + X );
if ( Color == SSD_COLOR_XOR ) {
*FBOffset ^= BIT( YBit );
} else {
*FBOffset = ( Color == SSD_COLOR_WHITE ) ? *FBOffset | BIT( YBit ) : *FBOffset & ~BIT( YBit );
}
}
void IRAM_ATTR SSD13x6_DrawPixel( struct SSD13x6_Device* DeviceHandle, int x, int y, int Color ) {
NullCheck( DeviceHandle, return );
if ( IsPixelVisible( DeviceHandle, x, y ) == true ) {
SSD13x6_DrawPixelFast( DeviceHandle, x, y, Color );
}
}
void IRAM_ATTR SSD13x6_DrawHLine( struct SSD13x6_Device* DeviceHandle, int x, int y, int Width, int Color ) {
int XEnd = x + Width;
NullCheck( DeviceHandle, return );
NullCheck( DeviceHandle->Framebuffer, return );
for ( ; x < XEnd; x++ ) {
if ( IsPixelVisible( DeviceHandle, x, y ) == true ) {
SSD13x6_DrawPixelFast( DeviceHandle, x, y, Color );
} else {
break;
}
}
}
void IRAM_ATTR SSD13x6_DrawVLine( struct SSD13x6_Device* DeviceHandle, int x, int y, int Height, int Color ) {
int YEnd = y + Height;
NullCheck( DeviceHandle, return );
NullCheck( DeviceHandle->Framebuffer, return );
for ( ; y < YEnd; y++ ) {
if ( IsPixelVisible( DeviceHandle, x, y ) == true ) {
SSD13x6_DrawPixel( DeviceHandle, x, y, Color );
} else {
break;
}
}
}
static inline void IRAM_ATTR DrawWideLine( struct SSD13x6_Device* DeviceHandle, int x0, int y0, int x1, int y1, int Color ) {
int dx = ( x1 - x0 );
int dy = ( y1 - y0 );
int Error = 0;
int Incr = 1;
int x = x0;
int y = y0;
if ( dy < 0 ) {
Incr = -1;
dy = -dy;
}
Error = ( dy * 2 ) - dx;
for ( ; x < x1; x++ ) {
if ( IsPixelVisible( DeviceHandle, x, y ) == true ) {
SSD13x6_DrawPixelFast( DeviceHandle, x, y, Color );
}
if ( Error > 0 ) {
Error-= ( dx * 2 );
y+= Incr;
}
Error+= ( dy * 2 );
}
}
static inline void IRAM_ATTR DrawTallLine( struct SSD13x6_Device* DeviceHandle, int x0, int y0, int x1, int y1, int Color ) {
int dx = ( x1 - x0 );
int dy = ( y1 - y0 );
int Error = 0;
int Incr = 1;
int x = x0;
int y = y0;
if ( dx < 0 ) {
Incr = -1;
dx = -dx;
}
Error = ( dx * 2 ) - dy;
for ( ; y < y1; y++ ) {
if ( IsPixelVisible( DeviceHandle, x, y ) == true ) {
SSD13x6_DrawPixelFast( DeviceHandle, x, y, Color );
}
if ( Error > 0 ) {
Error-= ( dy * 2 );
x+= Incr;
}
Error+= ( dx * 2 );
}
}
void IRAM_ATTR SSD13x6_DrawLine( struct SSD13x6_Device* DeviceHandle, int x0, int y0, int x1, int y1, int Color ) {
NullCheck( DeviceHandle, return );
NullCheck( DeviceHandle->Framebuffer, return );
if ( x0 == x1 ) {
SSD13x6_DrawVLine( DeviceHandle, x0, y0, ( y1 - y0 ), Color );
} else if ( y0 == y1 ) {
SSD13x6_DrawHLine( DeviceHandle, x0, y0, ( x1 - x0 ), Color );
} else {
if ( abs( x1 - x0 ) > abs( y1 - y0 ) ) {
/* Wide ( run > rise ) */
if ( x0 > x1 ) {
SwapInt( &x0, &x1 );
SwapInt( &y0, &y1 );
}
DrawWideLine( DeviceHandle, x0, y0, x1, y1, Color );
} else {
/* Tall ( rise > run ) */
if ( y0 > y1 ) {
SwapInt( &y0, &y1 );
SwapInt( &x0, &x1 );
}
DrawTallLine( DeviceHandle, x0, y0, x1, y1, Color );
}
}
}
void IRAM_ATTR SSD13x6_DrawBox( struct SSD13x6_Device* DeviceHandle, int x1, int y1, int x2, int y2, int Color, bool Fill ) {
int Width = ( x2 - x1 );
int Height = ( y2 - y1 );
NullCheck( DeviceHandle, return );
NullCheck( DeviceHandle->Framebuffer, return );
if ( Fill == false ) {
/* Top side */
SSD13x6_DrawHLine( DeviceHandle, x1, y1, Width, Color );
/* Bottom side */
SSD13x6_DrawHLine( DeviceHandle, x1, y1 + Height, Width, Color );
/* Left side */
SSD13x6_DrawVLine( DeviceHandle, x1, y1, Height, Color );
/* Right side */
SSD13x6_DrawVLine( DeviceHandle, x1 + Width, y1, Height, Color );
} else {
/* Fill the box by drawing horizontal lines */
for ( ; y1 <= y2; y1++ ) {
SSD13x6_DrawHLine( DeviceHandle, x1, y1, Width, Color );
}
}
}
void SSD13x6_Clear( struct SSD13x6_Device* DeviceHandle, int Color ) {
NullCheck( DeviceHandle, return );
NullCheck( DeviceHandle->Framebuffer, return );
memset( DeviceHandle->Framebuffer, Color, DeviceHandle->FramebufferSize );
}
void SSD13x6_ClearWindow( struct SSD13x6_Device* DeviceHandle, int x1, int y1, int x2, int y2, int Color ) {
NullCheck( DeviceHandle, return );
NullCheck( DeviceHandle->Framebuffer, return );
/*
int xr = ((x1 - 1) / 8) + 1 ) * 8;
int xl = (x2 / 8) * 8;
for (int y = y1; y <= y2; y++) {
for (int x = x1; x < xr; x++) SSD13x6_DrawPixelFast( DeviceHandle, x, y, Color);
if (xl > xr) memset( DeviceHandle->Framebuffer + (y / 8) * DeviceHandle->Width + xr, 0, xl - xr );
for (int x = xl; x <= x2; x++) SSD13x6_DrawPixelFast( DeviceHandle, x, y, Color);
}
return;
*/
// cheap optimization on boundaries
if (x1 == 0 && x2 == DeviceHandle->Width - 1 && y1 % 8 == 0 && (y2 + 1) % 8 == 0) {
memset( DeviceHandle->Framebuffer + (y1 / 8) * DeviceHandle->Width, 0, (y2 - y1 + 1) / 8 * DeviceHandle->Width );
} else {
for (int y = y1; y <= y2; y++) {
for (int x = x1; x <= x2; x++) {
SSD13x6_DrawPixelFast( DeviceHandle, x, y, Color);
}
}
}
}

View File

@@ -1,54 +0,0 @@
#ifndef _SSD13x6_DRAW_H_
#define _SSD13x6_DRAW_H_
#ifdef __cplusplus
extern "C" {
#endif
#include "sdkconfig.h"
#define SSD13x6_CLIPDEBUG_NONE 0
#define SSD13x6_CLIPDEBUG_WARNING 1
#define SSD13x6_CLIPDEBUG_ERROR 2
#if CONFIG_SSD13x6_CLIPDEBUG == SSD13x6_CLIPDEBUG_NONE
/*
* Clip silently with no console output.
*/
#define ClipDebug( x, y )
#elif CONFIG_SSD13x6_CLIPDEBUG == SSD13x6_CLIPDEBUG_WARNING
/*
* Log clipping to the console as a warning.
*/
#define ClipDebug( x, y ) { \
ESP_LOGW( __FUNCTION__, "Line %d: Pixel at %d, %d CLIPPED", __LINE__, x, y ); \
}
#elif CONFIG_SSD13x6_CLIPDEBUG == SSD13x6_CLIPDEBUG_ERROR
/*
* Log clipping as an error to the console.
* Also invokes an abort with stack trace.
*/
#define ClipDebug( x, y ) { \
ESP_LOGE( __FUNCTION__, "Line %d: Pixel at %d, %d CLIPPED, ABORT", __LINE__, x, y ); \
abort( ); \
}
#endif
#define SSD_COLOR_BLACK 0
#define SSD_COLOR_WHITE 1
#define SSD_COLOR_XOR 2
void SSD13x6_Clear( struct SSD13x6_Device* DeviceHandle, int Color );
void SSD13x6_ClearWindow( struct SSD13x6_Device* DeviceHandle, int x1, int y1, int x2, int y2, int Color );
void SSD13x6_DrawPixel( struct SSD13x6_Device* DeviceHandle, int X, int Y, int Color );
void SSD13x6_DrawPixelFast( struct SSD13x6_Device* DeviceHandle, int X, int Y, int Color );
void SSD13x6_DrawHLine( struct SSD13x6_Device* DeviceHandle, int x, int y, int Width, int Color );
void SSD13x6_DrawVLine( struct SSD13x6_Device* DeviceHandle, int x, int y, int Height, int Color );
void SSD13x6_DrawLine( struct SSD13x6_Device* DeviceHandle, int x0, int y0, int x1, int y1, int Color );
void SSD13x6_DrawBox( struct SSD13x6_Device* DeviceHandle, int x1, int y1, int x2, int y2, int Color, bool Fill );
#ifdef __cplusplus
}
#endif
#endif

View File

@@ -1,91 +0,0 @@
#ifndef _SSD13x6_FONT_H_
#define _SSD13x6_FONT_H_
#include <stdint.h>
#include <stdbool.h>
#ifdef __cplusplus
extern "C" {
#endif
struct SSD13x6_Device;
/*
* X-GLCD Font format:
*
* First byte of glyph is it's width in pixels.
* Each data byte represents 8 pixels going down from top to bottom.
*
* Example glyph layout for a 16x16 font
* 'a': [Glyph width][Pixel column 0][Pixel column 1] where the number of pixel columns is the font height divided by 8
* 'b': [Glyph width][Pixel column 0][Pixel column 1]...
* 'c': And so on...
*/
struct SSD13x6_FontDef {
const uint8_t* FontData;
int Width;
int Height;
int StartChar;
int EndChar;
bool Monospace;
};
typedef enum {
TextAnchor_East = 0,
TextAnchor_West,
TextAnchor_North,
TextAnchor_South,
TextAnchor_NorthEast,
TextAnchor_NorthWest,
TextAnchor_SouthEast,
TextAnchor_SouthWest,
TextAnchor_Center
} TextAnchor;
bool SSD13x6_SetFont( struct SSD13x6_Device* Display, const struct SSD13x6_FontDef* Font );
void SSD13x6_FontForceProportional( struct SSD13x6_Device* Display, bool Force );
void SSD13x6_FontForceMonospace( struct SSD13x6_Device* Display, bool Force );
int SSD13x6_FontGetWidth( struct SSD13x6_Device* Display );
int SSD13x6_FontGetHeight( struct SSD13x6_Device* Display );
int SSD13x6_FontGetMaxCharsPerRow( struct SSD13x6_Device* Display );
int SSD13x6_FontGetMaxCharsPerColumn( struct SSD13x6_Device* Display );
int SSD13x6_FontGetCharWidth( struct SSD13x6_Device* Display, char Character );
int SSD13x6_FontGetCharHeight( struct SSD13x6_Device* Display );
int SSD13x6_FontMeasureString( struct SSD13x6_Device* Display, const char* Text );\
void SSD13x6_FontDrawChar( struct SSD13x6_Device* Display, char Character, int x, int y, int Color );
void SSD13x6_FontDrawString( struct SSD13x6_Device* Display, int x, int y, const char* Text, int Color );
void SSD13x6_FontDrawAnchoredString( struct SSD13x6_Device* Display, TextAnchor Anchor, const char* Text, int Color );
void SSD13x6_FontGetAnchoredStringCoords( struct SSD13x6_Device* Display, int* OutX, int* OutY, TextAnchor Anchor, const char* Text );
extern const struct SSD13x6_FontDef Font_droid_sans_fallback_11x13;
extern const struct SSD13x6_FontDef Font_droid_sans_fallback_15x17;
extern const struct SSD13x6_FontDef Font_droid_sans_fallback_24x28;
extern const struct SSD13x6_FontDef Font_droid_sans_mono_7x13;
extern const struct SSD13x6_FontDef Font_droid_sans_mono_13x24;
extern const struct SSD13x6_FontDef Font_droid_sans_mono_16x31;
extern const struct SSD13x6_FontDef Font_liberation_mono_9x15;
extern const struct SSD13x6_FontDef Font_liberation_mono_13x21;
extern const struct SSD13x6_FontDef Font_liberation_mono_17x30;
extern const struct SSD13x6_FontDef Font_Tarable7Seg_16x32;
extern const struct SSD13x6_FontDef Font_Tarable7Seg_32x64;
extern const struct SSD13x6_FontDef Font_line_1;
extern const struct SSD13x6_FontDef Font_line_2;
#ifdef __cplusplus
}
#endif
#endif

View File

@@ -415,11 +415,7 @@ static void *rtsp_thread(void *arg) {
closesocket(sock);
LOG_INFO("RTSP close %u", sock);
sock = -1;
}
}
if (sock != -1) closesocket(sock);
}
}
if (sock != -1) closesocket(sock);

View File

@@ -23,6 +23,9 @@
#include "squeezelite.h"
#include "slimproto.h"
#include "display.h"
#include "gds.h"
#include "gds_text.h"
#include "gds_draw.h"
#pragma pack(push, 1)
@@ -85,8 +88,6 @@ struct ANIC_header {
#pragma pack(pop)
extern struct outputstate output;
static struct {
TaskHandle_t task;
SemaphoreHandle_t mutex;
@@ -212,15 +213,15 @@ bool sb_display_init(void) {
static EXT_RAM_ATTR StackType_t xStack[SCROLL_STACK_SIZE] __attribute__ ((aligned (4)));
// no display, just make sure we won't have requests
if (!display || display->height == 0 || display->width == 0) {
if (!display || GDS_GetWidth(display) <= 0 || GDS_GetHeight(display) <= 0) {
LOG_INFO("no display for LMS");
return false;
}
// need to force height to 32 maximum
displayer.width = display->width;
displayer.height = min(display->height, SB_HEIGHT);
SETD_width = display->width;
displayer.width = GDS_GetWidth(display);
displayer.height = min(GDS_GetHeight(display), SB_HEIGHT);
SETD_width = displayer.width;
// create visu configuration
visu.bar_gap = 1;
@@ -330,8 +331,8 @@ static void send_server(void) {
static void server(in_addr_t ip, u16_t hport, u16_t cport) {
char msg[32];
sprintf(msg, "%s:%hu", inet_ntoa(ip), hport);
if (displayer.owned) display->text(DISPLAY_FONT_DEFAULT, DISPLAY_CENTERED, DISPLAY_CLEAR | DISPLAY_UPDATE, msg);
SETD_width = display->width;
if (displayer.owned) GDS_TextPos(display, GDS_FONT_DEFAULT, GDS_TEXT_CENTERED, GDS_TEXT_CLEAR | GDS_TEXT_UPDATE, msg);
SETD_width = displayer.width;
displayer.dirty = true;
if (notify_chain) (*notify_chain)(ip, hport, cport);
}
@@ -426,8 +427,8 @@ static void show_display_buffer(char *ddram) {
LOG_DEBUG("\n\t%.40s\n\t%.40s", line1, line2);
display->line(1, DISPLAY_LEFT, DISPLAY_CLEAR, line1);
display->line(2, DISPLAY_LEFT, DISPLAY_CLEAR | DISPLAY_UPDATE, line2);
GDS_TextLine(display, 1, GDS_TEXT_LEFT, GDS_TEXT_CLEAR, line1);
GDS_TextLine(display, 2, GDS_TEXT_LEFT, GDS_TEXT_CLEAR | GDS_TEXT_UPDATE, line2);
}
/****************************************************************************************
@@ -495,13 +496,13 @@ static void grfe_handler( u8_t *data, int len) {
if (displayer.owned) {
// did we have something that might have write on the bottom of a SB_HEIGHT+ display
if (displayer.dirty) {
display->clear(true);
GDS_ClearExt(display, true);
displayer.dirty = false;
}
// draw new frame
display->draw_cbr(data + sizeof(struct grfe_packet), displayer.width, displayer.height);
display->update();
GDS_DrawBitmapCBR(display, data + sizeof(struct grfe_packet), displayer.width, displayer.height);
GDS_Update(display);
}
xSemaphoreGive(displayer.mutex);
@@ -519,10 +520,10 @@ static void grfb_handler(u8_t *data, int len) {
LOG_INFO("brightness %hu", pkt->brightness);
if (pkt->brightness < 0) {
display->on(false);
GDS_DisplayOff(display);
} else {
display->on(true);
display->brightness(pkt->brightness);
GDS_DisplayOn(display);
GDS_SetContrast(display, pkt->brightness);
}
}
@@ -603,8 +604,8 @@ static void grfg_handler(u8_t *data, int len) {
// can only write if we really own display
if (displayer.owned) {
display->draw_cbr(scroller.frame, scroller.back.width, displayer.height);
display->update();
GDS_DrawBitmapCBR(display, scroller.frame, scroller.back.width, displayer.height);
GDS_Update(display);
}
// now we can active scrolling, but only if we are not on a small screen
@@ -703,7 +704,7 @@ static void visu_update(void) {
visu_export.level = 0;
pthread_mutex_unlock(&visu_export.mutex);
display->clear(false, false, visu.col, visu.row, visu.col + visu.width - 1, visu.row + visu.height - 1);
GDS_ClearExt(display, false, false, visu.col, visu.row, visu.col + visu.width - 1, visu.row + visu.height - 1);
for (int i = visu.n; --i >= 0;) {
int x1 = visu.col + visu.border + visu.bar_border + i*(visu.bar_width + visu.bar_gap);
@@ -713,11 +714,11 @@ static void visu_update(void) {
else if (visu.bars[i].max) visu.bars[i].max--;
for (int j = 0; j <= visu.bars[i].current; j += 2)
display->draw_line( x1, y1 - j, x1 + visu.bar_width - 1, y1 - j);
GDS_DrawLine(display, x1, y1 - j, x1 + visu.bar_width - 1, y1 - j, GDS_COLOR_WHITE);
if (visu.bars[i].max > 2) {
display->draw_line( x1, y1 - visu.bars[i].max, x1 + visu.bar_width - 1, y1 - visu.bars[i].max);
display->draw_line( x1, y1 - visu.bars[i].max + 1, x1 + visu.bar_width - 1, y1 - visu.bars[i].max + 1);
GDS_DrawLine(display, x1, y1 - visu.bars[i].max, x1 + visu.bar_width - 1, y1 - visu.bars[i].max, GDS_COLOR_WHITE);
GDS_DrawLine(display, x1, y1 - visu.bars[i].max + 1, x1 + visu.bar_width - 1, y1 - visu.bars[i].max + 1, GDS_COLOR_WHITE);
}
}
}
@@ -756,7 +757,7 @@ static void visu_handler( u8_t *data, int len) {
visu.mode = pkt->which;
// little trick to clean the taller screens when switching visu
if (visu.row >= SB_HEIGHT) display->clear(false, true, visu.col, visu.row, visu.col + visu.width - 1, visu.row - visu.height - 1);
if (visu.row >= SB_HEIGHT) GDS_ClearExt(display, false, true, visu.col, visu.row, visu.col + visu.width - 1, visu.row - visu.height - 1);
if (visu.mode) {
if (pkt->count >= 4) {
@@ -767,17 +768,17 @@ static void visu_handler( u8_t *data, int len) {
visu.width = htonl(pkt->width);
visu.height = pkt->height ? pkt->height : SB_HEIGHT;
visu.col = pkt->col < 0 ? display->width + pkt->col : pkt->col;
visu.row = pkt->row < 0 ? display->height + pkt->row : pkt->row;
visu.col = pkt->col < 0 ? displayer.width + pkt->col : pkt->col;
visu.row = pkt->row < 0 ? GDS_GetHeight(display) + pkt->row : pkt->row;
visu.border = htonl(pkt->border);
bars = htonl(pkt->bars);
visu.spectrum_scale = htonl(pkt->spectrum_scale) / 100.;
} else {
// full screen visu, try to use bottom screen if available
visu.width = display->width;
visu.height = display->height > SB_HEIGHT ? display->height - SB_HEIGHT : display->height;
visu.width = displayer.width;
visu.height = GDS_GetHeight(display) > SB_HEIGHT ? GDS_GetHeight(display) - SB_HEIGHT : GDS_GetHeight(display);
visu.col = visu.border = 0;
visu.row = display->height - visu.height;
visu.row = GDS_GetHeight(display) - visu.height;
bars = htonl(pkt->full.bars);
visu.spectrum_scale = htonl(pkt->full.spectrum_scale) / 100.;
}
@@ -811,7 +812,7 @@ static void visu_handler( u8_t *data, int len) {
// reset bars maximum
for (int i = visu.n; --i >= 0;) visu.bars[i].max = 0;
display->clear(false, true, visu.col, visu.row, visu.col + visu.width - 1, visu.row - visu.height - 1);
GDS_ClearExt(display, false, true, visu.col, visu.row, visu.col + visu.width - 1, visu.row - visu.height - 1);
LOG_INFO("Visualizer with %u bars of width %d:%d:%d:%d (%w:%u,h:%u,c:%u,r:%u,s:%.02f)", visu.n, visu.bar_border, visu.bar_width, visu.bar_gap, visu.border, visu.width, visu.height, visu.col, visu.row, visu.spectrum_scale);
} else {
@@ -854,7 +855,7 @@ static void displayer_task(void *args) {
memcpy(scroller.frame, scroller.back.frame, scroller.back.width * displayer.height / 8);
for (int i = 0; i < scroller.width * displayer.height / 8; i++) scroller.frame[i] |= scroller.scroll.frame[scroller.scrolled * displayer.height / 8 + i];
scroller.scrolled += scroller.by;
if (displayer.owned) display->draw_cbr(scroller.frame, scroller.width, displayer.height);
if (displayer.owned) GDS_DrawBitmapCBR(display, scroller.frame, scroller.width, displayer.height);
// short sleep & don't need background update
scroller.wake = scroller.speed;
@@ -884,7 +885,7 @@ static void displayer_task(void *args) {
}
// need to make sure we own display
if (displayer.owned) display->update();
if (displayer.owned) GDS_Update(display);
// release semaphore and sleep what's needed
xSemaphoreGive(displayer.mutex);