diff --git a/OLED-I2C/I2C.h b/OLED-I2C/I2C.h new file mode 100644 index 0000000..8d66a16 --- /dev/null +++ b/OLED-I2C/I2C.h @@ -0,0 +1,189 @@ +#ifndef I2C_h +#define I2C_h + +// этот символ необходим для совместимости с Wire.h. его значение является максимумом для параметра uint8_t используемого в оригинальной библиотеке. +#define BUFFER_LENGTH 255 + +class I2CLIB { +public: + void begin(void); // инициализация шины + void setClock(uint32_t clock); // ручная установка частоты шины 31-900 kHz (в герцах) + void beginTransmission(uint8_t address); // открыть соединение (для записи данных) + uint8_t endTransmission(bool stop); // закрыть соединение , произвести stop или restart (по умолчанию - stop) + uint8_t endTransmission(void); // закрыть соединение , произвести stop + size_t write(uint8_t data); // отправить в шину байт данных , отправка производится сразу , формат - byte "unsigned char" + uint8_t requestFrom(uint8_t address , uint8_t length , bool stop); //открыть соединение и запросить данные от устройства, отпустить или удержать шину + uint8_t requestFrom(uint8_t address , uint8_t length); //открыть соединение и запросить данные от устройства, отпустить шину + uint8_t read(void); // прочитать байт , БУФЕРА НЕТ!!! , читайте сразу все запрошенные байты , stop или restart после чтения последнего байта, настраивается в requestFrom + uint8_t available(void); // вернет количество оставшихся для чтения байт + // функции добавлены для совместимости с Wire API + inline void beginTransmission(int address){beginTransmission((uint8_t)address);} + uint8_t requestFrom(uint8_t address, uint8_t quantity, uint8_t sendStop); + uint8_t requestFrom(uint8_t address, uint8_t quantity, uint32_t iaddress, uint8_t isize, uint8_t sendStop); + uint8_t requestFrom(int address, int quantity); + uint8_t requestFrom(int address, int quantity, int sendStop); + size_t write(const uint8_t *buffer, size_t size); + inline size_t write(unsigned long n) { return write((uint8_t)n); } + inline size_t write(long n) { return write((uint8_t)n); } + inline size_t write(unsigned int n) { return write((uint8_t)n); } + inline size_t write(int n) { return write((uint8_t)n); } + +private: + uint8_t _requested_bytes = 0; // переменная хранит количество запрошенных и непрочитанных байт + bool _address_nack = false; // Флаг для отслеживания ошибки при передаче адреса + bool _data_nack = false; // Флаг для отслеживания ошибки при передаче данных + bool _stop_after_request = true; // stop или restart после чтения последнего байта + void start(void); // сервисная функция с нее начинается любая работа с шиной + void stop(void); // сервисная функция ей заканчивается работа с шиной +}; +extern I2CLIB Wire; + +void setPinMode(uint8_t pin, uint8_t mode) { + if (mode == INPUT) { + DDRD &= ~(1 << pin); // устанавливаем вход + } else { + DDRD |= (1 << pin); // устанавливаем выход + } +} + +void I2CLIB::begin() +{ // Инициализация шины в роли master + setPinMode(SDA, INPUT_PULLUP); // Подтяжка шины + setPinMode(SCL, INPUT_PULLUP); // Подтяжка шины + TWBR = 72; // Стандартная скорость - 100kHz + TWSR = 0; // Делитель - /1 , статус - 0; +} + +void I2CLIB::setClock(uint32_t clock) +{ // Функция установки частоты шины 31-900 kHz (в герцах) + TWBR = (((long)F_CPU / clock) - 16) / 2; // Расчет baudrate - регистра +} + +void I2CLIB::beginTransmission(uint8_t address) +{ // Начать передачу (для записи данных) + I2CLIB::start(); // Старт + I2CLIB::write(address << 1); // Отправка slave - устройству адреса с битом "write" +} + +uint8_t I2CLIB::endTransmission(void) +{ // Завершить передачу и отпустить шину + return I2CLIB::endTransmission(true); +} + +uint8_t I2CLIB::endTransmission(bool stop) +{ // Завершить передачу (после записи данных) + if (stop) I2CLIB::stop(); // Если задано stop или аргумент пуст - отпустить шину + else I2CLIB::start(); // Иначе - restart (другой master на шине не сможет влезть между сообщениями) + if (_address_nack) { // Если нет ответа при передаче адреса + _address_nack = false; // Обнуляем оба флага + _data_nack = false; // Обнуляем оба флага + return 2; // Возвращаем '2' + } if (_data_nack) { // Если нет ответа при передаче данных + _address_nack = false; // Обнуляем оба флага + _data_nack = false; // Обнуляем оба флага + return 3; // Возвращаем '2' + } return 0; // Если все ОК - возвращаем '0' +} + +size_t I2CLIB::write(uint8_t data) +{ // Прямая отправка байта на шину + TWDR = data; // Записать данные в data - регистр + TWCR = _BV(TWEN) | _BV(TWINT); // Запустить передачу + while (!(TWCR & _BV(TWINT))); // Дождаться окончания + uint8_t _bus_status = TWSR & 0xF8; // Чтение статуса шины + if(_bus_status == 0x20) _address_nack = true; // SLA + W + NACK ? - нет ответа при передаче адреса + if(_bus_status == 0x30) _data_nack = true; // BYTE + NACK ? - нет ответа при передаче данных + return 1; // для совместимости с Wire API +} + +uint8_t I2CLIB::available() +{ // Вернуть оставшееся количество запрошенных для чтения байт + return _requested_bytes; // Это содержимое этой переменной +} + +uint8_t I2CLIB::read() +{ // Прямое чтение байта из шины после запроса + if (--_requested_bytes) { // Если байт не последний + TWCR = _BV(TWEN) | _BV(TWINT) | _BV(TWEA); // Запустить чтение шины (с подтверждением "ACK") + while (!(TWCR & _BV(TWINT))); // Дождаться окончания приема данных + return TWDR; // Вернуть принятые данные , это содержимое data - регистра + } + _requested_bytes = 0; // Если читаем последний байт + TWCR = _BV(TWEN) | _BV(TWINT); // Запустить чтение шины (БЕЗ подтверждения "NACK") + while (!(TWCR & _BV(TWINT))); // Дождаться окончания приема данных + if (_stop_after_request) I2CLIB::stop(); // Если в requestFrom не задан аргумент stop , или stop задан как true - отпустить шину + else I2CLIB::start(); // Иначе - restart (другой master на шине не сможет влезть между сообщениями) + return TWDR; // Вернуть принятый ранее байт из data - регистра +} + +uint8_t I2CLIB::requestFrom(uint8_t address , uint8_t length) +{ // Запрос n-го кол-ва байт от ведомого устройства и отпускание шины + return I2CLIB::requestFrom(address , length , true); +} + +uint8_t I2CLIB::requestFrom(uint8_t address , uint8_t length , bool stop) +{ // Запрос n-го кол-ва байт от ведомого устройства (Читайте все байты сразу!!!) + _stop_after_request = stop; // stop или restart после чтения последнего байта + _requested_bytes = length; // Записать в переменную количество запрошенных байт + I2CLIB::start(); // Начать работу на шине + I2CLIB::write((address << 1) | 0x1); // Отправить устройству адрес + бит "read" + return length; // вернуть длину сообщения для совместимости с оригинальной Wire API +} + +uint8_t I2CLIB::requestFrom(uint8_t address, uint8_t quantity, uint8_t sendStop){ + return requestFrom(address , quantity , sendStop!=0); +} + +uint8_t I2CLIB::requestFrom(uint8_t address, uint8_t quantity, uint32_t iaddress, uint8_t isize, uint8_t sendStop){ + if (isize > 0) { + // send internal address; this mode allows sending a repeated start to access + // some devices' internal registers. This function is executed by the hardware + // TWI module on other processors (for example Due's TWI_IADR and TWI_MMR registers) + + beginTransmission(address); + + // the maximum size of internal address is 4 bytes + // не понятно почему ограничение размерности регистра в оригинальной Wire API в 3 байта + if (isize > 4){ + isize = 4; + } + + // write internal register address - most significant byte first + while (isize-- > 0) + write((uint8_t)(iaddress >> (isize*8))); + endTransmission(false); + } + + return requestFrom(address, quantity, sendStop); +} + +uint8_t I2CLIB::requestFrom(int address, int quantity) +{ + return requestFrom((uint8_t)address, (uint8_t)quantity, (uint8_t)true); +} + +uint8_t I2CLIB::requestFrom(int address, int quantity, int sendStop) +{ + return requestFrom((uint8_t)address, (uint8_t)quantity, (uint8_t)sendStop); +} + +size_t I2CLIB::write(const uint8_t *buffer, size_t size){ + for (size_t i = 0 ; i < size; ++i) + write(buffer[i]); + return size; +} + +void I2CLIB::start() +{ // сервисная функция с нее начинается любая работа с шиной + TWCR = _BV(TWSTA) | _BV(TWEN) | _BV(TWINT); // start + I2CLIB enable + установка флага "выполнить задачу" + while (!(TWCR & _BV(TWINT))); // Ожидание завершения +} + +void I2CLIB::stop() +{ // сервисная функция ей заканчивается работа с шиной + TWCR = _BV(TWSTO) | _BV(TWEN) | _BV(TWINT); // stop + I2CLIB enable + установка флага "выполнить задачу" +} + +I2CLIB Wire = I2CLIB(); + +#endif \ No newline at end of file diff --git a/OLED-I2C/OLEDI2C.h b/OLED-I2C/OLEDI2C.h new file mode 100644 index 0000000..75724e7 --- /dev/null +++ b/OLED-I2C/OLEDI2C.h @@ -0,0 +1,979 @@ + +#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 +#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 +#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 \ No newline at end of file diff --git a/OLED-I2C/charMap.h b/OLED-I2C/charMap.h new file mode 100644 index 0000000..5a65a18 --- /dev/null +++ b/OLED-I2C/charMap.h @@ -0,0 +1,233 @@ +#pragma once +// шрифты для вывода текста +const uint8_t _charMap[][5] PROGMEM = { + {0x00, 0x00, 0x00, 0x00, 0x00}, // 0x20 32 + {0x00, 0x00, 0x6f, 0x00, 0x00}, // ! 0x21 33 + {0x00, 0x07, 0x00, 0x07, 0x00}, // " 0x22 34 + {0x14, 0x7f, 0x14, 0x7f, 0x14}, // # 0x23 35 + {0x8C, 0x92, 0xFF, 0x92, 0x62}, // $ 0x24 36 + {0x23, 0x13, 0x08, 0x64, 0x62}, // % 0x25 37 + {0x36, 0x49, 0x56, 0x20, 0x50}, // & 0x26 38 + {0x00, 0x00, 0x07, 0x00, 0x00}, // ' 0x27 39 + {0x00, 0x1c, 0x22, 0x41, 0x00}, // ( 0x28 40 + {0x00, 0x41, 0x22, 0x1c, 0x00}, // ) 0x29 41 + {0x14, 0x08, 0x3e, 0x08, 0x14}, // * 0x2a 42 + {0x08, 0x08, 0x3e, 0x08, 0x08}, // + 0x2b 43 + {0x00, 0x50, 0x30, 0x00, 0x00}, // , 0x2c 44 + {0x08, 0x08, 0x08, 0x08, 0x08}, // - 0x2d 45 + {0x00, 0x60, 0x60, 0x00, 0x00}, // . 0x2e 46 + {0x20, 0x10, 0x08, 0x04, 0x02}, // / 0x2f 47 + {0x3e, 0x51, 0x49, 0x45, 0x3e}, // 0 0x30 48 + {0x00, 0x42, 0x7f, 0x40, 0x00}, // 1 0x31 49 + {0x42, 0x61, 0x51, 0x49, 0x46}, // 2 0x32 50 + {0x21, 0x41, 0x45, 0x4b, 0x31}, // 3 0x33 51 + {0x18, 0x14, 0x12, 0x7f, 0x10}, // 4 0x34 52 + {0x27, 0x45, 0x45, 0x45, 0x39}, // 5 0x35 53 + {0x3c, 0x4a, 0x49, 0x49, 0x30}, // 6 0x36 54 + {0x01, 0x71, 0x09, 0x05, 0x03}, // 7 0x37 55 + {0x36, 0x49, 0x49, 0x49, 0x36}, // 8 0x38 56 + {0x06, 0x49, 0x49, 0x29, 0x1e}, // 9 0x39 57 + {0x00, 0x36, 0x36, 0x00, 0x00}, // : 0x3a 58 + {0x00, 0x56, 0x36, 0x00, 0x00}, // ; 0x3b 59 + {0x08, 0x14, 0x22, 0x41, 0x00}, // < 0x3c 60 + {0x14, 0x14, 0x14, 0x14, 0x14}, // = 0x3d 61 + {0x00, 0x41, 0x22, 0x14, 0x08}, // > 0x3e 62 + {0x02, 0x01, 0x51, 0x09, 0x06}, // ? 0x3f 63 + {0x3e, 0x41, 0x5d, 0x49, 0x4e}, // @ 0x40 64 + {0x7e, 0x09, 0x09, 0x09, 0x7e}, // A 0x41 65 + {0x7f, 0x49, 0x49, 0x49, 0x36}, // B 0x42 66 + {0x3e, 0x41, 0x41, 0x41, 0x22}, // C 0x43 67 + {0x7f, 0x41, 0x41, 0x41, 0x3e}, // D 0x44 68 + {0x7f, 0x49, 0x49, 0x49, 0x41}, // E 0x45 69 + {0x7f, 0x09, 0x09, 0x09, 0x01}, // F 0x46 70 + {0x3e, 0x41, 0x49, 0x49, 0x7a}, // G 0x47 71 + {0x7f, 0x08, 0x08, 0x08, 0x7f}, // H 0x48 72 + {0x00, 0x41, 0x7f, 0x41, 0x00}, // I 0x49 73 + {0x20, 0x40, 0x41, 0x3f, 0x01}, // J 0x4a 74 + {0x7f, 0x08, 0x14, 0x22, 0x41}, // K 0x4b 75 + {0x7f, 0x40, 0x40, 0x40, 0x40}, // L 0x4c 76 + {0x7f, 0x02, 0x0c, 0x02, 0x7f}, // M 0x4d 77 + {0x7f, 0x04, 0x08, 0x10, 0x7f}, // N 0x4e 78 + {0x3e, 0x41, 0x41, 0x41, 0x3e}, // O 0x4f 79 + {0x7f, 0x09, 0x09, 0x09, 0x06}, // P 0x50 80 + {0x3e, 0x41, 0x51, 0x21, 0x5e}, // Q 0x51 81 + {0x7f, 0x09, 0x19, 0x29, 0x46}, // R 0x52 82 + {0x46, 0x49, 0x49, 0x49, 0x31}, // S 0x53 83 + {0x01, 0x01, 0x7f, 0x01, 0x01}, // T 0x54 84 + {0x3f, 0x40, 0x40, 0x40, 0x3f}, // U 0x55 85 + {0x0f, 0x30, 0x40, 0x30, 0x0f}, // V 0x56 86 + {0x3f, 0x40, 0x30, 0x40, 0x3f}, // W 0x57 87 + {0x63, 0x14, 0x08, 0x14, 0x63}, // X 0x58 88 + {0x07, 0x08, 0x70, 0x08, 0x07}, // Y 0x59 89 + {0x61, 0x51, 0x49, 0x45, 0x43}, // Z 0x5a 90 + {0x00, 0x00, 0x7f, 0x41, 0x00}, // [ 0x5b 91 + {0x02, 0x04, 0x08, 0x10, 0x20}, // \ 0x5c 92 + {0x00, 0x41, 0x7f, 0x00, 0x00}, // ] 0x5d 93 + {0x04, 0x02, 0x01, 0x02, 0x04}, // ^ 0x5e 94 + {0x40, 0x40, 0x40, 0x40, 0x40}, // _ 0x5f 95 + {0x00, 0x00, 0x03, 0x04, 0x00}, // ` 0x60 96 + {0x20, 0x54, 0x54, 0x54, 0x78}, // a 0x61 97 + {0x7f, 0x48, 0x44, 0x44, 0x38}, // b 0x62 98 + {0x38, 0x44, 0x44, 0x44, 0x20}, // c 0x63 99 + {0x38, 0x44, 0x44, 0x48, 0x7f}, // d 0x64 100 + {0x38, 0x54, 0x54, 0x54, 0x18}, // e 0x65 101 + {0x08, 0x7e, 0x09, 0x01, 0x02}, // f 0x66 102 + {0x0c, 0x52, 0x52, 0x52, 0x3e}, // g 0x67 103 + {0x7f, 0x08, 0x04, 0x04, 0x78}, // h 0x68 104 + {0x00, 0x44, 0x7d, 0x40, 0x00}, // i 0x69 105 + {0x20, 0x40, 0x44, 0x3d, 0x00}, // j 0x6a 106 + {0x00, 0x7f, 0x10, 0x28, 0x44}, // k 0x6b 107 + {0x00, 0x41, 0x7f, 0x40, 0x00}, // l 0x6c 108 + {0x7c, 0x04, 0x18, 0x04, 0x78}, // m 0x6d 109 + {0x7c, 0x08, 0x04, 0x04, 0x78}, // n 0x6e 110 + {0x38, 0x44, 0x44, 0x44, 0x38}, // o 0x6f 111 + {0x7c, 0x14, 0x14, 0x14, 0x08}, // p 0x70 112 + {0x08, 0x14, 0x14, 0x18, 0x7c}, // q 0x71 113 + {0x7c, 0x08, 0x04, 0x04, 0x08}, // r 0x72 114 + {0x48, 0x54, 0x54, 0x54, 0x20}, // s 0x73 115 + {0x04, 0x3f, 0x44, 0x40, 0x20}, // t 0x74 116 + {0x3c, 0x40, 0x40, 0x20, 0x7c}, // u 0x75 117 + {0x1c, 0x20, 0x40, 0x20, 0x1c}, // v 0x76 118 + {0x3c, 0x40, 0x30, 0x40, 0x3c}, // w 0x77 119 + {0x44, 0x28, 0x10, 0x28, 0x44}, // x 0x78 120 + {0x0c, 0x50, 0x50, 0x50, 0x3c}, // y 0x79 121 + {0x44, 0x64, 0x54, 0x4c, 0x44}, // z 0x7a 122 + {0x00, 0x08, 0x36, 0x41, 0x41}, // { 0x7b 123 + {0x00, 0x00, 0x7f, 0x00, 0x00}, // | 0x7c 124 + {0x41, 0x41, 0x36, 0x08, 0x00}, // } 0x7d 125 + {0x04, 0x02, 0x04, 0x08, 0x04}, // ~ 0x7e 126 + + {0x7E, 0x11, 0x11, 0x11, 0x7E}, //__А (0xC0). + {0x7F, 0x49, 0x49, 0x49, 0x33}, //__Б (0xC1). + {0x7F, 0x49, 0x49, 0x49, 0x36}, //__В (0xC2). + {0x7F, 0x01, 0x01, 0x01, 0x03}, //__Г (0xC3). + {0xE0, 0x51, 0x4F, 0x41, 0xFF}, //__Д (0xC4). + {0x7F, 0x49, 0x49, 0x49, 0x41}, //__Е (0xC5). + {0x77, 0x08, 0x7F, 0x08, 0x77}, //__Ж (0xC6). + {0x41, 0x49, 0x49, 0x49, 0x36}, //__З (0xC7). + {0x7F, 0x10, 0x08, 0x04, 0x7F}, //__И (0xC8). + {0x7C, 0x21, 0x12, 0x09, 0x7C}, //__Й (0xC9). + {0x7F, 0x08, 0x14, 0x22, 0x41}, //__К (0xCA). + {0x20, 0x41, 0x3F, 0x01, 0x7F}, //__Л (0xCB). + {0x7F, 0x02, 0x0C, 0x02, 0x7F}, //__М (0xCC). + {0x7F, 0x08, 0x08, 0x08, 0x7F}, //__Н (0xCD). + {0x3E, 0x41, 0x41, 0x41, 0x3E}, //__О (0xCE). + {0x7F, 0x01, 0x01, 0x01, 0x7F}, //__П (0xCF). + {0x7F, 0x09, 0x09, 0x09, 0x06}, //__Р (0xD0). + {0x3E, 0x41, 0x41, 0x41, 0x22}, //__С (0xD1). + {0x01, 0x01, 0x7F, 0x01, 0x01}, //__Т (0xD2). + {0x47, 0x28, 0x10, 0x08, 0x07}, //__У (0xD3). + {0x1C, 0x22, 0x7F, 0x22, 0x1C}, //__Ф (0xD4). + {0x63, 0x14, 0x08, 0x14, 0x63}, //__Х (0xD5). + {0x7F, 0x40, 0x40, 0x40, 0xFF}, //__Ц (0xD6). + {0x07, 0x08, 0x08, 0x08, 0x7F}, //__Ч (0xD7). + {0x7F, 0x40, 0x7F, 0x40, 0x7F}, //__Ш (0xD8). + {0x7F, 0x40, 0x7F, 0x40, 0xFF}, //__Щ (0xD9). + {0x01, 0x7F, 0x48, 0x48, 0x30}, //__Ъ (0xDA). + {0x7F, 0x48, 0x30, 0x00, 0x7F}, //__Ы (0xDB). + {0x00, 0x7F, 0x48, 0x48, 0x30}, //__Ь (0xDC). + {0x22, 0x41, 0x49, 0x49, 0x3E}, //__Э (0xDD). + {0x7F, 0x08, 0x3E, 0x41, 0x3E}, //__Ю (0xDE). + {0x46, 0x29, 0x19, 0x09, 0x7F}, //__Я (0xDF). + + {0x20, 0x54, 0x54, 0x54, 0x78}, //__а (0xE0). + {0x3C, 0x4A, 0x4A, 0x49, 0x31}, //__б (0xE1). + {0x7C, 0x54, 0x54, 0x28, 0x00}, //__в (0xE2). + {0x7C, 0x04, 0x04, 0x0C, 0x00}, //__г (0xE3). + {0xE0, 0x54, 0x4C, 0x44, 0xFC}, //__д (0xE4). + {0x38, 0x54, 0x54, 0x54, 0x18}, //__е (0xE5). + {0x6C, 0x10, 0x7C, 0x10, 0x6C}, //__ж (0xE6). + {0x44, 0x54, 0x54, 0x28, 0x00}, //__з (0xE7). + {0x7C, 0x20, 0x10, 0x08, 0x7C}, //__и (0xE8). + {0x78, 0x42, 0x24, 0x12, 0x78}, //__й (0xE9). + {0x7C, 0x10, 0x28, 0x44, 0x00}, //__к (0xEA). + {0x20, 0x44, 0x3C, 0x04, 0x7C}, //__л (0xEB). + {0x7C, 0x08, 0x10, 0x08, 0x7C}, //__м (0xEC). + {0x7C, 0x10, 0x10, 0x10, 0x7C}, //__н (0xED). + {0x38, 0x44, 0x44, 0x44, 0x38}, //__о (0xEE). + {0x7C, 0x04, 0x04, 0x04, 0x7C}, //__п (0xEF). + {0x7C, 0x14, 0x14, 0x14, 0x08}, //__р (0xF0). + {0x38, 0x44, 0x44, 0x44, 0x00}, //__с (0xF1). + {0x04, 0x04, 0x7C, 0x04, 0x04}, //__т (0xF2). + {0x0C, 0x50, 0x50, 0x50, 0x3C}, //__у (0xF3). + {0x30, 0x48, 0xFE, 0x48, 0x30}, //__ф (0xF4). + {0x44, 0x28, 0x10, 0x28, 0x44}, //__х (0xF5). + {0x7C, 0x40, 0x40, 0x7C, 0xC0}, //__ц (0xF6). + {0x0C, 0x10, 0x10, 0x10, 0x7C}, //__ч (0xF7). + {0x7C, 0x40, 0x7C, 0x40, 0x7C}, //__ш (0xF8). + {0x7C, 0x40, 0x7C, 0x40, 0xFC}, //__щ (0xF9). + {0x04, 0x7C, 0x50, 0x50, 0x20}, //__ъ (0xFA). + {0x7C, 0x50, 0x50, 0x20, 0x7C}, //__ы (0xFB). + {0x7C, 0x50, 0x50, 0x20, 0x00}, //__ь (0xFC). + {0x28, 0x44, 0x54, 0x54, 0x38}, //__э (0xFD). + {0x7C, 0x10, 0x38, 0x44, 0x38}, //__ю (0xFE). + {0x08, 0x54, 0x34, 0x14, 0x7C}, //__я (0xFF). + {0x38, 0x55, 0x54, 0x55, 0x18}, //__ё (0xFF). + /* + {0x7e, 0x09, 0x09, 0x09, 0x7e}, // А 192 + {0x7F, 0x49, 0x49, 0x49, 0x71}, // Б + {0x7f, 0x49, 0x49, 0x49, 0x36}, // В + {0x7F, 0x01, 0x01, 0x01, 0x01}, // Г + {0x60, 0x3E, 0x21, 0x3F, 0x60}, // Д + {0x7f, 0x49, 0x49, 0x49, 0x41}, // Е + {0x76, 0x08, 0x7F, 0x08, 0x76}, // Ж + {0x21, 0x41, 0x45, 0x4b, 0x31}, // З + {0x7F, 0x20, 0x10, 0x08, 0x7F}, // И + {0x7E, 0x20, 0x11, 0x08, 0x7E}, // Й + {0x7f, 0x08, 0x14, 0x22, 0x41}, // К + {0x70, 0x0E, 0x01, 0x01, 0x7F}, // Л + {0x7f, 0x02, 0x0c, 0x02, 0x7f}, // М + {0x7f, 0x08, 0x08, 0x08, 0x7f}, // Н + {0x3e, 0x41, 0x41, 0x41, 0x3e}, // О + {0x7F, 0x01, 0x01, 0x01, 0x7F}, // П + {0x7f, 0x09, 0x09, 0x09, 0x06}, // Р + {0x3e, 0x41, 0x41, 0x41, 0x22}, // С + {0x01, 0x01, 0x7f, 0x01, 0x01}, // Т + {0x07, 0x48, 0x48, 0x48, 0x7F}, // У + {0x1C, 0x22, 0x7F, 0x22, 0x1C}, // Ф + {0x63, 0x14, 0x08, 0x14, 0x63}, // Х + {0x7F, 0x40, 0x40, 0x7F, 0xC0}, // Ц + {0x07, 0x08, 0x08, 0x08, 0x7F}, // Ч + {0x7F, 0x40, 0x7F, 0x40, 0x7F}, // Ш + {0x7F, 0x40, 0x7F, 0x40, 0xFF}, // Щ + {0x01, 0x7F, 0x48, 0x48, 0x70}, // Ъ + {0x7F, 0x48, 0x70, 0x00, 0x7F}, // Ы + {0x00, 0x7F, 0x48, 0x48, 0x70}, // Ь + {0x22, 0x41, 0x49, 0x49, 0x3E}, // Э + {0x7F, 0x08, 0x3E, 0x41, 0x3E}, // Ю + {0x46, 0x29, 0x19, 0x09, 0x7F}, // Я 223 + + {0x20, 0x54, 0x54, 0x54, 0x78}, //a 224 + {0x3c, 0x4a, 0x4a, 0x49, 0x31}, //б + {0x7c, 0x54, 0x54, 0x28, 0x00}, //в + {0x7c, 0x04, 0x04, 0x04, 0x0c}, //г + {0xe0, 0x54, 0x4c, 0x44, 0xfc}, //д + {0x38, 0x54, 0x54, 0x54, 0x18}, //e + {0x6c, 0x10, 0x7c, 0x10, 0x6c}, //ж + {0x44, 0x44, 0x54, 0x54, 0x28}, //з + {0x7c, 0x20, 0x10, 0x08, 0x7c}, //и + {0x7c, 0x41, 0x22, 0x11, 0x7c}, //й + {0x7c, 0x10, 0x28, 0x44, 0x00}, //к + {0x20, 0x44, 0x3c, 0x04, 0x7c}, //л + {0x7c, 0x08, 0x10, 0x08, 0x7c}, //м + {0x7c, 0x10, 0x10, 0x10, 0x7c}, //н + {0x38, 0x44, 0x44, 0x44, 0x38}, //o + {0x7c, 0x04, 0x04, 0x04, 0x7c}, //п + {0x7C, 0x14, 0x14, 0x14, 0x08}, //p + {0x38, 0x44, 0x44, 0x44, 0x20}, //c + {0x04, 0x04, 0x7c, 0x04, 0x04}, //т + {0x0C, 0x50, 0x50, 0x50, 0x3C}, //у + {0x30, 0x48, 0xfc, 0x48, 0x30}, //ф + {0x44, 0x28, 0x10, 0x28, 0x44}, //x + {0x7c, 0x40, 0x40, 0x40, 0xfc}, //ц + {0x0c, 0x10, 0x10, 0x10, 0x7c}, //ч + {0x7c, 0x40, 0x7c, 0x40, 0x7c}, //ш + {0x7c, 0x40, 0x7c, 0x40, 0xfc}, //щ + {0x04, 0x7c, 0x50, 0x50, 0x20}, //ъ + {0x7c, 0x50, 0x50, 0x20, 0x7c}, //ы + {0x7c, 0x50, 0x50, 0x20, 0x00}, //ь + {0x28, 0x44, 0x54, 0x54, 0x38}, //э + {0x7c, 0x10, 0x38, 0x44, 0x38}, //ю + {0x08, 0x54, 0x34, 0x14, 0x7c}, //я 255 +*/ +}; \ No newline at end of file diff --git a/OLED-I2C/sketch_i2c_martin_v2.ino b/OLED-I2C/sketch_i2c_martin_v2.ino new file mode 100644 index 0000000..851382c --- /dev/null +++ b/OLED-I2C/sketch_i2c_martin_v2.ino @@ -0,0 +1,16 @@ +// #include +// GyverOLED oled; + +#include "OLEDI2C.h" +OLEDI2C oled; + +void setup() { + oled.init(); // инициализация + oled.clear(); // очистка + oled.setScale(3); // масштаб текста (1..4) + oled.home(); // курсор в 0,0 + oled.println("22222"); + oled.println("33333"); +} +void loop() { +} \ No newline at end of file