SHIELD-Malb41k1/OLED-I2C/OLEDI2C.h

979 lines
33 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#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