14-07-2025, 00:20
Делая беспроводной модуль для Квант WiFi столкнулся с очень медленным выводом на дисплей SSD1306, разобравшись понял что дело в том что даже если рисуешь одну точку или один символ и потом вызываешь функцию display(); чтобы вывести на экран то происходит обновление всего экрана, а это передача по i2c целых 1024 байт! это минимум 28 а максимум 43 мс, зависит от настроек скорости i2c. Меня это категорически не устраивало так как у меня данные поступали каждые 12мс и их надо обрабатывать и выводить на экран в режиме реального времени.
Я начал искать функцию которая обновляет только ту область экрана которую меняешь, но её нет в библиотеке Adafruit_SSD1306. Думал уже заказывать SPI дисплей, он быстрее но всё равно не так как хотелось-бы, да и не наш это метод, дожать то что есть (i2c) показалось мне интересной задачей. Ну и вот написал я функцию которая позволяет обновлять только ту область экрана которую меняешь, это позволило мне уложится в 12 мс, может кому пригодится.
Вот пример вывода одной строки стандартной функцией(43мс) и модифицированной (11 мс).
Новая функция вставляется в файл Adafruit_SSD1306.cpp
Заголовочная в Adafruit_SSD1306.h
Я вставил рядом с стандартной функцией.
Все это должно быть в папке Adafruit_SSD1306-master которая там где у вас установлены библиотеки.
Это вставляем в Adafruit_SSD1306.cpp
Это вставляем в Adafruit_SSD1306.h
Page_start, Page_end значения 0...7, это стоки которые надо обновить.
Column_start, Column_end значения 0....127, это кусок строки который надо обновить.
Пример:
Есть ограничения, связано это с устройством экранной памяти дисплея. Обновлять можно только по строкам, несколько или одну, всего 8 строк шириной 8 пикселей и длинной 128 пикселей, а вот область строки можно выбирать с точностью до пикселя. Всё это надо учитывать когда размещаешь текст или изображение.
Я начал искать функцию которая обновляет только ту область экрана которую меняешь, но её нет в библиотеке Adafruit_SSD1306. Думал уже заказывать SPI дисплей, он быстрее но всё равно не так как хотелось-бы, да и не наш это метод, дожать то что есть (i2c) показалось мне интересной задачей. Ну и вот написал я функцию которая позволяет обновлять только ту область экрана которую меняешь, это позволило мне уложится в 12 мс, может кому пригодится.
Вот пример вывода одной строки стандартной функцией(43мс) и модифицированной (11 мс).
Новая функция вставляется в файл Adafruit_SSD1306.cpp
Заголовочная в Adafruit_SSD1306.h
Я вставил рядом с стандартной функцией.
Все это должно быть в папке Adafruit_SSD1306-master которая там где у вас установлены библиотеки.
Это вставляем в Adafruit_SSD1306.cpp
Код:
void Adafruit_SSD1306::display(uint8_t Page_start, uint8_t Page_end, uint8_t Column_start, uint8_t Column_end) {
TRANSACTION_START
static uint8_t dlist1[] = {
SSD1306_PAGEADDR,
0, // Page start address
0, // Page end (not really, but works here)
SSD1306_COLUMNADDR, 0, 0}; // Column start address
dlist1[1] = Page_start;
dlist1[2] = Page_end;
dlist1[4] = Column_start;
dlist1[5] = Column_end;
uint8_t n = sizeof(dlist1);
uint8_t *ptr = dlist1;
if (wire)
{
wire->beginTransmission(i2caddr);
WIRE_WRITE((uint8_t)0x00); // Co = 0, D/C = 0
while (n--)WIRE_WRITE(*ptr++);
wire->endTransmission();
}
else // SPI -- transaction started in calling function
{
SSD1306_MODE_COMMAND
while (n--) SPIwrite(*ptr++);
}
#if defined(ESP8266)
yield();
#endif
uint16_t bytesOut;
if (wire) // I2C
{
wire->beginTransmission(i2caddr);
WIRE_WRITE((uint8_t)0x40);
bytesOut = 1;
} else {SSD1306_MODE_DATA}
for (int i = Page_start; i <=Page_end; i++)
{
ptr = buffer + (i * 128 + Column_start);
uint16_t count = Column_end-Column_start+1;
while (count--)
{
if (bytesOut >= WIRE_MAX && wire)
{
wire->endTransmission();
wire->beginTransmission(i2caddr);
WIRE_WRITE((uint8_t)0x40);
bytesOut = 1;
}
if(wire){ WIRE_WRITE(*ptr++); bytesOut++;}
else SPIwrite(*ptr++);
}
}
if(wire)wire->endTransmission();
//TRANSACTION_END
#if defined(ESP8266)
yield();
#endif
}Это вставляем в Adafruit_SSD1306.h
Код:
void display(uint8_t Page_start, uint8_t Page_end, uint8_t Column_start, uint8_t Column_end);Page_start, Page_end значения 0...7, это стоки которые надо обновить.
Column_start, Column_end значения 0....127, это кусок строки который надо обновить.
Пример:
Код:
#include <Adafruit_SSD1306.h>
#define SSD1306_NO_SPLASH
Adafruit_SSD1306 display(128, 64, &Wire, -1);//
uint32_t startTime, leadTime;
void setup()
{
Serial.begin(9600);
display.begin(SSD1306_SWITCHCAPVCC, 0x3C);
display.clearDisplay();
display.setTextSize(1);
display.setTextColor(WHITE);
display.setFont();
startTime = millis();
display.setCursor(0,0);
display.print("SSD1306 speed test 1");
display.display();
leadTime = millis() - startTime;
Serial.print("SSD1306 speed test 1: ");
Serial.print(leadTime);
Serial.println(" ms");
Serial.println();
startTime = millis();
display.setCursor(0,8);
display.print("SSD1306 speed test 2");
display.display(1,1,0,127);
leadTime = millis() - startTime;
Serial.print("SSD1306 speed test 2: ");
Serial.print(leadTime);
Serial.println(" ms");
}
void loop()
{
}Есть ограничения, связано это с устройством экранной памяти дисплея. Обновлять можно только по строкам, несколько или одну, всего 8 строк шириной 8 пикселей и длинной 128 пикселей, а вот область строки можно выбирать с точностью до пикселя. Всё это надо учитывать когда размещаешь текст или изображение.

