Adafruit_NeoMatrix / Adafruit_GFX / Класс печати — как здесь работает печать текста
Я совсем новичок в Arduino и не писал код на C/C++ уже несколько лет. Я просматриваю библиотеку NeoMatrix, которая расширяет класс NeoPixel. Этот класс расширяет класс Adafruit_GFX, который расширяет класс Print. Класс NeoMatrix предоставляет много логики для различных конфигураций нитей NeoPixel. Adafruit_GFX предоставляет метод drawChar, и это последнее, что мне понятно.
Из примеров следует, что нужно вызвать метод print, который унаследован от класса Print ядра Arduino (насколько я понимаю). Я просматривал исходный файл и, хоть убей, не могу понять, как это работает. В классе Print нет вызовов чего-либо, что могло бы печатать символы, я не вижу ни одного метода, переопределенного где-либо выше в иерархии наследования классов. Можете ли вы помочь мне включить запись, чтобы выяснить, что здесь за стек вызовов — если я вызываю метод print на экземпляре Adafruit_NeoMatrix, как он в итоге печатает текст? Я думаю, где-то должен быть код, который вызывает метод drawChar, проверяет ширину символа, перемещает курсор — но я не могу его найти :)
Вот о каких библиотеках идет речь: https://github.com/adafruit/Adafruit_NeoMatrix https://github.com/adafruit/Adafruit_NeoPixel https://github.com/adafruit/Adafruit-GFX-Library
@theadam, 👍-1
Обсуждение2 ответа
Лучший ответ:
Ответ Ника Гэммона правильный. Вы оставили комментарий к нему, указав, вам нужны были некоторые подробности:
... Библиотеки, которые я посетил, Упомянутые методы, похоже, не переопределяют ни один из методов класса Print.
Вы увидите, что это делает то, что связано и описано в #3 ниже. И это аналогичная часть virtual size_t write (const byte c), показанная в ответе Ника Гэммона.
Я также не уверен, как просто перезаписать запись, как вы упомянули. работает.
write(uint8_t) в Print является чисто виртуальным. Полное рассмотрение (чисто) виртуальных функций заполнило бы главу (или, может быть, две) в книге по C++. Но вкратце: это объявление функции в Print без реализации. По сути, это дыра, намеренно оставленная в Print для типа-байт-записи-что-либо, которая должна быть заполнена производным классом, который в данном случае является Adafruit_GFX. Если вы попытаетесь создать экземпляр переменной типа Print (дословно как Print myPrint;), вы увидите ошибку, в которой упоминается write(uint8_t), поскольку он все еще отсутствует в полном типе. Если вы знаете, что такое обратный вызов с использованием указателя функции, то чистая виртуальная функция немного похожа на это. Когда Adafruit_GFX переопределяет Print и предоставляет свое собственное определение для write(uint8_t) (как вы увидите ниже), он фактически регистрирует это как обратный вызов, который используется всякий раз, когда что-либо пытается использовать write(uint8_t) Print на объекте Adafruit_GFX (или в этом случае на той части большего объекта Adafruit_NeoMatrix). Это включает в себя другие части Print (другие перегрузки print/println, которые понимают, как работать с числами или чем-то еще), которые используют write(uint8_t) напрямую или косвенно. Если вы действительно хотите узнать больше о том, как это выглядит в типичном сгенерированном коде, посмотрите, как работают vtables.
Я вызываю метод печати, что-то должно перебрать все символы и вызовите методы записи - я ищу этот код, пытаясь понять весь процесс печати.
Вы можете покопаться в коде и найти все места, где это происходит. Вот один пример из f ar too many. Это внутренняя вспомогательная функция для печати целого числа, которую вы найдете в некоторых общедоступных функциях print(), работающих с числами. Как вы можете видеть, она использует write(uint8_t).
Если вы покопаетесь в ядре, вы обнаружите, что все направлено на вызовы этой функции.
Но вы также можете просто заметить, что единственная чисто виртуальная функция в Print — это функция для записи отдельных байтов.
Вы заметите, что нет ничего похожего; например, нет функции для регистрации обратного вызова. Таким образом, в общем случае вещей, поддерживающих Print (файлы LiquidCrystal, SD.h, Serial и т. д.), чистая виртуальная перегрузка обработки одного байта write() является необходимой и достаточной вещью для создания класса, производного от Print.
Он может опционально иметь определенное поведение вокруг очистки. Он может опционально иметь переопределение записи, которое обрабатывает несколько символов; это уменьшит накладные расходы на вызовы для нескольких символов. Если вы потратите несколько часов и отследите все вызовы в ArduinoCore-API или внутренностях Print ArduinoCore-AVR, вы обнаружите, что все они сводятся к вызовам do чистого виртуального write(uint8_t) (при отсутствии подкласса, переопределяющего блочную форму write). Так что эта одна (или почти одна) чисто виртуальная функция просто является механизмом, с помощью которого общие вещи в Print вызывают выполнение вещей, специфичных для конкретного варианта использования, в производном классе.
Ваш конкретный случай
Ниже показана последовательность событий, включая случаи, когда реализация write(uint8_t) специфична для вашего случая.

