Удалить 'OLED-I2C/OLEDI2C.h'

This commit is contained in:
Максим Шумилов 2023-05-25 13:25:06 +00:00
parent ca1230d713
commit 36543140d0

View File

@ -1,979 +0,0 @@
#ifndef OLEDI2C_h
#define OLEDI2C_h
// ===== константы =====
#define SSD1306_128x32 0
#define SSD1306_128x64 1
#define SSH1106_128x64 2
#define OLED_I2C 0
#define OLED_SPI 1
#define OLED_NO_BUFFER 0
#define OLED_BUFFER 1
#define OLED_CLEAR 0
#define OLED_FILL 1
#define OLED_STROKE 2
#define BUF_ADD 0
#define BUF_SUBTRACT 1
#define BUF_REPLACE 2
#define BITMAP_NORMAL 0
#define BITMAP_INVERT 1
// ===========================================================================
#include "I2C.h" // лёгкая библиотека Wire (для atmega328)
#include "charMap.h"
#ifndef OLED_NO_PRINT
#include <Print.h>
#endif
// ============================ БЭКЭНД КОНСТАНТЫ ==============================
// внутренние константы
#define OLED_WIDTH 128
#define OLED_HEIGHT_32 0x02
#define OLED_HEIGHT_64 0x12
#define OLED_64 0x3F
#define OLED_32 0x1F
#define OLED_DISPLAY_OFF 0xAE
#define OLED_DISPLAY_ON 0xAF
#define OLED_COMMAND_MODE 0x00
#define OLED_ONE_COMMAND_MODE 0x80
#define OLED_DATA_MODE 0x40
#define OLED_ONE_DATA_MODE 0xC0
#define OLED_ADDRESSING_MODE 0x20
#define OLED_HORIZONTAL 0x00
#define OLED_VERTICAL 0x01
#define OLED_NORMAL_V 0xC8
#define OLED_FLIP_V 0xC0
#define OLED_NORMAL_H 0xA1
#define OLED_FLIP_H 0xA0
#define OLED_CONTRAST 0x81
#define OLED_SETCOMPINS 0xDA
#define OLED_SETVCOMDETECT 0xDB
#define OLED_CLOCKDIV 0xD5
#define OLED_SETMULTIPLEX 0xA8
#define OLED_COLUMNADDR 0x21
#define OLED_PAGEADDR 0x22
#define OLED_CHARGEPUMP 0x8D
#define OLED_NORMALDISPLAY 0xA6
#define OLED_INVERTDISPLAY 0xA7
#define BUFSIZE_128x64 (128 * 64 / 8)
#define BUFSIZE_128x32 (128 * 32 / 8)
#ifndef OLED_SPI_SPEED
#define OLED_SPI_SPEED 1000000ul
#endif
//static SPISettings OLED_SPI_SETT(OLED_SPI_SPEED, MSBFIRST, SPI_MODE0);
// список инициализации
static const uint8_t _oled_init[] PROGMEM = {
OLED_DISPLAY_OFF,
OLED_CLOCKDIV,
0x80, // value
OLED_CHARGEPUMP,
0x14, // value
OLED_ADDRESSING_MODE,
OLED_VERTICAL,
OLED_NORMAL_H,
OLED_NORMAL_V,
OLED_CONTRAST,
0x7F, // value
OLED_SETVCOMDETECT,
0x40, // value
OLED_NORMALDISPLAY,
OLED_DISPLAY_ON,
};
// ========================== КЛАСС КЛАСС КЛАСС =============================
template<int _TYPE, int _BUFF = OLED_BUFFER, int _CONN = OLED_I2C, int8_t _CS = -1, int8_t _DC = -1, int8_t _RST = -1 >
#ifndef OLED_NO_PRINT
class OLEDI2C : public Print {
#else
class OLEDI2C {
#endif
public:
// ========================== КОНСТРУКТОР =============================
OLEDI2C(uint8_t address = 0x3C)
: _address(address) {}
// ============================= СЕРВИС ===============================
// инициализация
void init(int __attribute__((unused)) sda = 0, int __attribute__((unused)) scl = 0) {
#if defined(ESP32) || defined(ESP8266)
if (sda || scl) Wire.begin(sda, scl);
else Wire.begin();
#else
Wire.begin();
#endif
beginCommand();
for (uint8_t i = 0; i < 15; i++) sendByte(pgm_read_byte(&_oled_init[i]));
endTransm();
beginCommand();
sendByte(OLED_SETCOMPINS);
sendByte(_TYPE ? OLED_HEIGHT_64 : OLED_HEIGHT_32);
sendByte(OLED_SETMULTIPLEX);
sendByte(_TYPE ? OLED_64 : OLED_32);
endTransm();
setCursorXY(0, 0);
if (_BUFF) setWindow(0, 0, _maxX, _maxRow); // для буфера включаем всю область
}
// очистить дисплей
void clear() {
fill(0);
}
// очистить область
void clear(int x0, int y0, int x1, int y1) {
if (_TYPE < 2) { // для SSD1306
if (_BUFF) rect(x0, y0, x1, y1, OLED_CLEAR);
else {
x1++;
y1++;
y0 >>= 3;
y1 = (y1 - 1) >> 3;
y0 = constrain(y0, 0, _maxRow);
y1 = constrain(y1, 0, _maxRow);
x0 = constrain(x0, 0, _maxX);
x1 = constrain(x1, 0, _maxX);
setWindow(x0, y0, x1, y1);
beginData();
for (int x = x0; x < x1; x++)
for (int y = y0; y < y1 + 1; y++)
writeData(0, y, x, 2);
endTransm();
}
} else {
// для SSH1108
}
setCursorXY(_x, _y);
}
// яркость 0-255
void setContrast(uint8_t value) {
sendCommand(OLED_CONTRAST, value);
}
// вкл/выкл
void setPower(bool mode) {
sendCommand(mode ? OLED_DISPLAY_ON : OLED_DISPLAY_OFF);
}
// отразить по горизонтали
void flipH(bool mode) {
sendCommand(mode ? OLED_FLIP_H : OLED_NORMAL_H);
}
// инвертировать дисплей
void invertDisplay(bool mode) {
sendCommand(mode ? OLED_INVERTDISPLAY : OLED_NORMALDISPLAY);
}
// отразить по вертикали
void flipV(bool mode) {
sendCommand(mode ? OLED_FLIP_V : OLED_NORMAL_V);
}
// ============================= ПЕЧАТЬ ==================================
virtual size_t write(uint8_t data) {
#ifndef OLED_NO_PRINT
// переносы и пределы
bool newPos = false;
if (data == '\r') {
_x = 0;
newPos = true;
data = 0;
} // получен возврат каретки
if (data == '\n') {
_y += _scaleY;
newPos = true;
data = 0;
_getn = 1;
} // получен перевод строки
if (_println && (_x + 6 * _scaleX) >= _maxX) {
_x = 0;
_y += _scaleY;
newPos = true;
} // строка переполненена, перевод и возврат
if (newPos) setCursorXY(_x, _y); // переставляем курсор
if (_y + _scaleY > _maxY + 1) data = 0; // дисплей переполнен
if (_getn && _println && data == ' ' && _x == 0) {
_getn = 0;
data = 0;
} // убираем первый пробел в строке
// фикс русских букв и некоторых символов
if (data > 127) {
uint8_t thisData = data;
// data = 0 - флаг на пропуск
if (data > 191) data = 0;
else if (_lastChar == 209 && data == 145) data = 192; // ё кастомная
else if (_lastChar == 208 && data == 129) data = 149; // Е вместо Ё
else if (_lastChar == 226 && data == 128) data = 0; // тире вместо длинного тире (начало)
else if (_lastChar == 128 && data == 148) data = 45; // тире вместо длинного тире
_lastChar = thisData;
}
if (data == 0) return 1;
// если тут не вылетели - печатаем символ
if (_TYPE < 2 || 1) { // для SSD1306
int newX = _x + _scaleX * 6;
if (newX < 0 || _x > _maxX) _x = newX; // пропускаем вывод "за экраном"
else {
if (!_BUFF) beginData();
for (uint8_t col = 0; col < 6; col++) { // 6 стобиков буквы
uint8_t bits = getFont(data, col); // получаем байт
if (_invState) bits = ~bits; // инверсия
if (_scaleX == 1) { // если масштаб 1
if (_x >= 0 && _x <= _maxX) { // внутри дисплея
if (_shift == 0) { // если вывод без сдвига на строку
writeData(bits, 0, 0, _mode); // выводим
} else { // со сдвигом
writeData(bits << _shift, 0, 0, _mode); // верхняя часть
writeData(bits >> (8 - _shift), 1, 0, _mode); // нижняя часть
}
}
} else { // масштаб 2, 3 или 4 - растягиваем шрифт
uint32_t newData = 0; // буфер
for (uint8_t i = 0, count = 0; i < 8; i++)
for (uint8_t j = 0; j < _scaleX; j++, count++)
bitWrite(newData, count, bitRead(bits, i)); // пакуем растянутый шрифт
for (uint8_t i = 0; i < _scaleX; i++) { // выводим. По Х
byte prevData = 0;
if (_x + i >= 0 && _x + i <= _maxX) // внутри дисплея
for (uint8_t j = 0; j < _scaleX; j++) { // выводим. По Y
byte data = newData >> (j * 8); // получаем кусок буфера
if (_shift == 0) { // если вывод без сдвига на строку
writeData(data, j, i, _mode); // выводим
} else { // со сдвигом
writeData((prevData >> (8 - _shift)) | (data << _shift), j, i, _mode); // склеиваем и выводим
prevData = data; // запоминаем предыдущий
}
}
if (_shift != 0) writeData(prevData >> (8 - _shift), _scaleX, i, _mode); // выводим нижний кусочек, если со сдвигом
}
}
_x += _scaleX; // двигаемся на ширину пикселя (1-4)
}
if (!_BUFF) endTransm();
}
} else {
// для SSH1106
}
#endif
return 1;
}
// автоматически переносить текст
void autoPrintln(bool mode) {
_println = mode;
}
// отправить курсор в 0,0
void home() {
setCursorXY(0, 0);
}
// поставить курсор для символа 0-127, 0-8(4)
void setCursor(int x, int y) {
setCursorXY(x, y << 3);
}
// поставить курсор для символа 0-127, 0-63(31)
void setCursorXY(int x, int y) {
_x = x;
_y = y;
setWindowShift(x, y, _maxX, _scaleY);
}
// масштаб шрифта (1-4)
void setScale(uint8_t scale) {
scale = constrain(scale, 1, 4); // защита от нечитающих доку
_scaleX = scale;
_scaleY = scale * 8;
setCursorXY(_x, _y);
}
// инвертировать текст (0-1)
void invertText(bool inv) {
_invState = inv;
}
void textMode(byte mode) {
_mode = mode;
}
// возвращает true, если дисплей "кончился" - при побуквенном выводе
bool isEnd() {
return (_y > _maxRow);
}
// ================================== ГРАФИКА ==================================
// точка (заливка 1/0)
void dot(int x, int y, byte fill = 1) {
if (x < 0 || x > _maxX || y < 0 || y > _maxY) return;
_x = 0;
_y = 0;
if (_BUFF) {
bitWrite(_oled_buffer[_bufIndex(x, y)], y & 0b111, fill);
} else {
if (!_buf_flag) {
setWindow(x, y >> 3, _maxX, _maxRow);
beginData();
sendByte(1 << (y & 0b111)); // задвигаем 1 на высоту y
endTransm();
} else {
x -= _bufX;
y -= _bufY;
if (x < 0 || x > _bufsizeX || y < 0 || y > (_bufsizeY << 3)) return;
bitWrite(_buf_ptr[(y >> 3) + x * _bufsizeY], y & 0b111, fill);
}
}
}
// линия
void line(int x0, int y0, int x1, int y1, byte fill = 1) {
_x = 0;
_y = 0;
if (x0 == x1) fastLineV(x0, y0, y1, fill);
else if (y0 == y1) fastLineH(y0, x0, x1, fill);
else {
int sx, sy, e2, err;
int dx = abs(x1 - x0);
int dy = abs(y1 - y0);
sx = (x0 < x1) ? 1 : -1;
sy = (y0 < y1) ? 1 : -1;
err = dx - dy;
for (;;) {
dot(x0, y0, fill);
if (x0 == x1 && y0 == y1) return;
e2 = err << 1;
if (e2 > -dy) {
err -= dy;
x0 += sx;
}
if (e2 < dx) {
err += dx;
y0 += sy;
}
}
}
}
// горизонтальная линия
void fastLineH(int y, int x0, int x1, byte fill = 1) {
_x = 0;
_y = 0;
if (x0 > x1) _swap(x0, x1);
if (y < 0 || y > _maxY) return;
if (x0 == x1) {
dot(x0, y, fill);
return;
}
x1++;
x0 = constrain(x0, 0, _maxX);
x1 = constrain(x1, 0, _maxX);
if (_BUFF) {
for (int x = x0; x < x1; x++) dot(x, y, fill);
} else {
if (_buf_flag) {
for (int x = x0; x < x1; x++) dot(x, y, fill);
return;
}
byte data = 0b1 << (y & 0b111);
y >>= 3;
setWindow(x0, y, x1, y);
beginData();
for (int x = x0; x < x1; x++) writeData(data, y, x);
endTransm();
}
}
// вертикальная линия
void fastLineV(int x, int y0, int y1, byte fill = 1) {
_x = 0;
_y = 0;
if (y0 > y1) _swap(y0, y1);
if (x < 0 || x > _maxX) return;
if (y0 == y1) {
dot(x, y0, fill);
return;
}
y1++;
if (_BUFF) {
for (int y = y0; y < y1; y++) dot(x, y, fill);
} else {
if (_buf_flag) {
for (int y = y0; y < y1; y++) dot(x, y, fill);
return;
}
if (fill) fill = 255;
byte shift = y0 & 0b111;
byte shift2 = 8 - (y1 & 0b111);
if (shift2 == 8) shift2 = 0;
int height = y1 - y0;
y0 >>= 3;
y1 = (y1 - 1) >> 3;
byte numBytes = y1 - y0;
setWindow(x, y0, x, y1);
beginData();
if (numBytes == 0) {
if (_inRange(y0, 0, _maxRow)) writeData((fill >> (8 - height)) << shift, y0, x);
} else {
if (_inRange(y0, 0, _maxRow)) writeData(fill << shift, y0, x); // начальный кусок
y0++;
for (byte i = 0; i < numBytes - 1; i++, y0++)
if (_inRange(y0, 0, _maxRow)) writeData(fill, y0, x); // столбик
if (_inRange(y0, 0, _maxRow)) writeData(fill >> shift2, y0, x); // нижний кусок
}
endTransm();
}
}
// прямоугольник (лев. верхн, прав. нижн)
void rect(int x0, int y0, int x1, int y1, byte fill = 1) {
_x = 0;
_y = 0;
if (x0 > x1) _swap(x0, x1);
if (y0 > y1) _swap(y0, y1);
if (fill == OLED_STROKE) {
fastLineH(y0, x0 + 1, x1 - 1);
fastLineH(y1, x0 + 1, x1 - 1);
fastLineV(x0, y0, y1);
fastLineV(x1, y0, y1);
} else {
if (x0 == x1 && y0 == y1) {
dot(x0, y0, fill);
return;
}
if (x0 == x1) {
fastLineV(x0, y0, y1);
return;
}
if (y0 == y1) {
fastLineH(y0, x0, x1);
return;
}
if (!_BUFF && fill == OLED_CLEAR) {
clear(x0, y0, x1, y1);
return;
}
// если рисуем в мини-буфер
if (_buf_flag) {
x0 = constrain(x0, 0, _maxX);
x1 = constrain(x1, 0, _maxX);
for (byte x = x0; x <= x1; x++) fastLineV(x, y0, y1, fill == OLED_FILL ? 1 : 0);
return;
}
byte thisFill = (fill == OLED_FILL ? 0 : 1);
// рисуем в олед и в большой буфер
x1++;
y1++;
byte shift = y0 & 0b111;
byte shift2 = 8 - (y1 & 0b111);
if (shift2 == 8) shift2 = 0;
int height = y1 - y0;
y0 >>= 3;
y1 = (y1 - 1) >> 3;
byte numBytes = y1 - y0;
x0 = constrain(x0, 0, _maxX);
x1 = constrain(x1, 0, _maxX);
if (!_BUFF) setWindow(x0, y0, x1, y1);
if (!_BUFF) beginData();
for (byte x = x0; x < x1; x++) {
int y = y0;
if (numBytes == 0) {
if (_inRange(y, 0, _maxRow)) writeData((255 >> (8 - height)) << shift, y, x, thisFill);
} else {
if (_inRange(y, 0, _maxRow)) writeData(255 << shift, y, x, thisFill); // начальный кусок
y++;
for (byte i = 0; i < numBytes - 1; i++, y++)
if (_inRange(y, 0, _maxRow)) writeData(255, y, x, thisFill); // столбик
if (_inRange(y, 0, _maxRow)) writeData(255 >> shift2, y, x, thisFill); // нижний кусок
}
}
if (!_BUFF) endTransm();
}
}
// прямоугольник скруглённый (лев. верхн, прав. нижн)
void roundRect(int x0, int y0, int x1, int y1, byte fill = OLED_FILL) {
if (fill == OLED_STROKE) {
fastLineV(x0, y0 + 2, y1 - 2);
fastLineV(x1, y0 + 2, y1 - 2);
fastLineH(y0, x0 + 2, x1 - 2);
fastLineH(y1, x0 + 2, x1 - 2);
dot(x0 + 1, y0 + 1);
dot(x1 - 1, y0 + 1);
dot(x1 - 1, y1 - 1);
dot(x0 + 1, y1 - 1);
} else {
fastLineV(x0, y0 + 2, y1 - 2, fill);
fastLineV(x0 + 1, y0 + 1, y1 - 1, fill);
fastLineV(x1 - 1, y0 + 1, y1 - 1, fill);
fastLineV(x1, y0 + 2, y1 - 2, fill);
rect(x0 + 2, y0, x1 - 2, y1, fill);
}
}
// окружность (центр х, центр у, радиус, заливка)
void circle(int x, int y, int radius, byte fill = OLED_FILL) {
//if (!_BUFF) createBuffer(x - radius, y - radius, x + radius + 1, y + radius);
int f = 1 - radius;
int ddF_x = 1;
int ddF_y = -2 * radius;
int x1 = 0;
int y1 = radius;
byte fillLine = (fill == OLED_CLEAR) ? 0 : 1;
dot(x, y + radius, fillLine);
dot(x, y - radius, fillLine);
dot(x + radius, y, fillLine);
dot(x - radius, y, fillLine);
//if (fill != OLED_STROKE) fastLineH(y, x - radius, x + radius-1, fillLine);
if (fill != OLED_STROKE) fastLineV(x, y - radius, y + radius - 1, fillLine);
while (x1 < y1) {
if (f >= 0) {
y1--;
ddF_y += 2;
f += ddF_y;
}
x1++;
ddF_x += 2;
f += ddF_x;
if (fill == OLED_STROKE) {
dot(x + x1, y + y1);
dot(x - x1, y + y1);
dot(x + x1, y - y1);
dot(x - x1, y - y1);
dot(x + y1, y + x1);
dot(x - y1, y + x1);
dot(x + y1, y - x1);
dot(x - y1, y - x1);
} else { // FILL / CLEAR
fastLineV(x + x1, y - y1, y + y1, fillLine);
fastLineV(x - x1, y - y1, y + y1, fillLine);
fastLineV(x + y1, y - x1, y + x1, fillLine);
fastLineV(x - y1, y - x1, y + x1, fillLine);
/*
fastLineH(y + y1, x - x1, x + x1-1, fillLine);
fastLineH(y - y1, x - x1, x + x1-1, fillLine);
fastLineH(y + x1, x - y1, x + y1-1, fillLine);
fastLineH(y - x1, x - y1, x + y1-1, fillLine);
*/
}
}
//if (!_BUFF) sendBuffer();
}
void bezier(int* arr, uint8_t size, uint8_t dense, uint8_t fill = 1) {
int a[size * 2];
for (int i = 0; i < (1 << dense); i++) {
for (int j = 0; j < size * 2; j++) a[j] = arr[j];
for (int j = (size - 1) * 2 - 1; j > 0; j -= 2)
for (int k = 0; k <= j; k++)
a[k] = a[k] + (((a[k + 2] - a[k]) * i) >> dense);
dot(a[0], a[1], fill);
}
}
// вывести битмап
void drawBitmap(int x, int y, const uint8_t* frame, int width, int height, uint8_t invert = 0, byte mode = 0) {
_x = 0;
_y = 0;
if (invert) invert = 255;
byte left = height & 0b111;
if (left != 0) height += (8 - left); // округляем до ближайшего кратного степени 2
int shiftY = (y >> 3) + (height >> 3); // строка (row) крайнего байта битмапа
setWindowShift(x, y, width, height); // выделяем окно
bool bottom = (_shift != 0 && shiftY >= 0 && shiftY <= _maxRow); // рисовать ли нижний сдвинутый байт
if (!_BUFF) beginData();
for (int X = x, countX = 0; X < x + width; X++, countX++) { // в пикселях
byte prevData = 0;
if (_inRange(X, 0, _maxX)) { // мы внутри дисплея по X
for (int Y = y >> 3, countY = 0; Y < shiftY; Y++, countY++) { // в строках (пикс/8)
byte data = pgm_read_word(&(frame[countY * width + countX])) ^ invert; // достаём байт
if (_shift == 0) { // без сдвига по Y
if (_inRange(Y, 0, _maxRow)) writeData(data, Y, X, mode); // мы внутри дисплея по Y
} else { // со сдвигом по Y
if (_inRange(Y, 0, _maxRow)) writeData((prevData >> (8 - _shift)) | (data << _shift), Y, X, mode); // задвигаем
prevData = data;
}
}
if (bottom) writeData(prevData >> (8 - _shift), shiftY, X, mode); // нижний байт
}
}
if (!_BUFF) endTransm();
}
// залить весь дисплей указанным байтом
void fill(uint8_t data) {
if (_BUFF) memset(_oled_buffer, data, _bufSize);
else {
if (_TYPE < 2 || 1) { // для SSD1306
setWindow(0, 0, _maxX, _maxRow);
beginData();
for (int i = 0; i < (_TYPE ? 1024 : 512); i++) sendByte(data);
endTransm();
} else { // для SSH1108
}
}
setCursorXY(_x, _y);
}
// шлёт байт в "столбик" setCursor() и setCursorXY()
void drawByte(uint8_t data) {
if (++_x > _maxX) return;
if (_TYPE < 2 || 1) { // для SSD1306
if (!_BUFF) beginData();
if (_shift == 0) { // если вывод без сдвига на строку
writeData(data); // выводим
} else { // со сдвигом
writeData(data << _shift); // верхняя часть
writeData(data >> (8 - _shift), 1); // нижняя часть со сдвигом на 1
}
if (!_BUFF) endTransm();
} else {
// для SSH1106
/*
int h = y & 0x07;
if (_BUFF) {
for (int p = 0; p < 2; p++) {
Wire.beginTransmission(_address);
continueCmd(0xB0 + (y >> 3) + p); // Page
continueCmd(0x00 + ((x + 2) & 0x0F)); // Column low nibble
continueCmd(0x10 + ((x + 2) >> 4)); // Column high nibble
continueCmd(0xE0); // Read modify write
Wire.write(OLED_ONE_DATA_MODE);
Wire.endTransmission();
Wire.requestFrom((int)_address, 2);
Wire.read(); // Dummy read
int j = Wire.read();
Wire.beginTransmission(_address);
Wire.write(OLED_ONE_DATA_MODE);
Wire.write((data << h) >> (p << 3) | j);
continueCmd(0xEE); // Cancel read modify write
Wire.endTransmission();
}
} else {
for (int p = 0; p < 2; p++) {
Wire.beginTransmission(_address);
continueCmd(0xB0 + (y >> 3) + p); // Page
continueCmd(0x00 + ((x + 2) & 0x0F)); // Column low nibble
continueCmd(0x10 + ((x + 2) >> 4)); // Column high nibble
Wire.write(OLED_ONE_DATA_MODE);
Wire.write((data << h) >> (p << 3));
Wire.endTransmission();
}
}*/
}
}
// вывести одномерный байтовый массив (линейный битмап высотой 8)
void drawBytes(uint8_t* data, byte size) {
if (!_BUFF) beginData();
for (byte i = 0; i < size; i++) {
if (++_x > _maxX) return;
if (_shift == 0) { // если вывод без сдвига на строку
writeData(data[i]); // выводим
} else { // со сдвигом
writeData(data[i] << _shift); // верхняя часть
writeData(data[i] >> (8 - _shift), 1); // нижняя часть со сдвигом на 1
}
}
if (!_BUFF) endTransm();
}
// ================================== СИСТЕМНОЕ ===================================
// полностью обновить дисплей из буфера
void update() {
if (_BUFF) {
if (_TYPE < 2) { // для 1306
setWindow(0, 0, _maxX, _maxRow);
beginData();
for (int i = 0; i < (_TYPE ? 1024 : 512); i++) sendByte(_oled_buffer[i]);
endTransm();
} else { // для 1106
sendCommand(0x00);
sendCommand(0x10);
sendCommand(0x40);
uint16_t ptr = 0;
for (uint8_t i = 0; i < (64 >> 3); i++) {
sendCommand(0xB0 + i + 0); //set page address
sendCommand(2 & 0xf); //set lower column address
sendCommand(0x10); //set higher column address
for (uint8_t a = 0; a < 8; a++) {
beginData();
for (uint8_t b = 0; b < (OLED_WIDTH >> 3); b++) {
sendByteRaw(_oled_buffer[((ptr & 0x7F) << 3) + (ptr >> 7)]);
ptr++;
}
endTransm();
}
}
}
}
}
// выборочно обновить дисплей из буфера (x0, y0, x1, y1)
void update(int x0, int y0, int x1, int y1) {
if (_BUFF) {
y0 >>= 3;
y1 >>= 3;
setWindow(x0, y0, x1, y1);
beginData();
for (int x = x0; x < x1; x++)
for (int y = y0; y < y1 + 1; y++)
if (x >= 0 && x <= _maxX && y >= 0 && y <= _maxRow)
sendByte(_oled_buffer[y + x * (_TYPE ? 8 : 4)]);
endTransm();
}
}
// отправить байт по i2с или в буфер
void writeData(byte data, byte offsetY = 0, byte offsetX = 0, int mode = 0) {
if (_BUFF) {
switch (mode) {
case 0:
_oled_buffer[_bufIndex(_x + offsetX, _y) + offsetY] |= data; // добавить
break;
case 1:
_oled_buffer[_bufIndex(_x + offsetX, _y) + offsetY] &= ~data; // вычесть
break;
case 2:
_oled_buffer[_bufIndex(_x + offsetX, _y) + offsetY] = data; // заменить
break;
}
} else {
if (_buf_flag) {
int x = _x - _bufX;
int y = _y - _bufY;
if (x < 0 || x > _bufsizeX || y < 0 || y > (_bufsizeY << 3)) return;
switch (mode) {
case 0:
_buf_ptr[(y >> 3) + x * _bufsizeY] |= data; // добавить
break;
case 1:
_buf_ptr[(y >> 3) + x * _bufsizeY] &= ~data; // вычесть
break;
case 2:
_buf_ptr[(y >> 3) + x * _bufsizeY] = data; // заменить
break;
}
} else {
sendByte(data);
}
}
}
// окно со сдвигом. x 0-127, y 0-63 (31), ширина в пикселях, высота в пикселях
void setWindowShift(int x0, int y0, int sizeX, int sizeY) {
_shift = y0 & 0b111;
if (!_BUFF) setWindow(x0, (y0 >> 3), x0 + sizeX, (y0 + sizeY - 1) >> 3);
}
// ================== ДИНАМИЧЕСКИЙ БУФЕР ================
// создать
bool createBuffer(int x0, int y0, int x1, int y1, byte fill = 0) {
if (!_BUFF) {
_bufX = x0;
_bufY = y0;
_bufsizeX = x1 - x0 + 1;
_bufsizeY = ((y1 - y0) >> 3) + 1;
int bufSize = _bufsizeX * _bufsizeY;
_buf_ptr = (byte*)malloc(bufSize);
if (_buf_ptr != NULL) {
_buf_flag = true;
memset(_buf_ptr, fill, bufSize);
return true;
} else {
_buf_flag = false;
return false;
}
}
}
// отправить
void sendBuffer() {
if (!_BUFF) {
if (_buf_flag) {
setWindow(_bufX, _bufY >> 3, _bufX + _bufsizeX, (_bufY >> 3) + _bufsizeY - 1);
beginData();
for (int i = 0; i < _bufsizeX * _bufsizeY; i++) {
sendByte(_buf_ptr[i]);
}
endTransm();
_buf_flag = false;
free(_buf_ptr);
}
}
}
// ========= ЛОУ-ЛЕВЕЛ ОТПРАВКА =========
// супер-костыль для либы Wire. Привет индусам!
void sendByte(uint8_t data) {
sendByteRaw(data);
#if !defined(microWire_h)
if (!_CONN) {
_writes++;
if (_writes >= 16) {
endTransm();
beginData();
}
}
#endif
}
void sendByteRaw(uint8_t data) {
Wire.write(data);
}
// отправить команду
void sendCommand(uint8_t cmd1) {
beginOneCommand();
sendByteRaw(cmd1);
endTransm();
}
// отправить код команды и команду
void sendCommand(uint8_t cmd1, uint8_t cmd2) {
beginCommand();
sendByteRaw(cmd1);
sendByteRaw(cmd2);
endTransm();
}
// выбрать "окно" дисплея
void setWindow(int x0, int y0, int x1, int y1) {
beginCommand();
sendByteRaw(OLED_COLUMNADDR);
sendByteRaw(constrain(x0, 0, _maxX));
sendByteRaw(constrain(x1, 0, _maxX));
sendByteRaw(OLED_PAGEADDR);
sendByteRaw(constrain(y0, 0, _maxRow));
sendByteRaw(constrain(y1, 0, _maxRow));
endTransm();
}
void beginData() {
startTransm();
if (_CONN) fastWrite(_DC, 1);
else sendByteRaw(OLED_DATA_MODE);
}
void beginCommand() {
startTransm();
if (_CONN) fastWrite(_DC, 0);
else sendByteRaw(OLED_COMMAND_MODE);
}
void beginOneCommand() {
startTransm();
if (_CONN) fastWrite(_DC, 0);
else sendByteRaw(OLED_ONE_COMMAND_MODE);
}
void endTransm() {
Wire.endTransmission();
_writes = 0;
}
void startTransm() {
Wire.beginTransmission(_address);
}
// получить "столбик-байт" буквы
uint8_t getFont(uint8_t font, uint8_t row) {
#ifndef OLED_NO_PRINT
if (row > 4) return 0;
font = font - '0' + 16; // перевод код символа из таблицы ASCII
if (font <= 95) {
return pgm_read_byte(&(_charMap[font][row])); // для английских букв и символов
} else if (font >= 96 && font <= 111) { // и пизд*ц для русских
return pgm_read_byte(&(_charMap[font + 47][row]));
} else if (font <= 159) {
return pgm_read_byte(&(_charMap[font - 17][row]));
} else {
return pgm_read_byte(&(_charMap[font - 1][row])); // для кастомных (ё)
}
#endif
}
// ==================== ПЕРЕМЕННЫЕ И КОНСТАНТЫ ====================
const uint8_t _address = 0x3C;
const uint8_t _maxRow = (_TYPE ? 8 : 4) - 1;
const uint8_t _maxY = (_TYPE ? 64 : 32) - 1;
const uint8_t _maxX = OLED_WIDTH - 1; // на случай добавления мелких дисплеев
// софт. буфер
const int _bufSize = ((_BUFF == 1) ? (_TYPE ? BUFSIZE_128x64 : BUFSIZE_128x32) : 0);
uint8_t _oled_buffer[((_BUFF == 1) ? (_TYPE ? BUFSIZE_128x64 : BUFSIZE_128x32) : 0)];
private:
// всякое
void fastWrite(const uint8_t pin, bool val) {
#if defined(__AVR_ATmega328P__) || defined(__AVR_ATmega168__)
if (pin < 8) bitWrite(PORTD, pin, val);
else if (pin < 14) bitWrite(PORTB, (pin - 8), val);
else if (pin < 20) bitWrite(PORTC, (pin - 14), val);
#elif defined(__AVR_ATtiny85__) || defined(__AVR_ATtiny13__)
bitWrite(PORTB, pin, val);
#else
digitalWrite(pin, val);
#endif
}
// индекс в буфере
int _bufIndex(int x, int y) {
return ((y) >> 3) + ((x) << (_TYPE ? 3 : 2)); // _y / 8 + _x * (4 или 8)
}
void _swap(int& x, int& y) {
int z = x;
x = y;
y = z;
}
bool _inRange(int x, int mi, int ma) {
return x >= mi && x <= ma;
}
bool _invState = 0;
bool _println = false;
bool _getn = false;
uint8_t _scaleX = 1, _scaleY = 8;
int _x = 0, _y = 0;
uint8_t _shift = 0;
uint8_t _lastChar;
uint8_t _writes = 0;
uint8_t _mode = 2;
// дин. буфер
int _bufsizeX, _bufsizeY;
int _bufX, _bufY;
uint8_t* _buf_ptr;
bool _buf_flag = false;
};
#endif