Add display library, show battery voltage

untested
This commit is contained in:
jonny_jr9 2023-09-09 16:51:01 +02:00
parent 3449bb7f34
commit 0804aaf240
11 changed files with 1873 additions and 0 deletions

View File

@ -6,6 +6,7 @@ idf_component_register(
"button.cpp"
"fan.cpp"
"auto.cpp"
"display.cpp"
INCLUDE_DIRS
"."
)

View File

@ -0,0 +1,207 @@
#include "display.hpp"
extern "C"{
#include <driver/adc.h>
}
//#
//# SSD1306 Configuration
//#
#define GPIO_RANGE_MAX 33
#define I2C_INTERFACE y
//# SPI_INTERFACE is not set
//# SSD1306_128x32 is not set
#define SSD1306_128x64 y
#define OFFSETX 0
//# FLIP is not set
#define SCL_GPIO 22
#define SDA_GPIO 23
#define RESET_GPIO 15 //FIXME remove this
#define I2C_PORT_0 y
//# I2C_PORT_1 is not set
//# end of SSD1306 Configuration
//--------------------------
//------- getVoltage -------
//--------------------------
//local function to get average voltage from adc
float getVoltage1(adc1_channel_t adc, uint32_t samples){
//measure voltage
int measure = 0;
for (int j=0; j<samples; j++){
measure += adc1_get_raw(adc);
ets_delay_us(50);
}
return (float)measure / samples / 4096 * 3.3;
}
void startDisplayTest(){
adc1_config_channel_atten(ADC1_CHANNEL_6, ADC_ATTEN_DB_11); //max voltage
SSD1306_t dev;
int center, top, bottom;
char lineChar[20];
#if I2C_INTERFACE
ESP_LOGI(tag, "INTERFACE is i2c");
ESP_LOGI(tag, "SDA_GPIO=%d",SDA_GPIO);
ESP_LOGI(tag, "SCL_GPIO=%d",SCL_GPIO);
ESP_LOGI(tag, "RESET_GPIO=%d",RESET_GPIO);
i2c_master_init(&dev, SDA_GPIO, SCL_GPIO, RESET_GPIO);
#endif // I2C_INTERFACE
#if FLIP
dev._flip = true;
ESP_LOGW(tag, "Flip upside down");
#endif
#if SSD1306_128x64
ESP_LOGI(tag, "Panel is 128x64");
ssd1306_init(&dev, 128, 64);
#endif // SSD1306_128x64
ssd1306_clear_screen(&dev, false);
ssd1306_contrast(&dev, 0xff);
ssd1306_display_text_x3(&dev, 0, "Hello", 5, false);
vTaskDelay(3000 / portTICK_PERIOD_MS);
top = 2;
center = 3;
bottom = 8;
//ssd1306_display_text(&dev, 0, "SSD1306 128x64", 14, false);
//ssd1306_display_text(&dev, 1, "ABCDEFGHIJKLMNOP", 16, false);
//ssd1306_display_text(&dev, 2, "abcdefghijklmnop",16, false);
//ssd1306_display_text(&dev, 3, "Hello World!!", 13, false);
////ssd1306_clear_line(&dev, 4, true);
////ssd1306_clear_line(&dev, 5, true);
////ssd1306_clear_line(&dev, 6, true);
////ssd1306_clear_line(&dev, 7, true);
//ssd1306_display_text(&dev, 4, "SSD1306 128x64", 14, true);
//ssd1306_display_text(&dev, 5, "ABCDEFGHIJKLMNOP", 16, true);
//ssd1306_display_text(&dev, 6, "abcdefghijklmnop",16, true);
//ssd1306_display_text(&dev, 7, "Hello World!!", 13, true);
vTaskDelay(3000 / portTICK_PERIOD_MS);
while(1){
float voltage = getVoltage1( ADC1_CHANNEL_6, 1000);
float battVoltage = voltage * 9;
char buf[20];
int len = snprintf(buf, sizeof(buf), "Batt: %.3fV", battVoltage);
ssd1306_display_text(&dev, 2, buf, len, false);
ssd1306_display_text(&dev, 2, buf, len, true);
vTaskDelay(2000 / portTICK_PERIOD_MS);
}
//// Display Count Down
//uint8_t image[24];
//memset(image, 0, sizeof(image));
//ssd1306_display_image(&dev, top, (6*8-1), image, sizeof(image));
//ssd1306_display_image(&dev, top+1, (6*8-1), image, sizeof(image));
//ssd1306_display_image(&dev, top+2, (6*8-1), image, sizeof(image));
//for(int font=0x39;font>0x30;font--) {
// memset(image, 0, sizeof(image));
// ssd1306_display_image(&dev, top+1, (7*8-1), image, 8);
// memcpy(image, font8x8_basic_tr[font], 8);
// if (dev._flip) ssd1306_flip(image, 8);
// ssd1306_display_image(&dev, top+1, (7*8-1), image, 8);
// vTaskDelay(1000 / portTICK_PERIOD_MS);
//}
//
//// Scroll Up
//ssd1306_clear_screen(&dev, false);
//ssd1306_contrast(&dev, 0xff);
//ssd1306_display_text(&dev, 0, "---Scroll UP---", 16, true);
////ssd1306_software_scroll(&dev, 7, 1);
//ssd1306_software_scroll(&dev, (dev._pages - 1), 1);
//for (int line=0;line<bottom+10;line++) {
// lineChar[0] = 0x01;
// sprintf(&lineChar[1], " Line %02d", line);
// ssd1306_scroll_text(&dev, lineChar, strlen(lineChar), false);
// vTaskDelay(500 / portTICK_PERIOD_MS);
//}
//vTaskDelay(3000 / portTICK_PERIOD_MS);
//
//// Scroll Down
//ssd1306_clear_screen(&dev, false);
//ssd1306_contrast(&dev, 0xff);
//ssd1306_display_text(&dev, 0, "--Scroll DOWN--", 16, true);
////ssd1306_software_scroll(&dev, 1, 7);
//ssd1306_software_scroll(&dev, 1, (dev._pages - 1) );
//for (int line=0;line<bottom+10;line++) {
// lineChar[0] = 0x02;
// sprintf(&lineChar[1], " Line %02d", line);
// ssd1306_scroll_text(&dev, lineChar, strlen(lineChar), false);
// vTaskDelay(500 / portTICK_PERIOD_MS);
//}
//vTaskDelay(3000 / portTICK_PERIOD_MS);
//// Page Down
//ssd1306_clear_screen(&dev, false);
//ssd1306_contrast(&dev, 0xff);
//ssd1306_display_text(&dev, 0, "---Page DOWN---", 16, true);
//ssd1306_software_scroll(&dev, 1, (dev._pages-1) );
//for (int line=0;line<bottom+10;line++) {
// //if ( (line % 7) == 0) ssd1306_scroll_clear(&dev);
// if ( (line % (dev._pages-1)) == 0) ssd1306_scroll_clear(&dev);
// lineChar[0] = 0x02;
// sprintf(&lineChar[1], " Line %02d", line);
// ssd1306_scroll_text(&dev, lineChar, strlen(lineChar), false);
// vTaskDelay(500 / portTICK_PERIOD_MS);
//}
//vTaskDelay(3000 / portTICK_PERIOD_MS);
//// Horizontal Scroll
//ssd1306_clear_screen(&dev, false);
//ssd1306_contrast(&dev, 0xff);
//ssd1306_display_text(&dev, center, "Horizontal", 10, false);
//ssd1306_hardware_scroll(&dev, SCROLL_RIGHT);
//vTaskDelay(5000 / portTICK_PERIOD_MS);
//ssd1306_hardware_scroll(&dev, SCROLL_LEFT);
//vTaskDelay(5000 / portTICK_PERIOD_MS);
//ssd1306_hardware_scroll(&dev, SCROLL_STOP);
//
//// Vertical Scroll
//ssd1306_clear_screen(&dev, false);
//ssd1306_contrast(&dev, 0xff);
//ssd1306_display_text(&dev, center, "Vertical", 8, false);
//ssd1306_hardware_scroll(&dev, SCROLL_DOWN);
//vTaskDelay(5000 / portTICK_PERIOD_MS);
//ssd1306_hardware_scroll(&dev, SCROLL_UP);
//vTaskDelay(5000 / portTICK_PERIOD_MS);
//ssd1306_hardware_scroll(&dev, SCROLL_STOP);
//
//// Invert
//ssd1306_clear_screen(&dev, true);
//ssd1306_contrast(&dev, 0xff);
//ssd1306_display_text(&dev, center, " Good Bye!!", 12, true);
//vTaskDelay(5000 / portTICK_PERIOD_MS);
//// Fade Out
//ssd1306_fadeout(&dev);
#if 0
// Fade Out
for(int contrast=0xff;contrast>0;contrast=contrast-0x20) {
ssd1306_contrast(&dev, contrast);
vTaskDelay(40);
}
#endif
}

View File

@ -0,0 +1,14 @@
extern "C" {
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_log.h"
#include "ssd1306.h"
#include "font8x8_basic.h"
}
void startDisplayTest();

View File

