mirror of
https://github.com/skot/ESP-Miner.git
synced 2025-03-18 13:52:07 +01:00
314 lines
9.0 KiB
C
314 lines
9.0 KiB
C
// OLED SSD1306 using the I2C interface
|
|
// Written by Larry Bank (bitbank@pobox.com)
|
|
// Project started 1/15/2017
|
|
//
|
|
// The I2C writes (through a file handle) can be single or multiple bytes.
|
|
// The write mode stays in effect throughout each call to write()
|
|
// To write commands to the OLED controller, start a byte sequence with 0x00,
|
|
// to write data, start a byte sequence with 0x40,
|
|
// The OLED controller is set to "page mode". This divides the display
|
|
// into 8 128x8 "pages" or strips. Each data write advances the output
|
|
// automatically to the next address. The bytes are arranged such that the LSB
|
|
// is the topmost pixel and the MSB is the bottom.
|
|
// The font data comes from another source and must be rotated 90 degrees
|
|
// (at init time) to match the orientation of the bits on the display memory.
|
|
// A copy of the display memory is maintained by this code so that single pixel
|
|
// writes can occur without having to read from the display controller.
|
|
|
|
#include <stdbool.h>
|
|
#include <stdint.h>
|
|
#include <string.h>
|
|
#include "esp_err.h"
|
|
#include "esp_log.h"
|
|
|
|
#include "nvs_config.h"
|
|
#include "i2c_bitaxe.h"
|
|
#include "oled.h"
|
|
|
|
#define OLED_I2C_ADDR 0x3C
|
|
|
|
extern unsigned char ucSmallFont[];
|
|
static int iScreenOffset; // current write offset of screen data
|
|
static unsigned char ucScreen[1024]; // local copy of the image buffer
|
|
static int oled_type, oled_flip;
|
|
|
|
static void write_command(unsigned char);
|
|
static esp_err_t write(uint8_t *, uint8_t);
|
|
|
|
static bool oled_active;
|
|
|
|
static i2c_master_dev_handle_t ssd1306_dev_handle;
|
|
|
|
// Initialialize the OLED Screen
|
|
esp_err_t OLED_init(void)
|
|
{
|
|
|
|
//init the I2C device
|
|
if (i2c_bitaxe_add_device(OLED_I2C_ADDR, &ssd1306_dev_handle) != ESP_OK) {
|
|
return ESP_FAIL;
|
|
}
|
|
|
|
uint8_t oled32_initbuf[] = {0x00,
|
|
0xae, // cmd: display off
|
|
0xd5, // cmd: set display clock
|
|
0x80,
|
|
0xa8, // cmd: set multiplex ratio
|
|
0x1f, // HEIGHT - 1 -> 31
|
|
0xd3, // cmd: set display offset
|
|
0x00,
|
|
0x40, // cmd: Set Display Start Line
|
|
0x8d,
|
|
0x14, // cmd: Set Higher Column Start Address for Page Addressing Mode
|
|
0xa1,
|
|
0xc8, // cmd: Set COM Output Scan Direction C0/C8
|
|
0xda, // cmd: Set COM Pins Hardware Configuration
|
|
0x02, //
|
|
0x81, // cmd: Set Contrast control
|
|
0x7f,
|
|
0xd9, // cmd: Set Pre-Charge Period
|
|
0xf1,
|
|
0xdb, // comd: Vcom regulator output
|
|
0x40,
|
|
0xa4, // cmd: display on ram contents
|
|
0xa6, // cmd: set normal
|
|
0xaf}; // cmd: display on
|
|
uint8_t uc[4];
|
|
|
|
uint8_t bFlip = nvs_config_get_u16(NVS_CONFIG_FLIP_SCREEN, 1);
|
|
uint8_t bInvert = nvs_config_get_u16(NVS_CONFIG_INVERT_SCREEN, 0);
|
|
oled_active = false;
|
|
|
|
// //enable the module
|
|
// GPIO_write(Board_OLED_DISP_ENABLE, 0);
|
|
// DELAY_MS(50);
|
|
// GPIO_write(Board_OLED_DISP_ENABLE, 1);
|
|
// DELAY_MS(50);
|
|
|
|
oled_type = OLED_128x32;
|
|
oled_flip = bFlip;
|
|
|
|
// /* Call driver init functions */
|
|
// I2C_init();
|
|
|
|
// /* Create I2C for usage */
|
|
// I2C_Params_init(&oled_i2cParams);
|
|
// oled_i2cParams.bitRate = I2C_100kHz;
|
|
// oled_i2c = I2C_open(Board_I2C_SSD1306, &oled_i2cParams);
|
|
|
|
// if (oled_i2c == NULL) {
|
|
// return false;
|
|
// }
|
|
oled_active = true;
|
|
|
|
write(oled32_initbuf, sizeof(oled32_initbuf));
|
|
|
|
if (bInvert) {
|
|
uc[0] = 0; // command
|
|
uc[1] = 0xa7; // invert command
|
|
write(uc, 2);
|
|
}
|
|
|
|
if (bFlip) { // rotate display 180
|
|
uc[0] = 0; // command
|
|
uc[1] = 0xa0;
|
|
write(uc, 2);
|
|
uc[1] = 0xc0;
|
|
write(uc, 2);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
// Sends a command to turn off the OLED display
|
|
// Closes the I2C file handle
|
|
void OLED_shutdown()
|
|
{
|
|
|
|
write_command(0xaE); // turn off OLED
|
|
// I2C_close(oled_i2c);
|
|
// GPIO_write(Board_OLED_DISP_ENABLE, 0); //turn off power
|
|
oled_active = false;
|
|
}
|
|
|
|
// Send a single byte command to the OLED controller
|
|
static void write_command(uint8_t c)
|
|
{
|
|
uint8_t buf[2];
|
|
|
|
buf[0] = 0x00; // command introducer
|
|
buf[1] = c;
|
|
write(buf, 2);
|
|
}
|
|
|
|
static void oledWriteCommand2(uint8_t c, uint8_t d)
|
|
{
|
|
uint8_t buf[3];
|
|
|
|
buf[0] = 0x00;
|
|
buf[1] = c;
|
|
buf[2] = d;
|
|
write(buf, 3);
|
|
}
|
|
|
|
bool OLED_setContrast(uint8_t ucContrast)
|
|
{
|
|
|
|
oledWriteCommand2(0x81, ucContrast);
|
|
return true;
|
|
}
|
|
|
|
// Send commands to position the "cursor" to the given
|
|
// row and column
|
|
static void oledSetPosition(int x, int y)
|
|
{
|
|
iScreenOffset = (y * 128) + x;
|
|
if (oled_type == OLED_64x32) // visible display starts at column 32, row 4
|
|
{
|
|
x += 32; // display is centered in VRAM, so this is always true
|
|
if (oled_flip == 0) // non-flipped display starts from line 4
|
|
y += 4;
|
|
} else if (oled_type == OLED_132x64) // SH1106 has 128 pixels centered in 132
|
|
{
|
|
x += 2;
|
|
}
|
|
|
|
write_command(0xb0 | y); // go to page Y
|
|
write_command(0x00 | (x & 0x0f)); // lower col addr
|
|
write_command(0x10 | ((x >> 4) & 0x0f));// upper col addr
|
|
}
|
|
|
|
// Write a block of pixel data to the OLED
|
|
// Length can be anything from 1 to 1024 (whole display)
|
|
static void oledWriteDataBlock(const uint8_t * ucBuf, int iLen)
|
|
{
|
|
uint8_t ucTemp[129];
|
|
|
|
ucTemp[0] = 0x40; // data command
|
|
memcpy(&ucTemp[1], ucBuf, iLen);
|
|
write(ucTemp, iLen + 1);
|
|
// Keep a copy in local buffer
|
|
memcpy(&ucScreen[iScreenOffset], ucBuf, iLen);
|
|
iScreenOffset += iLen;
|
|
}
|
|
|
|
// Set (or clear) an individual pixel
|
|
// The local copy of the frame buffer is used to avoid
|
|
// reading data from the display controller
|
|
int OLED_setPixel(int x, int y, uint8_t ucColor)
|
|
{
|
|
int i;
|
|
uint8_t uc, ucOld;
|
|
|
|
// if (oled_i2c == NULL)
|
|
// return -1;
|
|
|
|
i = ((y >> 3) * 128) + x;
|
|
if (i < 0 || i > 1023) // off the screen
|
|
return -1;
|
|
uc = ucOld = ucScreen[i];
|
|
uc &= ~(0x1 << (y & 7));
|
|
if (ucColor) {
|
|
uc |= (0x1 << (y & 7));
|
|
}
|
|
if (uc != ucOld) // pixel changed
|
|
{
|
|
oledSetPosition(x, y >> 3);
|
|
oledWriteDataBlock(&uc, 1);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
// Write a bitmap to the screen
|
|
// The X position is in character widths (8 or 16)
|
|
// The Y position is in memory pages (8 lines each)
|
|
// Bitmap should be aligned vertical, 1 bit per pixel
|
|
// Width and Height must be per 8 pixels
|
|
// Conversion tool: https://javl.github.io/image2cpp/
|
|
int OLED_showBitmap(int x, int y, const uint8_t *bitmap, int width, int height)
|
|
{
|
|
for (int row = 0; row < (height >> 3); row++) {
|
|
oledSetPosition(x, y + row);
|
|
oledWriteDataBlock(&bitmap[row * width], width);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
//
|
|
// Draw a string of small (8x8), large (16x24), or very small (6x8) characters
|
|
// At the given col+row
|
|
// The X position is in character widths (8 or 16)
|
|
// The Y position is in memory pages (8 lines each)
|
|
//
|
|
int OLED_writeString(int x, int y, const char * szMsg)
|
|
{
|
|
int i, iLen;
|
|
uint8_t * s;
|
|
|
|
// if (oled_i2c == NULL) return -1; // not initialized
|
|
|
|
iLen = strlen(szMsg);
|
|
|
|
oledSetPosition(x * 6, y);
|
|
if (iLen + x > 21)
|
|
iLen = 21 - x;
|
|
if (iLen < 0)
|
|
return -1;
|
|
for (i = 0; i < iLen; i++) {
|
|
s = &ucSmallFont[(unsigned char) szMsg[i] * 6];
|
|
oledWriteDataBlock(s, 6);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
// Fill the frame buffer with a byte pattern
|
|
// e.g. all off (0x00) or all on (0xff)
|
|
int OLED_fill(uint8_t ucData)
|
|
{
|
|
int y;
|
|
uint8_t temp[128];
|
|
int iLines, iCols;
|
|
|
|
// if (oled_i2c == NULL) return -1; // not initialized
|
|
|
|
iLines = (oled_type == OLED_128x32 || oled_type == OLED_64x32) ? 4 : 8;
|
|
iCols = (oled_type == OLED_64x32) ? 4 : 8;
|
|
|
|
memset(temp, ucData, 128);
|
|
for (y = 0; y < iLines; y++) {
|
|
oledSetPosition(0, y); // set to (0,Y)
|
|
oledWriteDataBlock(temp, iCols * 16); // fill with data byte
|
|
} // for y
|
|
return 0;
|
|
}
|
|
|
|
int OLED_clearLine(uint8_t line)
|
|
{
|
|
uint8_t temp[128];
|
|
|
|
// if (oled_i2c == NULL) return -1; // not initialized
|
|
if (line > 4)
|
|
return -1; // line number too big
|
|
|
|
memset(temp, 0, 128);
|
|
oledSetPosition(0, line); // set to (0,Y)
|
|
oledWriteDataBlock(temp, 128); // fill with data byte
|
|
|
|
return 0;
|
|
}
|
|
|
|
bool OLED_status(void)
|
|
{
|
|
return oled_active;
|
|
}
|
|
|
|
/**
|
|
* @brief Write a byte to a I2C register
|
|
*/
|
|
static esp_err_t write(uint8_t * data, uint8_t len)
|
|
{
|
|
//return i2c_master_write_to_device(I2C_MASTER_NUM, 0x3C, data, len, I2C_MASTER_TIMEOUT_MS / portTICK_PERIOD_MS);
|
|
|
|
return i2c_bitaxe_register_write_bytes(ssd1306_dev_handle, data, len);
|
|
}
|