Сказать об этом можно только так много. Вы вызываете некоторую функцию
print. Таким образом, вы вызываете напрямую в кодPrint, а не что-либо в коде Adafruit. Но имейте в виду, что вы делаете это с объектом, для которогоPrintбыл завершен с определениемwrite(uint8_t)внутриAdafruit_GFX, ссылка на который приведена ниже.Нет ничего особенного в понимании этой части, за исключением того, что в
Printесть только несколько чисто виртуальных функций. Единственная заслуживающая внимания функция здесь — этоwrite(uint8_t). Технически возможно реализовать формуwrite(), принимающую блоки. НоAdafruit_GFX, как и большинство вещей, этого не делает и вместо этого полагается на реализацию по умолчанию перегрузки, принимающей блоки, дляwrite(), чтобы передать ее перегрузке, обрабатывающей отдельные байты.Здесь Adafruit_GFX переносит работу из своего
write(uint8_t)вdrawChar(). Я выделил только одно место, где он делает этот вызов.drawChar()передача работы вwritePixel()writePixel()вызываетdrawPixel(). Выше есть примечание, которое, по-видимому, говорит о потенциальном различии между записью и рисованием для вещей, которые переопределяютwritePixel. NeoMatrix не переопределяетwritePixel(). Так что никакого различия.drawPixel()является чисто виртуальным в Adafruit_GFX. Его реализация дляNeoMatrixвызывает классNeoPixel.Вызов скетча
show()просто идет прямо в часть NeoPixel объекта, чтобы отправить полезную нагрузку провода, как обычно. Он не переопределяется NeoMatrix для выполнения чего-либо дополнительного. р>
Хорошо объяснено, очень хорошо!, @Nick Gammon
Если вы наследуете класс от класса Print, то все, что вам нужно сделать, это переопределить функцию write для записи на устройство любым удобным вам способом. Например, с помощью SPI, I2C, Neopixels или чего-то еще.
Это позволяет вам использовать возможности Print (печатать целые числа, символы, строки), и все, что вам нужно сделать, это предоставить функцию, которая выводит один символ.
Вот пример с моей веб-страницы об отладке Arduino:
class tSPIdebug : public Print
{
public:
virtual size_t write (const byte c)
{
digitalWrite(SS, LOW);
SPI.transfer (c);
digitalWrite(SS, HIGH);
return 1;
} // конец tSPIdebug::write
}; // конец tSPIdebug
Обратите внимание, как я создал класс, производный от Print, и все, что он делает, это переопределяет, как write обрабатывает вывод. Все остальное затем обрабатывается Print, и все, что должен сделать мой класс, это фактически выполнить печать одного байта.
Я понимаю, как это должно работать. Библиотеки, о которых я упомянул, похоже, не переопределяют ни один из методов класса Print. Я также не уверен, как работает просто перезапись записи, как вы упомянули. Я вызываю метод печати, что-то должно перебрать все символы и вызвать методы записи — я ищу этот код, пытаясь понять весь поток процесса печати., @theadam
Timemage объяснил гораздо подробнее. Adafruit_GFX.cpp реализует size_t Adafruit_GFX::write(uint8_t c), которая является той самой функцией (write), о которой я упоминал. Этот класс является производным от Print: class Adafruit_GFX : public Print., @Nick Gammon
- Управлять несколькими полосками WS2812B с разным количеством светодиодов.
- Как управлять светодиодами Neopixel параллельно?
- Светодиодная лента WS2812B + First Pixel продолжает ломаться
- Использование кольца NeoPixel без Arduino
- Странное поведение со светодиодной лентой WS2812B RGB
- Какая хорошая альтернатива Arduino Nano, которую можно использовать с Neopixels?
- Как сгладить переход на другой цвет NEOPIXELS
- Как запрограммировать кнопку на цвет для полоски неопикселей?
пожалуйста, отредактируйте свой пост и добавьте ссылку на библиотеку, @jsotola
Если быть точным, это C++, а не C., @Nick Gammon
Связаны библиотеки. Это правда для C++., @theadam