@ -28,6 +28,8 @@ extern "C"
#include "uart_common.hpp"
#include "display.hpp"
//tag for logging
static const char * TAG = "main";
@ -232,6 +234,10 @@ extern "C" void app_main(void) {
//control.changeMode(controlMode_t::HTTP);
//========== display test ============
startDisplayTest();
//--- main loop ---
//does nothing except for testing things
while(1){

View File

@ -0,0 +1,5 @@
set(component_srcs "ssd1306.c" "ssd1306_i2c.c" "ssd1306_spi.c")
idf_component_register(SRCS "${component_srcs}"
PRIV_REQUIRES driver
INCLUDE_DIRS ".")

View File

@ -0,0 +1,183 @@
menu "SSD1306 Configuration"
config GPIO_RANGE_MAX
int
default 33 if IDF_TARGET_ESP32
default 46 if IDF_TARGET_ESP32S2
default 48 if IDF_TARGET_ESP32S3
default 18 if IDF_TARGET_ESP32C2
default 19 if IDF_TARGET_ESP32C3
default 30 if IDF_TARGET_ESP32C6
choice INTERFACE
prompt "Interface"
default I2C_INTERFACE
help
Select Interface.
config I2C_INTERFACE
bool "I2C Interface"
help
I2C Interface.
config SPI_INTERFACE
bool "SPI Interface"
help
SPI Interface.
endchoice
choice PANEL
prompt "Panel Type"
default SSD1306_128x64
help
Select Panel Type.
config SSD1306_128x32
bool "128x32 Panel"
help
Panel is 128x32.
config SSD1306_128x64
bool "128x64 Panel"
help
Panel is 128x64.
endchoice
config OFFSETX
int "GRAM X OFFSET"
range 0 99
default 0
help
When your TFT have offset(X), set it.
config FLIP
bool "Flip upside down"
default false
help
Flip upside down.
config SCL_GPIO
depends on I2C_INTERFACE
int "SCL GPIO number"
range 0 GPIO_RANGE_MAX
default 22 if IDF_TARGET_ESP32
default 12 if IDF_TARGET_ESP32S2
default 12 if IDF_TARGET_ESP32S3
default 6 # C3 and others
help
GPIO number (IOxx) to I2C SCL.
Some GPIOs are used for other purposes (flash connections, etc.) and cannot be used to I2C.
GPIOs 35-39 are input-only so cannot be used as outputs.
config SDA_GPIO
depends on I2C_INTERFACE
int "SDA GPIO number"
range 0 GPIO_RANGE_MAX
default 21 if IDF_TARGET_ESP32
default 11 if IDF_TARGET_ESP32S2
default 11 if IDF_TARGET_ESP32S3
default 5 # C3 and others
help
GPIO number (IOxx) to I2C SDA.
Some GPIOs are used for other purposes (flash connections, etc.) and cannot be used to I2C.
GPIOs 35-39 are input-only so cannot be used as outputs.
config MOSI_GPIO
depends on SPI_INTERFACE
int "MOSI GPIO number"
range 0 GPIO_RANGE_MAX
default 23 if IDF_TARGET_ESP32
default 35 if IDF_TARGET_ESP32S2
default 35 if IDF_TARGET_ESP32S3
default 1 # C3 and others
help
GPIO number (IOxx) to SPI MOSI.
Some GPIOs are used for other purposes (flash connections, etc.) and cannot be used to MOSI.
On the ESP32, GPIOs 35-39 are input-only so cannot be used as outputs.
On the ESP32-S2, GPIO 46 is input-only so cannot be used as outputs.
config SCLK_GPIO
depends on SPI_INTERFACE
int "SCLK GPIO number"
range 0 GPIO_RANGE_MAX
default 18 if IDF_TARGET_ESP32
default 36 if IDF_TARGET_ESP32S2
default 36 if IDF_TARGET_ESP32S3
default 2 # C3 and others
help
GPIO number (IOxx) to SPI SCLK.
Some GPIOs are used for other purposes (flash connections, etc.) and cannot be used to SCLK.
On the ESP32, GPIOs 35-39 are input-only so cannot be used as outputs.
On the ESP32-S2, GPIO 46 is input-only so cannot be used as outputs.
config CS_GPIO
depends on SPI_INTERFACE
int "CS GPIO number"
range 0 GPIO_RANGE_MAX
default 5 if IDF_TARGET_ESP32
default 34 if IDF_TARGET_ESP32S2
default 34 if IDF_TARGET_ESP32S3
default 10 # C3 and others
help
GPIO number (IOxx) to SPI CS.
Some GPIOs are used for other purposes (flash connections, etc.) and cannot be used to CS.
GPIOs 35-39 are input-only so cannot be used as outputs.
config DC_GPIO
depends on SPI_INTERFACE
int "DC GPIO number"
range 0 GPIO_RANGE_MAX
default 4 if IDF_TARGET_ESP32
default 37 if IDF_TARGET_ESP32S2
default 37 if IDF_TARGET_ESP32S3
default 3 # C3 and others
help
GPIO number (IOxx) to SPI DC.
Some GPIOs are used for other purposes (flash connections, etc.) and cannot be used to DC.
GPIOs 35-39 are input-only so cannot be used as outputs.
config RESET_GPIO
int "RESET GPIO number"
range -1 GPIO_RANGE_MAX
default 15 if IDF_TARGET_ESP32
default 38 if IDF_TARGET_ESP32S2
default 38 if IDF_TARGET_ESP32S3
default 4 # C3 and others
help
GPIO number (IOxx) to RESET.
When it is -1, RESET isn't performed.
Some GPIOs are used for other purposes (flash connections, etc.) and cannot be used to RESET.
GPIOs 35-39 are input-only so cannot be used as outputs.
choice I2C_PORT
depends on I2C_INTERFACE
prompt "I2C port that controls this bus"
default I2C_PORT_0
help
Select I2C port that controls this bus.
config I2C_PORT_0
bool "I2C_PORT_0"
help
Use I2C_PORT_0.
config I2C_PORT_1
depends on IDF_TARGET_ESP32 || IDF_TARGET_ESP32S2 || IDF_TARGET_ESP32S3
bool "I2C_PORT_1"
help
Use I2C_PORT_1.
endchoice
choice SPI_HOST
depends on SPI_INTERFACE
prompt "SPI peripheral that controls this bus"
default SPI2_HOST
help
Select SPI peripheral that controls this bus.
config SPI2_HOST
bool "SPI2_HOST"
help
Use SPI2_HOST. This is also called HSPI_HOST.
config SPI3_HOST
depends on IDF_TARGET_ESP32 || IDF_TARGET_ESP32S2 || IDF_TARGET_ESP32S3
bool "SPI3_HOST"
help
USE SPI3_HOST. This is also called VSPI_HOST
endchoice
endmenu

View File

@ -0,0 +1,174 @@
/*
* font8x8_basic.h
*
* Created on: 2017/05/03
* Author: yanbe
*/
#ifndef MAIN_FONT8X8_BASIC_H_
#define MAIN_FONT8X8_BASIC_H_
/*
Constant: font8x8_basic_tr
Contains an 90 digree transposed 8x8 font map for unicode points
U+0000 - U+007F (basic latin)
To make it easy to use with SSD1306's GDDRAM mapping and API,
this constant is an 90 degree transposed.
The original version written by Marcel Sondaar is availble at:
https://github.com/dhepper/font8x8/blob/master/font8x8_basic.h
Conversion is done via following procedure:
for (int code = 0; code < 128; code++) {
uint8_t trans[8];
for (int w = 0; w < 8; w++) {
trans[w] = 0x00;
for (int b = 0; b < 8; b++) {
trans[w] |= ((font8x8_basic[code][b] & (1 << w)) >> w) << b;
}
}
for (int w = 0; w < 8; w++) {
if (w == 0) { printf(" { "); }
printf("0x%.2X", trans[w]);
if (w < 7) { printf(", "); }
if (w == 7) { printf(" }, // U+00%.2X (%c)\n", code, code); }
}
}
*/
static uint8_t font8x8_basic_tr[128][8] = {
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // U+0000 (nul)
{ 0x00, 0x04, 0x02, 0xFF, 0x02, 0x04, 0x00, 0x00 }, // U+0001 (Up Allow)
{ 0x00, 0x20, 0x40, 0xFF, 0x40, 0x20, 0x00, 0x00 }, // U+0002 (Down Allow)
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // U+0003
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // U+0004
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // U+0005
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // U+0006
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // U+0007
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // U+0008
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // U+0009
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // U+000A
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // U+000B
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // U+000C
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // U+000D
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // U+000E
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // U+000F
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // U+0010
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // U+0011
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // U+0012
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // U+0013
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // U+0014
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // U+0015
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // U+0016
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // U+0017
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // U+0018
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // U+0019
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // U+001A
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // U+001B
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // U+001C
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // U+001D
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // U+001E
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // U+001F
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // U+0020 (space)
{ 0x00, 0x00, 0x06, 0x5F, 0x5F, 0x06, 0x00, 0x00 }, // U+0021 (!)
{ 0x00, 0x03, 0x03, 0x00, 0x03, 0x03, 0x00, 0x00 }, // U+0022 (")
{ 0x14, 0x7F, 0x7F, 0x14, 0x7F, 0x7F, 0x14, 0x00 }, // U+0023 (#)
{ 0x24, 0x2E, 0x6B, 0x6B, 0x3A, 0x12, 0x00, 0x00 }, // U+0024 ($)
{ 0x46, 0x66, 0x30, 0x18, 0x0C, 0x66, 0x62, 0x00 }, // U+0025 (%)
{ 0x30, 0x7A, 0x4F, 0x5D, 0x37, 0x7A, 0x48, 0x00 }, // U+0026 (&)
{ 0x04, 0x07, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00 }, // U+0027 (')
{ 0x00, 0x1C, 0x3E, 0x63, 0x41, 0x00, 0x00, 0x00 }, // U+0028 (()
{ 0x00, 0x41, 0x63, 0x3E, 0x1C, 0x00, 0x00, 0x00 }, // U+0029 ())
{ 0x08, 0x2A, 0x3E, 0x1C, 0x1C, 0x3E, 0x2A, 0x08 }, // U+002A (*)
{ 0x08, 0x08, 0x3E, 0x3E, 0x08, 0x08, 0x00, 0x00 }, // U+002B (+)
{ 0x00, 0x80, 0xE0, 0x60, 0x00, 0x00, 0x00, 0x00 }, // U+002C (,)
{ 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x00, 0x00 }, // U+002D (-)
{ 0x00, 0x00, 0x60, 0x60, 0x00, 0x00, 0x00, 0x00 }, // U+002E (.)
{ 0x60, 0x30, 0x18, 0x0C, 0x06, 0x03, 0x01, 0x00 }, // U+002F (/)
{ 0x3E, 0x7F, 0x71, 0x59, 0x4D, 0x7F, 0x3E, 0x00 }, // U+0030 (0)
{ 0x40, 0x42, 0x7F, 0x7F, 0x40, 0x40, 0x00, 0x00 }, // U+0031 (1)
{ 0x62, 0x73, 0x59, 0x49, 0x6F, 0x66, 0x00, 0x00 }, // U+0032 (2)
{ 0x22, 0x63, 0x49, 0x49, 0x7F, 0x36, 0x00, 0x00 }, // U+0033 (3)
{ 0x18, 0x1C, 0x16, 0x53, 0x7F, 0x7F, 0x50, 0x00 }, // U+0034 (4)
{ 0x27, 0x67, 0x45, 0x45, 0x7D, 0x39, 0x00, 0x00 }, // U+0035 (5)
{ 0x3C, 0x7E, 0x4B, 0x49, 0x79, 0x30, 0x00, 0x00 }, // U+0036 (6)
{ 0x03, 0x03, 0x71, 0x79, 0x0F, 0x07, 0x00, 0x00 }, // U+0037 (7)
{ 0x36, 0x7F, 0x49, 0x49, 0x7F, 0x36, 0x00, 0x00 }, // U+0038 (8)
{ 0x06, 0x4F, 0x49, 0x69, 0x3F, 0x1E, 0x00, 0x00 }, // U+0039 (9)
{ 0x00, 0x00, 0x66, 0x66, 0x00, 0x00, 0x00, 0x00 }, // U+003A (:)
{ 0x00, 0x80, 0xE6, 0x66, 0x00, 0x00, 0x00, 0x00 }, // U+003B (;)
{ 0x08, 0x1C, 0x36, 0x63, 0x41, 0x00, 0x00, 0x00 }, // U+003C (<)
{ 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x00, 0x00 }, // U+003D (=)
{ 0x00, 0x41, 0x63, 0x36, 0x1C, 0x08, 0x00, 0x00 }, // U+003E (>)
{ 0x02, 0x03, 0x51, 0x59, 0x0F, 0x06, 0x00, 0x00 }, // U+003F (?)
{ 0x3E, 0x7F, 0x41, 0x5D, 0x5D, 0x1F, 0x1E, 0x00 }, // U+0040 (@)
{ 0x7C, 0x7E, 0x13, 0x13, 0x7E, 0x7C, 0x00, 0x00 }, // U+0041 (A)
{ 0x41, 0x7F, 0x7F, 0x49, 0x49, 0x7F, 0x36, 0x00 }, // U+0042 (B)
{ 0x1C, 0x3E, 0x63, 0x41, 0x41, 0x63, 0x22, 0x00 }, // U+0043 (C)
{ 0x41, 0x7F, 0x7F, 0x41, 0x63, 0x3E, 0x1C, 0x00 }, // U+0044 (D)
{ 0x41, 0x7F, 0x7F, 0x49, 0x5D, 0x41, 0x63, 0x00 }, // U+0045 (E)
{ 0x41, 0x7F, 0x7F, 0x49, 0x1D, 0x01, 0x03, 0x00 }, // U+0046 (F)
{ 0x1C, 0x3E, 0x63, 0x41, 0x51, 0x73, 0x72, 0x00 }, // U+0047 (G)
{ 0x7F, 0x7F, 0x08, 0x08, 0x7F, 0x7F, 0x00, 0x00 }, // U+0048 (H)
{ 0x00, 0x41, 0x7F, 0x7F, 0x41, 0x00, 0x00, 0x00 }, // U+0049 (I)
{ 0x30, 0x70, 0x40, 0x41, 0x7F, 0x3F, 0x01, 0x00 }, // U+004A (J)
{ 0x41, 0x7F, 0x7F, 0x08, 0x1C, 0x77, 0x63, 0x00 }, // U+004B (K)
{ 0x41, 0x7F, 0x7F, 0x41, 0x40, 0x60, 0x70, 0x00 }, // U+004C (L)
{ 0x7F, 0x7F, 0x0E, 0x1C, 0x0E, 0x7F, 0x7F, 0x00 }, // U+004D (M)
{ 0x7F, 0x7F, 0x06, 0x0C, 0x18, 0x7F, 0x7F, 0x00 }, // U+004E (N)
{ 0x1C, 0x3E, 0x63, 0x41, 0x63, 0x3E, 0x1C, 0x00 }, // U+004F (O)
{ 0x41, 0x7F, 0x7F, 0x49, 0x09, 0x0F, 0x06, 0x00 }, // U+0050 (P)
{ 0x1E, 0x3F, 0x21, 0x71, 0x7F, 0x5E, 0x00, 0x00 }, // U+0051 (Q)
{ 0x41, 0x7F, 0x7F, 0x09, 0x19, 0x7F, 0x66, 0x00 }, // U+0052 (R)
{ 0x26, 0x6F, 0x4D, 0x59, 0x73, 0x32, 0x00, 0x00 }, // U+0053 (S)
{ 0x03, 0x41, 0x7F, 0x7F, 0x41, 0x03, 0x00, 0x00 }, // U+0054 (T)
{ 0x7F, 0x7F, 0x40, 0x40, 0x7F, 0x7F, 0x00, 0x00 }, // U+0055 (U)
{ 0x1F, 0x3F, 0x60, 0x60, 0x3F, 0x1F, 0x00, 0x00 }, // U+0056 (V)
{ 0x7F, 0x7F, 0x30, 0x18, 0x30, 0x7F, 0x7F, 0x00 }, // U+0057 (W)
{ 0x43, 0x67, 0x3C, 0x18, 0x3C, 0x67, 0x43, 0x00 }, // U+0058 (X)
{ 0x07, 0x4F, 0x78, 0x78, 0x4F, 0x07, 0x00, 0x00 }, // U+0059 (Y)
{ 0x47, 0x63, 0x71, 0x59, 0x4D, 0x67, 0x73, 0x00 }, // U+005A (Z)
{ 0x00, 0x7F, 0x7F, 0x41, 0x41, 0x00, 0x00, 0x00 }, // U+005B ([)
{ 0x01, 0x03, 0x06, 0x0C, 0x18, 0x30, 0x60, 0x00 }, // U+005C (\)
{ 0x00, 0x41, 0x41, 0x7F, 0x7F, 0x00, 0x00, 0x00 }, // U+005D (])
{ 0x08, 0x0C, 0x06, 0x03, 0x06, 0x0C, 0x08, 0x00 }, // U+005E (^)
{ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80 }, // U+005F (_)
{ 0x00, 0x00, 0x03, 0x07, 0x04, 0x00, 0x00, 0x00 }, // U+0060 (`)
{ 0x20, 0x74, 0x54, 0x54, 0x3C, 0x78, 0x40, 0x00 }, // U+0061 (a)
{ 0x41, 0x7F, 0x3F, 0x48, 0x48, 0x78, 0x30, 0x00 }, // U+0062 (b)
{ 0x38, 0x7C, 0x44, 0x44, 0x6C, 0x28, 0x00, 0x00 }, // U+0063 (c)
{ 0x30, 0x78, 0x48, 0x49, 0x3F, 0x7F, 0x40, 0x00 }, // U+0064 (d)
{ 0x38, 0x7C, 0x54, 0x54, 0x5C, 0x18, 0x00, 0x00 }, // U+0065 (e)
{ 0x48, 0x7E, 0x7F, 0x49, 0x03, 0x02, 0x00, 0x00 }, // U+0066 (f)
{ 0x98, 0xBC, 0xA4, 0xA4, 0xF8, 0x7C, 0x04, 0x00 }, // U+0067 (g)
{ 0x41, 0x7F, 0x7F, 0x08, 0x04, 0x7C, 0x78, 0x00 }, // U+0068 (h)
{ 0x00, 0x44, 0x7D, 0x7D, 0x40, 0x00, 0x00, 0x00 }, // U+0069 (i)
{ 0x60, 0xE0, 0x80, 0x80, 0xFD, 0x7D, 0x00, 0x00 }, // U+006A (j)
{ 0x41, 0x7F, 0x7F, 0x10, 0x38, 0x6C, 0x44, 0x00 }, // U+006B (k)
{ 0x00, 0x41, 0x7F, 0x7F, 0x40, 0x00, 0x00, 0x00 }, // U+006C (l)
{ 0x7C, 0x7C, 0x18, 0x38, 0x1C, 0x7C, 0x78, 0x00 }, // U+006D (m)
{ 0x7C, 0x7C, 0x04, 0x04, 0x7C, 0x78, 0x00, 0x00 }, // U+006E (n)
{ 0x38, 0x7C, 0x44, 0x44, 0x7C, 0x38, 0x00, 0x00 }, // U+006F (o)
{ 0x84, 0xFC, 0xF8, 0xA4, 0x24, 0x3C, 0x18, 0x00 }, // U+0070 (p)
{ 0x18, 0x3C, 0x24, 0xA4, 0xF8, 0xFC, 0x84, 0x00 }, // U+0071 (q)
{ 0x44, 0x7C, 0x78, 0x4C, 0x04, 0x1C, 0x18, 0x00 }, // U+0072 (r)
{ 0x48, 0x5C, 0x54, 0x54, 0x74, 0x24, 0x00, 0x00 }, // U+0073 (s)
{ 0x00, 0x04, 0x3E, 0x7F, 0x44, 0x24, 0x00, 0x00 }, // U+0074 (t)
{ 0x3C, 0x7C, 0x40, 0x40, 0x3C, 0x7C, 0x40, 0x00 }, // U+0075 (u)
{ 0x1C, 0x3C, 0x60, 0x60, 0x3C, 0x1C, 0x00, 0x00 }, // U+0076 (v)
{ 0x3C, 0x7C, 0x70, 0x38, 0x70, 0x7C, 0x3C, 0x00 }, // U+0077 (w)
{ 0x44, 0x6C, 0x38, 0x10, 0x38, 0x6C, 0x44, 0x00 }, // U+0078 (x)
{ 0x9C, 0xBC, 0xA0, 0xA0, 0xFC, 0x7C, 0x00, 0x00 }, // U+0079 (y)
{ 0x4C, 0x64, 0x74, 0x5C, 0x4C, 0x64, 0x00, 0x00 }, // U+007A (z)
{ 0x08, 0x08, 0x3E, 0x77, 0x41, 0x41, 0x00, 0x00 }, // U+007B ({)
{ 0x00, 0x00, 0x00, 0x77, 0x77, 0x00, 0x00, 0x00 }, // U+007C (|)
{ 0x41, 0x41, 0x77, 0x3E, 0x08, 0x08, 0x00, 0x00 }, // U+007D (})
{ 0x02, 0x03, 0x01, 0x03, 0x02, 0x03, 0x01, 0x00 }, // U+007E (~)
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } // U+007F
};
#endif /* MAIN_FONT8X8_BASIC_H_ */

View File

@ -0,0 +1,619 @@
#include <string.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_log.h"
#include "ssd1306.h"
#include "font8x8_basic.h"
#define TAG "SSD1306"
#define PACK8 __attribute__((aligned( __alignof__( uint8_t ) ), packed ))
typedef union out_column_t {
uint32_t u32;
uint8_t u8[4];
} PACK8 out_column_t;
void ssd1306_init(SSD1306_t * dev, int width, int height)
{
if (dev->_address == SPIAddress) {
spi_init(dev, width, height);
} else {
i2c_init(dev, width, height);
}
// Initialize internal buffer
for (int i=0;i<dev->_pages;i++) {
memset(dev->_page[i]._segs, 0, 128);
}
}
int ssd1306_get_width(SSD1306_t * dev)
{
return dev->_width;
}
int ssd1306_get_height(SSD1306_t * dev)
{
return dev->_height;
}
int ssd1306_get_pages(SSD1306_t * dev)
{
return dev->_pages;
}
void ssd1306_show_buffer(SSD1306_t * dev)
{
if (dev->_address == SPIAddress) {
for (int page=0; page<dev->_pages;page++) {
spi_display_image(dev, page, 0, dev->_page[page]._segs, dev->_width);
}
} else {
for (int page=0; page<dev->_pages;page++) {
i2c_display_image(dev, page, 0, dev->_page[page]._segs, dev->_width);
}
}
}
void ssd1306_set_buffer(SSD1306_t * dev, uint8_t * buffer)
{
int index = 0;
for (int page=0; page<dev->_pages;page++) {
memcpy(&dev->_page[page]._segs, &buffer[index], 128);
index = index + 128;
}
}
void ssd1306_get_buffer(SSD1306_t * dev, uint8_t * buffer)
{
int index = 0;
for (int page=0; page<dev->_pages;page++) {
memcpy(&buffer[index], &dev->_page[page]._segs, 128);
index = index + 128;
}
}
void ssd1306_display_image(SSD1306_t * dev, int page, int seg, uint8_t * images, int width)
{
if (dev->_address == SPIAddress) {
spi_display_image(dev, page, seg, images, width);
} else {
i2c_display_image(dev, page, seg, images, width);
}
// Set to internal buffer
memcpy(&dev->_page[page]._segs[seg], images, width);
}
void ssd1306_display_text(SSD1306_t * dev, int page, char * text, int text_len, bool invert)
{
if (page >= dev->_pages) return;
int _text_len = text_len;
if (_text_len > 16) _text_len = 16;
uint8_t seg = 0;
uint8_t image[8];
for (uint8_t i = 0; i < _text_len; i++) {
memcpy(image, font8x8_basic_tr[(uint8_t)text[i]], 8);
if (invert) ssd1306_invert(image, 8);
if (dev->_flip) ssd1306_flip(image, 8);
ssd1306_display_image(dev, page, seg, image, 8);
#if 0
if (dev->_address == SPIAddress) {
spi_display_image(dev, page, seg, image, 8);
} else {
i2c_display_image(dev, page, seg, image, 8);
}
#endif
seg = seg + 8;
}
}
// by Coert Vonk
void
ssd1306_display_text_x3(SSD1306_t * dev, int page, char * text, int text_len, bool invert)
{
if (page >= dev->_pages) return;
int _text_len = text_len;
if (_text_len > 5) _text_len = 5;
uint8_t seg = 0;
for (uint8_t nn = 0; nn < _text_len; nn++) {
uint8_t const * const in_columns = font8x8_basic_tr[(uint8_t)text[nn]];
// make the character 3x as high
out_column_t out_columns[8];
memset(out_columns, 0, sizeof(out_columns));
for (uint8_t xx = 0; xx < 8; xx++) { // for each column (x-direction)
uint32_t in_bitmask = 0b1;
uint32_t out_bitmask = 0b111;
for (uint8_t yy = 0; yy < 8; yy++) { // for pixel (y-direction)
if (in_columns[xx] & in_bitmask) {
out_columns[xx].u32 |= out_bitmask;
}
in_bitmask <<= 1;
out_bitmask <<= 3;
}
}
// render character in 8 column high pieces, making them 3x as wide
for (uint8_t yy = 0; yy < 3; yy++) { // for each group of 8 pixels high (y-direction)
uint8_t image[24];
for (uint8_t xx = 0; xx < 8; xx++) { // for each column (x-direction)
image[xx*3+0] =
image[xx*3+1] =
image[xx*3+2] = out_columns[xx].u8[yy];
}
if (invert) ssd1306_invert(image, 24);
if (dev->_flip) ssd1306_flip(image, 24);
if (dev->_address == SPIAddress) {
spi_display_image(dev, page+yy, seg, image, 24);
} else {
i2c_display_image(dev, page+yy, seg, image, 24);
}
memcpy(&dev->_page[page+yy]._segs[seg], image, 24);
}
seg = seg + 24;
}
}
void ssd1306_clear_screen(SSD1306_t * dev, bool invert)
{
char space[16];
memset(space, 0x00, sizeof(space));
for (int page = 0; page < dev->_pages; page++) {
ssd1306_display_text(dev, page, space, sizeof(space), invert);
}
}
void ssd1306_clear_line(SSD1306_t * dev, int page, bool invert)
{
char space[16];
memset(space, 0x00, sizeof(space));
ssd1306_display_text(dev, page, space, sizeof(space), invert);
}
void ssd1306_contrast(SSD1306_t * dev, int contrast)
{
if (dev->_address == SPIAddress) {
spi_contrast(dev, contrast);
} else {
i2c_contrast(dev, contrast);
}
}
void ssd1306_software_scroll(SSD1306_t * dev, int start, int end)
{
ESP_LOGD(TAG, "software_scroll start=%d end=%d _pages=%d", start, end, dev->_pages);
if (start < 0 || end < 0) {
dev->_scEnable = false;
} else if (start >= dev->_pages || end >= dev->_pages) {
dev->_scEnable = false;
} else {
dev->_scEnable = true;
dev->_scStart = start;
dev->_scEnd = end;
dev->_scDirection = 1;
if (start > end ) dev->_scDirection = -1;
}
}
void ssd1306_scroll_text(SSD1306_t * dev, char * text, int text_len, bool invert)
{
ESP_LOGD(TAG, "dev->_scEnable=%d", dev->_scEnable);
if (dev->_scEnable == false) return;
void (*func)(SSD1306_t * dev, int page, int seg, uint8_t * images, int width);
if (dev->_address == SPIAddress) {
func = spi_display_image;
} else {
func = i2c_display_image;
}
int srcIndex = dev->_scEnd - dev->_scDirection;
while(1) {
int dstIndex = srcIndex + dev->_scDirection;
ESP_LOGD(TAG, "srcIndex=%d dstIndex=%d", srcIndex,dstIndex);
for(int seg = 0; seg < dev->_width; seg++) {
dev->_page[dstIndex]._segs[seg] = dev->_page[srcIndex]._segs[seg];
}
(*func)(dev, dstIndex, 0, dev->_page[dstIndex]._segs, sizeof(dev->_page[dstIndex]._segs));
if (srcIndex == dev->_scStart) break;
srcIndex = srcIndex - dev->_scDirection;
}
int _text_len = text_len;
if (_text_len > 16) _text_len = 16;
ssd1306_display_text(dev, srcIndex, text, text_len, invert);
}
void ssd1306_scroll_clear(SSD1306_t * dev)
{
ESP_LOGD(TAG, "dev->_scEnable=%d", dev->_scEnable);
if (dev->_scEnable == false) return;
int srcIndex = dev->_scEnd - dev->_scDirection;
while(1) {
int dstIndex = srcIndex + dev->_scDirection;
ESP_LOGD(TAG, "srcIndex=%d dstIndex=%d", srcIndex,dstIndex);
ssd1306_clear_line(dev, dstIndex, false);
if (dstIndex == dev->_scStart) break;
srcIndex = srcIndex - dev->_scDirection;
}
}
void ssd1306_hardware_scroll(SSD1306_t * dev, ssd1306_scroll_type_t scroll)
{
if (dev->_address == SPIAddress) {
spi_hardware_scroll(dev, scroll);
} else {
i2c_hardware_scroll(dev, scroll);
}
}
// delay = 0 : display with no wait
// delay > 0 : display with wait
// delay < 0 : no display
void ssd1306_wrap_arround(SSD1306_t * dev, ssd1306_scroll_type_t scroll, int start, int end, int8_t delay)
{
if (scroll == SCROLL_RIGHT) {
int _start = start; // 0 to 7
int _end = end; // 0 to 7
if (_end >= dev->_pages) _end = dev->_pages - 1;
uint8_t wk;
//for (int page=0;page<dev->_pages;page++) {
for (int page=_start;page<=_end;page++) {
wk = dev->_page[page]._segs[127];
for (int seg=127;seg>0;seg--) {
dev->_page[page]._segs[seg] = dev->_page[page]._segs[seg-1];
}
dev->_page[page]._segs[0] = wk;
}
} else if (scroll == SCROLL_LEFT) {
int _start = start; // 0 to 7
int _end = end; // 0 to 7
if (_end >= dev->_pages) _end = dev->_pages - 1;
uint8_t wk;
//for (int page=0;page<dev->_pages;page++) {
for (int page=_start;page<=_end;page++) {
wk = dev->_page[page]._segs[0];
for (int seg=0;seg<127;seg++) {
dev->_page[page]._segs[seg] = dev->_page[page]._segs[seg+1];
}
dev->_page[page]._segs[127] = wk;
}
} else if (scroll == SCROLL_UP) {
int _start = start; // 0 to {width-1}
int _end = end; // 0 to {width-1}
if (_end >= dev->_width) _end = dev->_width - 1;
uint8_t wk0;
uint8_t wk1;
uint8_t wk2;
uint8_t save[128];
// Save pages 0
for (int seg=0;seg<128;seg++) {
save[seg] = dev->_page[0]._segs[seg];
}
// Page0 to Page6
for (int page=0;page<dev->_pages-1;page++) {
//for (int seg=0;seg<128;seg++) {
for (int seg=_start;seg<=_end;seg++) {
wk0 = dev->_page[page]._segs[seg];
wk1 = dev->_page[page+1]._segs[seg];
if (dev->_flip) wk0 = ssd1306_rotate_byte(wk0);
if (dev->_flip) wk1 = ssd1306_rotate_byte(wk1);
if (seg == 0) {
ESP_LOGD(TAG, "b page=%d wk0=%02x wk1=%02x", page, wk0, wk1);
}
wk0 = wk0 >> 1;
wk1 = wk1 & 0x01;
wk1 = wk1 << 7;
wk2 = wk0 | wk1;
if (seg == 0) {
ESP_LOGD(TAG, "a page=%d wk0=%02x wk1=%02x wk2=%02x", page, wk0, wk1, wk2);
}
if (dev->_flip) wk2 = ssd1306_rotate_byte(wk2);
dev->_page[page]._segs[seg] = wk2;
}
}
// Page7
int pages = dev->_pages-1;
//for (int seg=0;seg<128;seg++) {
for (int seg=_start;seg<=_end;seg++) {
wk0 = dev->_page[pages]._segs[seg];
wk1 = save[seg];
if (dev->_flip) wk0 = ssd1306_rotate_byte(wk0);
if (dev->_flip) wk1 = ssd1306_rotate_byte(wk1);
wk0 = wk0 >> 1;
wk1 = wk1 & 0x01;
wk1 = wk1 << 7;
wk2 = wk0 | wk1;
if (dev->_flip) wk2 = ssd1306_rotate_byte(wk2);
dev->_page[pages]._segs[seg] = wk2;
}
} else if (scroll == SCROLL_DOWN) {
int _start = start; // 0 to {width-1}
int _end = end; // 0 to {width-1}
if (_end >= dev->_width) _end = dev->_width - 1;
uint8_t wk0;
uint8_t wk1;
uint8_t wk2;
uint8_t save[128];
// Save pages 7
int pages = dev->_pages-1;
for (int seg=0;seg<128;seg++) {
save[seg] = dev->_page[pages]._segs[seg];
}
// Page7 to Page1
for (int page=pages;page>0;page--) {
//for (int seg=0;seg<128;seg++) {
for (int seg=_start;seg<=_end;seg++) {
wk0 = dev->_page[page]._segs[seg];
wk1 = dev->_page[page-1]._segs[seg];
if (dev->_flip) wk0 = ssd1306_rotate_byte(wk0);
if (dev->_flip) wk1 = ssd1306_rotate_byte(wk1);
if (seg == 0) {
ESP_LOGD(TAG, "b page=%d wk0=%02x wk1=%02x", page, wk0, wk1);
}
wk0 = wk0 << 1;
wk1 = wk1 & 0x80;
wk1 = wk1 >> 7;
wk2 = wk0 | wk1;
if (seg == 0) {
ESP_LOGD(TAG, "a page=%d wk0=%02x wk1=%02x wk2=%02x", page, wk0, wk1, wk2);
}
if (dev->_flip) wk2 = ssd1306_rotate_byte(wk2);
dev->_page[page]._segs[seg] = wk2;
}
}
// Page0
//for (int seg=0;seg<128;seg++) {
for (int seg=_start;seg<=_end;seg++) {
wk0 = dev->_page[0]._segs[seg];
wk1 = save[seg];
if (dev->_flip) wk0 = ssd1306_rotate_byte(wk0);
if (dev->_flip) wk1 = ssd1306_rotate_byte(wk1);
wk0 = wk0 << 1;
wk1 = wk1 & 0x80;
wk1 = wk1 >> 7;
wk2 = wk0 | wk1;
if (dev->_flip) wk2 = ssd1306_rotate_byte(wk2);
dev->_page[0]._segs[seg] = wk2;
}
}
if (delay >= 0) {
for (int page=0;page<dev->_pages;page++) {
if (dev->_address == SPIAddress) {
spi_display_image(dev, page, 0, dev->_page[page]._segs, 128);
} else {
i2c_display_image(dev, page, 0, dev->_page[page]._segs, 128);
}
if (delay) vTaskDelay(delay);
}
}
}
void ssd1306_bitmaps(SSD1306_t * dev, int xpos, int ypos, uint8_t * bitmap, int width, int height, bool invert)
{
if ( (width % 8) != 0) {
ESP_LOGE(TAG, "width must be a multiple of 8");
return;
}
int _width = width / 8;
uint8_t wk0;
uint8_t wk1;
uint8_t wk2;
uint8_t page = (ypos / 8);
uint8_t _seg = xpos;
uint8_t dstBits = (ypos % 8);
ESP_LOGD(TAG, "ypos=%d page=%d dstBits=%d", ypos, page, dstBits);
int offset = 0;
for(int _height=0;_height<height;_height++) {
for (int index=0;index<_width;index++) {
for (int srcBits=7; srcBits>=0; srcBits--) {
wk0 = dev->_page[page]._segs[_seg];
if (dev->_flip) wk0 = ssd1306_rotate_byte(wk0);
wk1 = bitmap[index+offset];
if (invert) wk1 = ~wk1;
//wk2 = ssd1306_copy_bit(bitmap[index+offset], srcBits, wk0, dstBits);
wk2 = ssd1306_copy_bit(wk1, srcBits, wk0, dstBits);
if (dev->_flip) wk2 = ssd1306_rotate_byte(wk2);
ESP_LOGD(TAG, "index=%d offset=%d page=%d _seg=%d, wk2=%02x", index, offset, page, _seg, wk2);
dev->_page[page]._segs[_seg] = wk2;
_seg++;
}
}
vTaskDelay(1);
offset = offset + _width;
dstBits++;
_seg = xpos;
if (dstBits == 8) {
page++;
dstBits=0;
}
}
#if 0
for (int _seg=ypos;_seg<ypos+width;_seg++) {
ssd1306_dump_page(dev, page-1, _seg);
}
for (int _seg=ypos;_seg<ypos+width;_seg++) {
ssd1306_dump_page(dev, page, _seg);
}
#endif
ssd1306_show_buffer(dev);
}
// Set pixel to internal buffer. Not show it.
void _ssd1306_pixel(SSD1306_t * dev, int xpos, int ypos, bool invert)
{
uint8_t _page = (ypos / 8);
uint8_t _bits = (ypos % 8);
uint8_t _seg = xpos;
uint8_t wk0 = dev->_page[_page]._segs[_seg];
uint8_t wk1 = 1 << _bits;
ESP_LOGD(TAG, "ypos=%d _page=%d _bits=%d wk0=0x%02x wk1=0x%02x", ypos, _page, _bits, wk0, wk1);
if (invert) {
wk0 = wk0 & ~wk1;
} else {
wk0 = wk0 | wk1;
}
if (dev->_flip) wk0 = ssd1306_rotate_byte(wk0);
ESP_LOGD(TAG, "wk0=0x%02x wk1=0x%02x", wk0, wk1);
dev->_page[_page]._segs[_seg] = wk0;
}
// Set line to internal buffer. Not show it.
void _ssd1306_line(SSD1306_t * dev, int x1, int y1, int x2, int y2, bool invert)
{
int i;
int dx,dy;
int sx,sy;
int E;
/* distance between two points */
dx = ( x2 > x1 ) ? x2 - x1 : x1 - x2;
dy = ( y2 > y1 ) ? y2 - y1 : y1 - y2;
/* direction of two point */
sx = ( x2 > x1 ) ? 1 : -1;
sy = ( y2 > y1 ) ? 1 : -1;
/* inclination < 1 */
if ( dx > dy ) {
E = -dx;
for ( i = 0 ; i <= dx ; i++ ) {
_ssd1306_pixel(dev, x1, y1, invert);
x1 += sx;
E += 2 * dy;
if ( E >= 0 ) {
y1 += sy;
E -= 2 * dx;
}
}
/* inclination >= 1 */
} else {
E = -dy;
for ( i = 0 ; i <= dy ; i++ ) {
_ssd1306_pixel(dev, x1, y1, invert);
y1 += sy;
E += 2 * dx;
if ( E >= 0 ) {
x1 += sx;
E -= 2 * dy;
}
}
}
}
void ssd1306_invert(uint8_t *buf, size_t blen)
{
uint8_t wk;
for(int i=0; i<blen; i++){
wk = buf[i];
buf[i] = ~wk;
}
}
// Flip upside down
void ssd1306_flip(uint8_t *buf, size_t blen)
{
for(int i=0; i<blen; i++){
buf[i] = ssd1306_rotate_byte(buf[i]);
}
}
uint8_t ssd1306_copy_bit(uint8_t src, int srcBits, uint8_t dst, int dstBits)
{
ESP_LOGD(TAG, "src=%02x srcBits=%d dst=%02x dstBits=%d", src, srcBits, dst, dstBits);
uint8_t smask = 0x01 << srcBits;
uint8_t dmask = 0x01 << dstBits;
uint8_t _src = src & smask;
#if 0
if (_src != 0) _src = 1;
uint8_t _wk = _src << dstBits;
uint8_t _dst = dst | _wk;
#endif
uint8_t _dst;
if (_src != 0) {
_dst = dst | dmask; // set bit
} else {
_dst = dst & ~(dmask); // clear bit
}
return _dst;
}
// Rotate 8-bit data
// 0x12-->0x48
uint8_t ssd1306_rotate_byte(uint8_t ch1) {
uint8_t ch2 = 0;
for (int j=0;j<8;j++) {
ch2 = (ch2 << 1) + (ch1 & 0x01);
ch1 = ch1 >> 1;
}
return ch2;
}
void ssd1306_fadeout(SSD1306_t * dev)
{
void (*func)(SSD1306_t * dev, int page, int seg, uint8_t * images, int width);
if (dev->_address == SPIAddress) {
func = spi_display_image;
} else {
func = i2c_display_image;
}
uint8_t image[1];
for(int page=0; page<dev->_pages; page++) {
image[0] = 0xFF;
for(int line=0; line<8; line++) {
if (dev->_flip) {
image[0] = image[0] >> 1;
} else {
image[0] = image[0] << 1;
}
for(int seg=0; seg<128; seg++) {
(*func)(dev, page, seg, image, 1);
dev->_page[page]._segs[seg] = image[0];
}
}
}
}
void ssd1306_dump(SSD1306_t dev)
{
printf("_address=%x\n",dev._address);
printf("_width=%x\n",dev._width);
printf("_height=%x\n",dev._height);
printf("_pages=%x\n",dev._pages);
}
void ssd1306_dump_page(SSD1306_t * dev, int page, int seg)
{
ESP_LOGI(TAG, "dev->_page[%d]._segs[%d]=%02x", page, seg, dev->_page[page]._segs[seg]);
}

View File

@ -0,0 +1,157 @@
#ifndef MAIN_SSD1306_H_
#define MAIN_SSD1306_H_
#include "driver/spi_master.h"
// Following definitions are bollowed from
// http://robotcantalk.blogspot.com/2015/03/interfacing-arduino-with-ssd1306-driven.html
/* Control byte for i2c
Co : bit 8 : Continuation Bit
* 1 = no-continuation (only one byte to follow)
* 0 = the controller should expect a stream of bytes.
D/C# : bit 7 : Data/Command Select bit
* 1 = the next byte or byte stream will be Data.
* 0 = a Command byte or byte stream will be coming up next.
Bits 6-0 will be all zeros.
Usage:
0x80 : Single Command byte
0x00 : Command Stream
0xC0 : Single Data byte
0x40 : Data Stream
*/
#define OLED_CONTROL_BYTE_CMD_SINGLE 0x80
#define OLED_CONTROL_BYTE_CMD_STREAM 0x00
#define OLED_CONTROL_BYTE_DATA_SINGLE 0xC0
#define OLED_CONTROL_BYTE_DATA_STREAM 0x40
// Fundamental commands (pg.28)
#define OLED_CMD_SET_CONTRAST 0x81 // follow with 0x7F
#define OLED_CMD_DISPLAY_RAM 0xA4
#define OLED_CMD_DISPLAY_ALLON 0xA5
#define OLED_CMD_DISPLAY_NORMAL 0xA6
#define OLED_CMD_DISPLAY_INVERTED 0xA7
#define OLED_CMD_DISPLAY_OFF 0xAE
#define OLED_CMD_DISPLAY_ON 0xAF
// Addressing Command Table (pg.30)
#define OLED_CMD_SET_MEMORY_ADDR_MODE 0x20
#define OLED_CMD_SET_HORI_ADDR_MODE 0x00 // Horizontal Addressing Mode
#define OLED_CMD_SET_VERT_ADDR_MODE 0x01 // Vertical Addressing Mode
#define OLED_CMD_SET_PAGE_ADDR_MODE 0x02 // Page Addressing Mode
#define OLED_CMD_SET_COLUMN_RANGE 0x21 // can be used only in HORZ/VERT mode - follow with 0x00 and 0x7F = COL127
#define OLED_CMD_SET_PAGE_RANGE 0x22 // can be used only in HORZ/VERT mode - follow with 0x00 and 0x07 = PAGE7
// Hardware Config (pg.31)
#define OLED_CMD_SET_DISPLAY_START_LINE 0x40
#define OLED_CMD_SET_SEGMENT_REMAP_0 0xA0
#define OLED_CMD_SET_SEGMENT_REMAP_1 0xA1
#define OLED_CMD_SET_MUX_RATIO 0xA8 // follow with 0x3F = 64 MUX
#define OLED_CMD_SET_COM_SCAN_MODE 0xC8
#define OLED_CMD_SET_DISPLAY_OFFSET 0xD3 // follow with 0x00
#define OLED_CMD_SET_COM_PIN_MAP 0xDA // follow with 0x12
#define OLED_CMD_NOP 0xE3 // NOP
// Timing and Driving Scheme (pg.32)
#define OLED_CMD_SET_DISPLAY_CLK_DIV 0xD5 // follow with 0x80
#define OLED_CMD_SET_PRECHARGE 0xD9 // follow with 0xF1
#define OLED_CMD_SET_VCOMH_DESELCT 0xDB // follow with 0x30
// Charge Pump (pg.62)
#define OLED_CMD_SET_CHARGE_PUMP 0x8D // follow with 0x14
// Scrolling Command
#define OLED_CMD_HORIZONTAL_RIGHT 0x26
#define OLED_CMD_HORIZONTAL_LEFT 0x27
#define OLED_CMD_CONTINUOUS_SCROLL 0x29
#define OLED_CMD_DEACTIVE_SCROLL 0x2E
#define OLED_CMD_ACTIVE_SCROLL 0x2F
#define OLED_CMD_VERTICAL 0xA3
#define I2CAddress 0x3C
#define SPIAddress 0xFF
typedef enum {
SCROLL_RIGHT = 1,
SCROLL_LEFT = 2,
SCROLL_DOWN = 3,
SCROLL_UP = 4,
SCROLL_STOP = 5
} ssd1306_scroll_type_t;
typedef struct {
bool _valid; // Not using it anymore
int _segLen; // Not using it anymore
uint8_t _segs[128];
} PAGE_t;
typedef struct {
int _address;
int _width;
int _height;
int _pages;
int _dc;
spi_device_handle_t _SPIHandle;
bool _scEnable;
int _scStart;
int _scEnd;
int _scDirection;
PAGE_t _page[8];
bool _flip;
} SSD1306_t;
#ifdef __cplusplus
extern "C"
{
#endif
void ssd1306_init(SSD1306_t * dev, int width, int height);
int ssd1306_get_width(SSD1306_t * dev);
int ssd1306_get_height(SSD1306_t * dev);
int ssd1306_get_pages(SSD1306_t * dev);
void ssd1306_show_buffer(SSD1306_t * dev);
void ssd1306_set_buffer(SSD1306_t * dev, uint8_t * buffer);
void ssd1306_get_buffer(SSD1306_t * dev, uint8_t * buffer);
void ssd1306_display_image(SSD1306_t * dev, int page, int seg, uint8_t * images, int width);
void ssd1306_display_text(SSD1306_t * dev, int page, char * text, int text_len, bool invert);
void ssd1306_display_text_x3(SSD1306_t * dev, int page, char * text, int text_len, bool invert);
void ssd1306_clear_screen(SSD1306_t * dev, bool invert);
void ssd1306_clear_line(SSD1306_t * dev, int page, bool invert);
void ssd1306_contrast(SSD1306_t * dev, int contrast);
void ssd1306_software_scroll(SSD1306_t * dev, int start, int end);
void ssd1306_scroll_text(SSD1306_t * dev, char * text, int text_len, bool invert);
void ssd1306_scroll_clear(SSD1306_t * dev);
void ssd1306_hardware_scroll(SSD1306_t * dev, ssd1306_scroll_type_t scroll);
void ssd1306_wrap_arround(SSD1306_t * dev, ssd1306_scroll_type_t scroll, int start, int end, int8_t delay);
void ssd1306_bitmaps(SSD1306_t * dev, int xpos, int ypos, uint8_t * bitmap, int width, int height, bool invert);
void _ssd1306_pixel(SSD1306_t * dev, int xpos, int ypos, bool invert);
void _ssd1306_line(SSD1306_t * dev, int x1, int y1, int x2, int y2, bool invert);
void ssd1306_invert(uint8_t *buf, size_t blen);
void ssd1306_flip(uint8_t *buf, size_t blen);
uint8_t ssd1306_copy_bit(uint8_t src, int srcBits, uint8_t dst, int dstBits);
uint8_t ssd1306_rotate_byte(uint8_t ch1);
void ssd1306_fadeout(SSD1306_t * dev);
void ssd1306_dump(SSD1306_t dev);
void ssd1306_dump_page(SSD1306_t * dev, int page, int seg);
void i2c_master_init(SSD1306_t * dev, int16_t sda, int16_t scl, int16_t reset);
void i2c_init(SSD1306_t * dev, int width, int height);
void i2c_display_image(SSD1306_t * dev, int page, int seg, uint8_t * images, int width);
void i2c_contrast(SSD1306_t * dev, int contrast);
void i2c_hardware_scroll(SSD1306_t * dev, ssd1306_scroll_type_t scroll);
void spi_master_init(SSD1306_t * dev, int16_t GPIO_MOSI, int16_t GPIO_SCLK, int16_t GPIO_CS, int16_t GPIO_DC, int16_t GPIO_RESET);
bool spi_master_write_byte(spi_device_handle_t SPIHandle, const uint8_t* Data, size_t DataLength );
bool spi_master_write_command(SSD1306_t * dev, uint8_t Command );
bool spi_master_write_data(SSD1306_t * dev, const uint8_t* Data, size_t DataLength );
void spi_init(SSD1306_t * dev, int width, int height);
void spi_display_image(SSD1306_t * dev, int page, int seg, uint8_t * images, int width);
void spi_contrast(SSD1306_t * dev, int contrast);
void spi_hardware_scroll(SSD1306_t * dev, ssd1306_scroll_type_t scroll);
#ifdef __cplusplus
}
#endif
#endif /* MAIN_SSD1306_H_ */

View File

@ -0,0 +1,253 @@
#include <string.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "driver/i2c.h"
#include "esp_log.h"
#include "ssd1306.h"
#define tag "SSD1306"
#if CONFIG_I2C_PORT_0
#define I2C_NUM I2C_NUM_0
#elif CONFIG_I2C_PORT_1
#define I2C_NUM I2C_NUM_1
#else
#define I2C_NUM I2C_NUM_0 // if spi is selected
#endif
#define I2C_MASTER_FREQ_HZ 400000 /*!< I2C clock of SSD1306 can run at 400 kHz max. */
void i2c_master_init(SSD1306_t * dev, int16_t sda, int16_t scl, int16_t reset)
{
i2c_config_t i2c_config = {
.mode = I2C_MODE_MASTER,
.sda_io_num = sda,
.scl_io_num = scl,
.sda_pullup_en = GPIO_PULLUP_ENABLE,
.scl_pullup_en = GPIO_PULLUP_ENABLE,
.master.clk_speed = I2C_MASTER_FREQ_HZ
};
ESP_ERROR_CHECK(i2c_param_config(I2C_NUM, &i2c_config));
ESP_ERROR_CHECK(i2c_driver_install(I2C_NUM, I2C_MODE_MASTER, 0, 0, 0));
if (reset >= 0) {
//gpio_pad_select_gpio(reset);
gpio_reset_pin(reset);
gpio_set_direction(reset, GPIO_MODE_OUTPUT);
gpio_set_level(reset, 0);
vTaskDelay(50 / portTICK_PERIOD_MS);
gpio_set_level(reset, 1);
}
dev->_address = I2CAddress;
dev->_flip = false;
}
void i2c_init(SSD1306_t * dev, int width, int height) {
dev->_width = width;
dev->_height = height;
dev->_pages = 8;
if (dev->_height == 32) dev->_pages = 4;
i2c_cmd_handle_t cmd = i2c_cmd_link_create();
i2c_master_start(cmd);
i2c_master_write_byte(cmd, (dev->_address << 1) | I2C_MASTER_WRITE, true);
i2c_master_write_byte(cmd, OLED_CONTROL_BYTE_CMD_STREAM, true);
i2c_master_write_byte(cmd, OLED_CMD_DISPLAY_OFF, true); // AE
i2c_master_write_byte(cmd, OLED_CMD_SET_MUX_RATIO, true); // A8
if (dev->_height == 64) i2c_master_write_byte(cmd, 0x3F, true);
if (dev->_height == 32) i2c_master_write_byte(cmd, 0x1F, true);
i2c_master_write_byte(cmd, OLED_CMD_SET_DISPLAY_OFFSET, true); // D3
i2c_master_write_byte(cmd, 0x00, true);
//i2c_master_write_byte(cmd, OLED_CONTROL_BYTE_DATA_STREAM, true); // 40
i2c_master_write_byte(cmd, OLED_CMD_SET_DISPLAY_START_LINE, true); // 40
//i2c_master_write_byte(cmd, OLED_CMD_SET_SEGMENT_REMAP, true); // A1
if (dev->_flip) {
i2c_master_write_byte(cmd, OLED_CMD_SET_SEGMENT_REMAP_0, true); // A0
} else {
i2c_master_write_byte(cmd, OLED_CMD_SET_SEGMENT_REMAP_1, true); // A1
}
i2c_master_write_byte(cmd, OLED_CMD_SET_COM_SCAN_MODE, true); // C8
i2c_master_write_byte(cmd, OLED_CMD_SET_DISPLAY_CLK_DIV, true); // D5
i2c_master_write_byte(cmd, 0x80, true);
i2c_master_write_byte(cmd, OLED_CMD_SET_COM_PIN_MAP, true); // DA
if (dev->_height == 64) i2c_master_write_byte(cmd, 0x12, true);
if (dev->_height == 32) i2c_master_write_byte(cmd, 0x02, true);
i2c_master_write_byte(cmd, OLED_CMD_SET_CONTRAST, true); // 81
i2c_master_write_byte(cmd, 0xFF, true);
i2c_master_write_byte(cmd, OLED_CMD_DISPLAY_RAM, true); // A4
i2c_master_write_byte(cmd, OLED_CMD_SET_VCOMH_DESELCT, true); // DB
i2c_master_write_byte(cmd, 0x40, true);
i2c_master_write_byte(cmd, OLED_CMD_SET_MEMORY_ADDR_MODE, true); // 20
//i2c_master_write_byte(cmd, OLED_CMD_SET_HORI_ADDR_MODE, true); // 00
i2c_master_write_byte(cmd, OLED_CMD_SET_PAGE_ADDR_MODE, true); // 02
// Set Lower Column Start Address for Page Addressing Mode
i2c_master_write_byte(cmd, 0x00, true);
// Set Higher Column Start Address for Page Addressing Mode
i2c_master_write_byte(cmd, 0x10, true);
i2c_master_write_byte(cmd, OLED_CMD_SET_CHARGE_PUMP, true); // 8D
i2c_master_write_byte(cmd, 0x14, true);
i2c_master_write_byte(cmd, OLED_CMD_DEACTIVE_SCROLL, true); // 2E
i2c_master_write_byte(cmd, OLED_CMD_DISPLAY_NORMAL, true); // A6
i2c_master_write_byte(cmd, OLED_CMD_DISPLAY_ON, true); // AF
i2c_master_stop(cmd);
esp_err_t espRc = i2c_master_cmd_begin(I2C_NUM, cmd, 10/portTICK_PERIOD_MS);
if (espRc == ESP_OK) {
ESP_LOGI(tag, "OLED configured successfully");
} else {
ESP_LOGE(tag, "OLED configuration failed. code: 0x%.2X", espRc);
}
i2c_cmd_link_delete(cmd);
}
void i2c_display_image(SSD1306_t * dev, int page, int seg, uint8_t * images, int width) {
i2c_cmd_handle_t cmd;
if (page >= dev->_pages) return;
if (seg >= dev->_width) return;
int _seg = seg + CONFIG_OFFSETX;
uint8_t columLow = _seg & 0x0F;
uint8_t columHigh = (_seg >> 4) & 0x0F;
int _page = page;
if (dev->_flip) {
_page = (dev->_pages - page) - 1;
}
cmd = i2c_cmd_link_create();
i2c_master_start(cmd);
i2c_master_write_byte(cmd, (dev->_address << 1) | I2C_MASTER_WRITE, true);
i2c_master_write_byte(cmd, OLED_CONTROL_BYTE_CMD_STREAM, true);
// Set Lower Column Start Address for Page Addressing Mode
i2c_master_write_byte(cmd, (0x00 + columLow), true);
// Set Higher Column Start Address for Page Addressing Mode
i2c_master_write_byte(cmd, (0x10 + columHigh), true);
// Set Page Start Address for Page Addressing Mode
i2c_master_write_byte(cmd, 0xB0 | _page, true);
i2c_master_stop(cmd);
i2c_master_cmd_begin(I2C_NUM, cmd, 10/portTICK_PERIOD_MS);
i2c_cmd_link_delete(cmd);
cmd = i2c_cmd_link_create();
i2c_master_start(cmd);
i2c_master_write_byte(cmd, (dev->_address << 1) | I2C_MASTER_WRITE, true);
i2c_master_write_byte(cmd, OLED_CONTROL_BYTE_DATA_STREAM, true);
i2c_master_write(cmd, images, width, true);
i2c_master_stop(cmd);
i2c_master_cmd_begin(I2C_NUM, cmd, 10/portTICK_PERIOD_MS);
i2c_cmd_link_delete(cmd);
}
void i2c_contrast(SSD1306_t * dev, int contrast) {
i2c_cmd_handle_t cmd;
int _contrast = contrast;
if (contrast < 0x0) _contrast = 0;
if (contrast > 0xFF) _contrast = 0xFF;
cmd = i2c_cmd_link_create();
i2c_master_start(cmd);
i2c_master_write_byte(cmd, (dev->_address << 1) | I2C_MASTER_WRITE, true);
i2c_master_write_byte(cmd, OLED_CONTROL_BYTE_CMD_STREAM, true);
i2c_master_write_byte(cmd, OLED_CMD_SET_CONTRAST, true); // 81
i2c_master_write_byte(cmd, _contrast, true);
i2c_master_stop(cmd);
i2c_master_cmd_begin(I2C_NUM, cmd, 10/portTICK_PERIOD_MS);
i2c_cmd_link_delete(cmd);
}
void i2c_hardware_scroll(SSD1306_t * dev, ssd1306_scroll_type_t scroll) {
esp_err_t espRc;
i2c_cmd_handle_t cmd = i2c_cmd_link_create();
i2c_master_start(cmd);
i2c_master_write_byte(cmd, (dev->_address << 1) | I2C_MASTER_WRITE, true);
i2c_master_write_byte(cmd, OLED_CONTROL_BYTE_CMD_STREAM, true);
if (scroll == SCROLL_RIGHT) {
i2c_master_write_byte(cmd, OLED_CMD_HORIZONTAL_RIGHT, true); // 26
i2c_master_write_byte(cmd, 0x00, true); // Dummy byte
i2c_master_write_byte(cmd, 0x00, true); // Define start page address
i2c_master_write_byte(cmd, 0x07, true); // Frame frequency
i2c_master_write_byte(cmd, 0x07, true); // Define end page address
i2c_master_write_byte(cmd, 0x00, true); //
i2c_master_write_byte(cmd, 0xFF, true); //
i2c_master_write_byte(cmd, OLED_CMD_ACTIVE_SCROLL, true); // 2F
}
if (scroll == SCROLL_LEFT) {
i2c_master_write_byte(cmd, OLED_CMD_HORIZONTAL_LEFT, true); // 27
i2c_master_write_byte(cmd, 0x00, true); // Dummy byte
i2c_master_write_byte(cmd, 0x00, true); // Define start page address
i2c_master_write_byte(cmd, 0x07, true); // Frame frequency
i2c_master_write_byte(cmd, 0x07, true); // Define end page address
i2c_master_write_byte(cmd, 0x00, true); //
i2c_master_write_byte(cmd, 0xFF, true); //
i2c_master_write_byte(cmd, OLED_CMD_ACTIVE_SCROLL, true); // 2F
}
if (scroll == SCROLL_DOWN) {
i2c_master_write_byte(cmd, OLED_CMD_CONTINUOUS_SCROLL, true); // 29
i2c_master_write_byte(cmd, 0x00, true); // Dummy byte
i2c_master_write_byte(cmd, 0x00, true); // Define start page address
i2c_master_write_byte(cmd, 0x07, true); // Frame frequency
//i2c_master_write_byte(cmd, 0x01, true); // Define end page address
i2c_master_write_byte(cmd, 0x00, true); // Define end page address
i2c_master_write_byte(cmd, 0x3F, true); // Vertical scrolling offset
i2c_master_write_byte(cmd, OLED_CMD_VERTICAL, true); // A3
i2c_master_write_byte(cmd, 0x00, true);
if (dev->_height == 64)
//i2c_master_write_byte(cmd, 0x7F, true);
i2c_master_write_byte(cmd, 0x40, true);
if (dev->_height == 32)
i2c_master_write_byte(cmd, 0x20, true);
i2c_master_write_byte(cmd, OLED_CMD_ACTIVE_SCROLL, true); // 2F
}
if (scroll == SCROLL_UP) {
i2c_master_write_byte(cmd, OLED_CMD_CONTINUOUS_SCROLL, true); // 29
i2c_master_write_byte(cmd, 0x00, true); // Dummy byte
i2c_master_write_byte(cmd, 0x00, true); // Define start page address
i2c_master_write_byte(cmd, 0x07, true); // Frame frequency
//i2c_master_write_byte(cmd, 0x01, true); // Define end page address
i2c_master_write_byte(cmd, 0x00, true); // Define end page address
i2c_master_write_byte(cmd, 0x01, true); // Vertical scrolling offset
i2c_master_write_byte(cmd, OLED_CMD_VERTICAL, true); // A3
i2c_master_write_byte(cmd, 0x00, true);
if (dev->_height == 64)
//i2c_master_write_byte(cmd, 0x7F, true);
i2c_master_write_byte(cmd, 0x40, true);
if (dev->_height == 32)
i2c_master_write_byte(cmd, 0x20, true);
i2c_master_write_byte(cmd, OLED_CMD_ACTIVE_SCROLL, true); // 2F
}
if (scroll == SCROLL_STOP) {
i2c_master_write_byte(cmd, OLED_CMD_DEACTIVE_SCROLL, true); // 2E
}
i2c_master_stop(cmd);
espRc = i2c_master_cmd_begin(I2C_NUM, cmd, 10/portTICK_PERIOD_MS);
if (espRc == ESP_OK) {
ESP_LOGD(tag, "Scroll command succeeded");
} else {
ESP_LOGE(tag, "Scroll command failed. code: 0x%.2X", espRc);
}
i2c_cmd_link_delete(cmd);
}

View File

@ -0,0 +1,254 @@
#include <string.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "driver/spi_master.h"
#include "driver/gpio.h"
#include "esp_log.h"
#include "ssd1306.h"
#define TAG "SSD1306"
#if CONFIG_SPI2_HOST
#define HOST_ID SPI2_HOST
#elif CONFIG_SPI3_HOST
#define HOST_ID SPI3_HOST
#else
#define HOST_ID SPI2_HOST // If i2c is selected
#endif
static const int SPI_Command_Mode = 0;
static const int SPI_Data_Mode = 1;
static const int SPI_Frequency = 1000000; // 1MHz
void spi_master_init(SSD1306_t * dev, int16_t GPIO_MOSI, int16_t GPIO_SCLK, int16_t GPIO_CS, int16_t GPIO_DC, int16_t GPIO_RESET)
{
esp_err_t ret;
//gpio_pad_select_gpio( GPIO_CS );
gpio_reset_pin( GPIO_CS );
gpio_set_direction( GPIO_CS, GPIO_MODE_OUTPUT );
gpio_set_level( GPIO_CS, 0 );
//gpio_pad_select_gpio( GPIO_DC );
gpio_reset_pin( GPIO_DC );
gpio_set_direction( GPIO_DC, GPIO_MODE_OUTPUT );
gpio_set_level( GPIO_DC, 0 );
if ( GPIO_RESET >= 0 ) {
//gpio_pad_select_gpio( GPIO_RESET );
gpio_reset_pin( GPIO_RESET );
gpio_set_direction( GPIO_RESET, GPIO_MODE_OUTPUT );
gpio_set_level( GPIO_RESET, 0 );
vTaskDelay( pdMS_TO_TICKS( 100 ) );
gpio_set_level( GPIO_RESET, 1 );
}
spi_bus_config_t spi_bus_config = {
.mosi_io_num = GPIO_MOSI,
.miso_io_num = -1,
.sclk_io_num = GPIO_SCLK,
.quadwp_io_num = -1,
.quadhd_io_num = -1,
.max_transfer_sz = 0,
.flags = 0
};
ESP_LOGI(TAG, "SPI HOST_ID=%d", HOST_ID);
ret = spi_bus_initialize( HOST_ID, &spi_bus_config, SPI_DMA_CH_AUTO );
ESP_LOGI(TAG, "spi_bus_initialize=%d",ret);
assert(ret==ESP_OK);
spi_device_interface_config_t devcfg;
memset( &devcfg, 0, sizeof( spi_device_interface_config_t ) );
devcfg.clock_speed_hz = SPI_Frequency;
devcfg.spics_io_num = GPIO_CS;
devcfg.queue_size = 1;
spi_device_handle_t handle;
ret = spi_bus_add_device( HOST_ID, &devcfg, &handle);
ESP_LOGI(TAG, "spi_bus_add_device=%d",ret);
assert(ret==ESP_OK);
dev->_dc = GPIO_DC;
dev->_SPIHandle = handle;
dev->_address = SPIAddress;
dev->_flip = false;
}
bool spi_master_write_byte(spi_device_handle_t SPIHandle, const uint8_t* Data, size_t DataLength )
{
spi_transaction_t SPITransaction;
if ( DataLength > 0 ) {
memset( &SPITransaction, 0, sizeof( spi_transaction_t ) );
SPITransaction.length = DataLength * 8;
SPITransaction.tx_buffer = Data;
spi_device_transmit( SPIHandle, &SPITransaction );
}
return true;
}
bool spi_master_write_command(SSD1306_t * dev, uint8_t Command )
{
static uint8_t CommandByte = 0;
CommandByte = Command;
gpio_set_level( dev->_dc, SPI_Command_Mode );
return spi_master_write_byte( dev->_SPIHandle, &CommandByte, 1 );
}
bool spi_master_write_data(SSD1306_t * dev, const uint8_t* Data, size_t DataLength )
{
gpio_set_level( dev->_dc, SPI_Data_Mode );
return spi_master_write_byte( dev->_SPIHandle, Data, DataLength );
}
void spi_init(SSD1306_t * dev, int width, int height)
{
dev->_width = width;
dev->_height = height;
dev->_pages = 8;
if (dev->_height == 32) dev->_pages = 4;
spi_master_write_command(dev, OLED_CMD_DISPLAY_OFF); // AE
spi_master_write_command(dev, OLED_CMD_SET_MUX_RATIO); // A8
if (dev->_height == 64) spi_master_write_command(dev, 0x3F);
if (dev->_height == 32) spi_master_write_command(dev, 0x1F);
spi_master_write_command(dev, OLED_CMD_SET_DISPLAY_OFFSET); // D3
spi_master_write_command(dev, 0x00);
spi_master_write_command(dev, OLED_CONTROL_BYTE_DATA_STREAM); // 40
if (dev->_flip) {
spi_master_write_command(dev, OLED_CMD_SET_SEGMENT_REMAP_0); // A0
} else {
spi_master_write_command(dev, OLED_CMD_SET_SEGMENT_REMAP_1); // A1
}
//spi_master_write_command(dev, OLED_CMD_SET_SEGMENT_REMAP); // A1
spi_master_write_command(dev, OLED_CMD_SET_COM_SCAN_MODE); // C8
spi_master_write_command(dev, OLED_CMD_SET_DISPLAY_CLK_DIV); // D5
spi_master_write_command(dev, 0x80);
spi_master_write_command(dev, OLED_CMD_SET_COM_PIN_MAP); // DA
if (dev->_height == 64) spi_master_write_command(dev, 0x12);
if (dev->_height == 32) spi_master_write_command(dev, 0x02);
spi_master_write_command(dev, OLED_CMD_SET_CONTRAST); // 81
spi_master_write_command(dev, 0xFF);
spi_master_write_command(dev, OLED_CMD_DISPLAY_RAM); // A4
spi_master_write_command(dev, OLED_CMD_SET_VCOMH_DESELCT); // DB
spi_master_write_command(dev, 0x40);
spi_master_write_command(dev, OLED_CMD_SET_MEMORY_ADDR_MODE); // 20
//spi_master_write_command(dev, OLED_CMD_SET_HORI_ADDR_MODE); // 00
spi_master_write_command(dev, OLED_CMD_SET_PAGE_ADDR_MODE); // 02
// Set Lower Column Start Address for Page Addressing Mode
spi_master_write_command(dev, 0x00);
// Set Higher Column Start Address for Page Addressing Mode
spi_master_write_command(dev, 0x10);
spi_master_write_command(dev, OLED_CMD_SET_CHARGE_PUMP); // 8D
spi_master_write_command(dev, 0x14);
spi_master_write_command(dev, OLED_CMD_DEACTIVE_SCROLL); // 2E
spi_master_write_command(dev, OLED_CMD_DISPLAY_NORMAL); // A6
spi_master_write_command(dev, OLED_CMD_DISPLAY_ON); // AF
}
void spi_display_image(SSD1306_t * dev, int page, int seg, uint8_t * images, int width)
{
if (page >= dev->_pages) return;
if (seg >= dev->_width) return;
int _seg = seg + CONFIG_OFFSETX;
uint8_t columLow = _seg & 0x0F;
uint8_t columHigh = (_seg >> 4) & 0x0F;
int _page = page;
if (dev->_flip) {
_page = (dev->_pages - page) - 1;
}
// Set Lower Column Start Address for Page Addressing Mode
spi_master_write_command(dev, (0x00 + columLow));
// Set Higher Column Start Address for Page Addressing Mode
spi_master_write_command(dev, (0x10 + columHigh));
// Set Page Start Address for Page Addressing Mode
spi_master_write_command(dev, 0xB0 | _page);
spi_master_write_data(dev, images, width);
}
void spi_contrast(SSD1306_t * dev, int contrast) {
int _contrast = contrast;
if (contrast < 0x0) _contrast = 0;
if (contrast > 0xFF) _contrast = 0xFF;
spi_master_write_command(dev, OLED_CMD_SET_CONTRAST); // 81
spi_master_write_command(dev, _contrast);
}
void spi_hardware_scroll(SSD1306_t * dev, ssd1306_scroll_type_t scroll)
{
if (scroll == SCROLL_RIGHT) {
spi_master_write_command(dev, OLED_CMD_HORIZONTAL_RIGHT); // 26
spi_master_write_command(dev, 0x00); // Dummy byte
spi_master_write_command(dev, 0x00); // Define start page address
spi_master_write_command(dev, 0x07); // Frame frequency
spi_master_write_command(dev, 0x07); // Define end page address
spi_master_write_command(dev, 0x00); //
spi_master_write_command(dev, 0xFF); //
spi_master_write_command(dev, OLED_CMD_ACTIVE_SCROLL); // 2F
}
if (scroll == SCROLL_LEFT) {
spi_master_write_command(dev, OLED_CMD_HORIZONTAL_LEFT); // 27
spi_master_write_command(dev, 0x00); // Dummy byte
spi_master_write_command(dev, 0x00); // Define start page address
spi_master_write_command(dev, 0x07); // Frame frequency
spi_master_write_command(dev, 0x07); // Define end page address
spi_master_write_command(dev, 0x00); //
spi_master_write_command(dev, 0xFF); //
spi_master_write_command(dev, OLED_CMD_ACTIVE_SCROLL); // 2F
}
if (scroll == SCROLL_DOWN) {
spi_master_write_command(dev, OLED_CMD_CONTINUOUS_SCROLL); // 29
spi_master_write_command(dev, 0x00); // Dummy byte
spi_master_write_command(dev, 0x00); // Define start page address
spi_master_write_command(dev, 0x07); // Frame frequency
//spi_master_write_command(dev, 0x01); // Define end page address
spi_master_write_command(dev, 0x00); // Define end page address
spi_master_write_command(dev, 0x3F); // Vertical scrolling offset
spi_master_write_command(dev, OLED_CMD_VERTICAL); // A3
spi_master_write_command(dev, 0x00);
if (dev->_height == 64)
spi_master_write_command(dev, 0x40);
if (dev->_height == 32)
spi_master_write_command(dev, 0x20);
spi_master_write_command(dev, OLED_CMD_ACTIVE_SCROLL); // 2F
}
if (scroll == SCROLL_UP) {
spi_master_write_command(dev, OLED_CMD_CONTINUOUS_SCROLL); // 29
spi_master_write_command(dev, 0x00); // Dummy byte
spi_master_write_command(dev, 0x00); // Define start page address
spi_master_write_command(dev, 0x07); // Frame frequency
//spi_master_write_command(dev, 0x01); // Define end page address
spi_master_write_command(dev, 0x00); // Define end page address
spi_master_write_command(dev, 0x01); // Vertical scrolling offset
spi_master_write_command(dev, OLED_CMD_VERTICAL); // A3
spi_master_write_command(dev, 0x00);
if (dev->_height == 64)
spi_master_write_command(dev, 0x40);
if (dev->_height == 32)
spi_master_write_command(dev, 0x20);
spi_master_write_command(dev, OLED_CMD_ACTIVE_SCROLL); // 2F
}
if (scroll == SCROLL_STOP) {
spi_master_write_command(dev, OLED_CMD_DEACTIVE_SCROLL); // 2E
}
}