Картинка в скетче представляет собой одномерный массив, где его элементами являются числа - два байта на одну точку экрана. Есть приложения-конвертеры для пк.
Например:
Это я реализовал еще до создания темы. Вопрос в считывании нужного фрагмента картинки. У вас в примере используется указатель на выводимый фрагмент. Могу предположить, что: 1. Картинка хранится в памяти, а не файле 2. картинка хранится фрагментами.
Я не спрашиваю готовую библиотеку. Не вижу проблем самому ее написать. Хочется красивый алгоритм. Не в виде кода, а прям словами можно.
Спасибо за пояснение. Хоть формат похожий придумал В моей реализации файл состоит из строк, каждая соответствует 1 строке дисплея. Когда считываю, то загоняю в 2х мерный массив.
Сейчас решил датащит почитать. Лучше поздно, чем никогда. Может еще какие мысли появятся.
Если сможете, попробуйте функцию написать для чтения данных по пикселям из экрана…
…Ну а вообще желательно динамику графики сначала в голове представлять, что вы хотите. Есть два подхода - заготовки (фоны, спрайты,символы) хранятся на флеш мк или изображение генерируется с помощью математики и нигде как в памяти экрана не хранится, ну ещё в буфере (копия экрана в ОЗУ мк).
Это лишнее. Одномерный массив можно “разбить” на двухмерный и трёх мерный. Пример в реальности это лента из умных пикселей - можно ленту, можно сложить плоскость, можно и куб из неё сделать.
Это как?
И, кстати, в UNO три разных вида памяти. В какую?
А файл то откуда взялся?
Неправильно Вы знаете. Для доступа к любой позиции в файле служит функция seek.
И, опять же, откуда взялись в файлы?
Вы бы сформулировали в одном месте, что и как делаете, а то, когда в нескольких десятках сообщений разбросаны взаимоисключающие утверждения, довольно трудно понять, о чем идет речь.
Хочется внятное описание задачи. Хотя бы потому, что хорошего универсального решения не существует, а для конкретного случая не хватает информации, чем этот конкретный случай отличается от общего.
Поясните, что именно Вы подразумеваете под строкой (в программировании это многозначный термин).
То есть у Вас образ экрана целиком помещается в оперативной памяти?
Если “нет”, то аналогия неочевидна.
И, все-таки, хотелось бы видеть подробное изложение задачи конкретно, а не в общем виде (т.к. в общем виде совет один: объем оперативной памяти должен в несколько раз превышать объем информации на экране).
Я опять боюсь кого-то задеть, но зачем “бегать в мешке”? Только если нужно освоить технику бега в мешке.
Я все про то, с чего тема началась. Есть Ардуино-совместимые контроллеры за те же 200 рублей, но с нормальным количеством памяти. RP2040 к примеру.
Если в рамках подготовки к “Концу Света”, и нужно учиться работать с 4К ОЗУ… ну тогда ОК. Но тут же все равно цепляется SD карта и файл. Весь зоопарк получается дороже RP2040 или ESP32. А памяти все равно в разы меньше и медленнее.
Я думаю, что даже в случае “зомби-апокалипсиса” еще останутся запасы 32 битных контроллеров… и даже большие, чем 8 битных.
Если заговорили о “Конце Света”, то вспомним о начале компьютерной эпохи. Важно не количество ресурсов : памяти и мощи процессора, а умение их оперативно распределять. Так для рисования графических картинок требуется много ресурсов, а когда рисовать не надо, то эти ресурсы не к чему. Вот и происходят рывки в работе устройств, которые видны визуально. Так что говоря о графике надо или говорить о создании отдельного графического ядра или программирование с фоновой работой.
Не, там чёрт ногу сломит, как всё сложно. Вот функция чтения пикселя и как это применить к UNO - хрен знает :).
/* Читает в буфер data данные о цвете пикселей из окна дисплея с координатами левого верхнего угла (x, y), шириной w, высотой h.
* Доступны 2 режима работы со spi: полный дуплекс (full-duplex) и полудуплекс (half-duplex).
* Полный дуплекс применяется на дисплеях, подключаемых к МК по двум однонаправленным линиям данных: MOSI и MISO.
* Полудуплекс применяется для чтения данных с контроллеров дисплеев, содержащих только один вывод SDA, совмещающий линии
* in/out. Вывод CS в режиме полудуплекса СТРОГО ОБЯЗАТЕЛЕН, т.к. только после установки в высокий уровень этого сигнала
* контроллер дисплея выйдет из режима передачи данных MCU и будет готов к приему новых команд от MCU. При отсутствии
* вывода CS возврат в режим приема команд контроллером дисплея может быть осуществлен только после сброса (reset)
* контроллера дисплея, который в данном драйвере/библиотеке выполняется командой инициализации LCD_Init(lcd),
* где lcd - указатель на обработчик дисплея.
* Управление режимом работы процедуры осуществляется параметром SPI_HALF_DUPLEX_READ в файле display.h.
* Для включения полудуплекса:
* #define SPI_HALF_DUPLEX_READ 1
* Для включения полного дуплекса:
* #define SPI_HALF_DUPLEX_READ 0
* Скорость чтения, как правило, ниже (иногда значительно) скорости записи данных в контроллер поэтому введен параметр,
* определяющий скорость чтения из контроллера дисплея SPI_SPEED_DISPLAY_READ. Принимает значение от 0 до 7 и настраивается
* в display.h. Причем, 0 соответствует clk/2, 1 - clk/4, ... 7 - clk/256. Где clk - частота тактирования spi.
*/
void LCD_ReadImage(LCD_Handler* lcd, uint16_t x, uint16_t y, uint16_t w, uint16_t h, uint16_t *data)
{
uint16_t x1 = x + w - 1, y1 = y + h - 1; //Определяем координаты правого нижнего угла окна
//Проверяем параметры окна, т.к. за пределами размерности дисплея не работаем
if (x >= lcd->Width || y >= lcd->Height || x1 >= lcd->Width || y1 >= lcd->Height) return;
//Пересчет координат окна в зависимости от характеристик матрицы дисплея и контроллера.
//Соответствующий offs в драйвере дисплея определяет эти характеристики, учитывая разницу размерностей,
//начальное смещение матрицы дисплея относительно поля памяти контроллера дисплея и ориентацию изображения на матрице
x += lcd->x_offs;
y += lcd->y_offs;
x1 += lcd->x_offs;
y1 += lcd->y_offs;
//Создаем управляющую строку для интерпретатора драйвера дисплея, которая укажет контроллеру дисплея,
//что мы определяем область памяти и хотим прочитать эту область. Предварительно переведем дисплей в
//режим цвета 18 бит, так как команда Memory Read (0x2E) должна работать только с режимом цвета 18 бит.
uint8_t set_win_and_read[] = { //Команда выбора режима цвета
LCD_UPR_COMMAND, 0x3A, 1, 0x66, //0x66 - 18-битный цвет, 0x55 - 16-битный
//Установка адресов, определяющих блок:
LCD_UPR_COMMAND, 0x2A, 4, 0, 0, 0, 0, //столбец
LCD_UPR_COMMAND, 0x2B, 4, 0, 0, 0, 0, //строка
//Команда Memory Read (читать память)
LCD_UPR_COMMAND, 0x2E, 0,
LCD_UPR_END };
//Вписываем в управляющую строку координаты заданного окна
set_win_and_read[7] = x >> 8; set_win_and_read[8] = x & 0xFF;
set_win_and_read[9] = x1 >> 8; set_win_and_read[10] = x1 & 0xFF;
set_win_and_read[14] = y >> 8; set_win_and_read[15] = y & 0xFF;
set_win_and_read[16] = y1 >> 8; set_win_and_read[17] = y1 & 0xFF;
//Ждем, когда дисплей освободится и будет готов к приему новых команд и данных
while (LCD_GetState(lcd) != LCD_STATE_READY) { __NOP(); }
lcd->cs_control = 1; //Отключаем управление линией CS со стороны интерпретатора управляющих строк
SPI_TypeDef *spi = lcd->spi_data.spi;
uint32_t spi_param = spi->CR1; //Запоминаем параметры spi
//Настраиваем spi (spi у нас уже выключен)
//настройки полный дуплекс
spi->CR1 &= ~(SPI_CR1_BIDIMODE | //2 линии, однонаправленный режим
SPI_CR1_RXONLY | //прием и передача
SPI_CR1_CRCEN |
SPI_CR1_BR_Msk | //Маска скорости spi
SPI_CR1_DFF); //Ширина кадра 8 бит
//Установим скорость spi для чтения дисплея.
//Параметр SPI_SPEED_DISPLAY_READ настраивается в display.h
//Дело в том, что согласно спецификаций на контроллеры дисплея, скорость
//в режиме чтения из контроллера, как правило, ниже скорости в режиме записи данных
//в контроллер.
spi->CR1 |= (uint32_t)((SPI_SPEED_DISPLAY_READ & 7) << 3);
//Отправляем через интерпретатор управляющую строку на контроллер дисплея
//"Дергать" выводом CS после отправки команды 0x2E нельзя, т.к. контроллер
//дисплея может "подумать", что мы хотим прерывать чтение.
LCD_CS_LOW //Подключаем контроллер дисплея к МК
LCD_String_Interpretator(lcd, set_win_and_read);
LCD_DC_HI //Вывод DC контроллера дисплея установим в положении "данные". Но, мои эксперименты показывают,
//что чтение работает и в положении "команда", что странно, т.к. согласно спецификации, первая команда, в т.ч.,
//NOP должна прерывать операцию чтения памяти контроллера. В общем, чтение идет до тех пор, пока мы не прочитаем
//всю выбранную нами область, либо пока тупо не прервем процесс чтения.
uint32_t len = w * h; //Количество пикселей для чтения
uint16_t *data_ptr = data; //Указатель на местоположение буфера для хранения пикселей
uint8_t r, g, b; //Переменные с цветовыми составляющими
#if (SPI_HALF_DUPLEX_READ == 1) //Если используется полудуплекс, то скорректируем настройки spi
//Настройки для полудуплекса (только прием):
spi->CR1 |= SPI_CR1_BIDIMODE; //Двунаправленная линия данных
spi->CR1 &= ~SPI_CR1_BIDIOE; //Режим приема
spi->CR1 ^= SPI_CR1_CPHA_Msk; //Согласно спецификации при приеме меняем фазу на противоположную
#endif
//Включаем spi. Для полудуплексного режима прием стартует сразу же, как только включим spi
spi->CR1 |= SPI_CR1_SPE;
//16 холостых тактов для подготовки контроллера дисплея к отправке данных MCU
int i = 2;
while (i--) {
#if (SPI_HALF_DUPLEX_READ == 0) //В полудуплексе при приеме заливать данные в DR для старта тактирования не надо
LL_SPI_TransmitData8(spi, 0x00); //NOP
#endif
while (!(spi->SR & SPI_SR_RXNE)) { __NOP(); } //Ожидаем прием ответа от контроллера дисплея
r = LL_SPI_ReceiveData8(spi);
}
//------------------------------ Читаем данные о цвете len пикселей --------------------------
while (len--) {
//Считываем последовательно цветовые составляющие
//По спецификации последовательность считываемых составляющих цветов заявлена r, g, b,
//Если считываемые цвета будут не соответствовать фактическим, то снизьте скорость spi для чтения,
//но иногда стабильности чтения помогает подтяжка к питанию линии MISO spi.
#if (SPI_HALF_DUPLEX_READ == 0)
LL_SPI_TransmitData8(spi, 0x00); //NOP
#endif
while (!(spi->SR & SPI_SR_RXNE)) { __NOP(); }//Ожидаем прием ответа от контроллера дисплея
r = LL_SPI_ReceiveData8(spi);
#if (SPI_HALF_DUPLEX_READ == 0)
LL_SPI_TransmitData8(spi, 0x00); //NOP
#endif
while (!(spi->SR & SPI_SR_RXNE)) { __NOP(); }
g = LL_SPI_ReceiveData8(spi);
#if (SPI_HALF_DUPLEX_READ == 0)
LL_SPI_TransmitData8(spi, 0x00); //NOP
#endif
while (!(spi->SR & SPI_SR_RXNE)) { __NOP(); }
b = LL_SPI_ReceiveData8(spi);
*data_ptr++ = LCD_Color(lcd, r, g, b); //Преобразуем цвет из R8G8B8 в R5G6B5 и запоминаем его
}
LCD_CS_HI //Отключаем контроллер дисплея от МК.
//В режиме полудуплекса, согласно спецификации, это будет еще и сигналом для
//переключения направления линии SDA на прием информации от MCU. Так что, без
//линии CS на дисплее НЕ ОБОЙТИСЬ.
#if (SPI_HALF_DUPLEX_READ == 0)
while (spi->SR & SPI_SR_BSY) { __NOP(); } //Ждем когда spi освободится
//А в полудуплексе ждать не надо (см. спецификацию MCU),
//но надо дочитывать...
#endif
spi->CR1 &= ~SPI_CR1_SPE; //spi выключаем
#if (SPI_HALF_DUPLEX_READ == 1)
//Обязательное дочитывание после выключения spi для полудуплексного режима, иначе будет "не гуд"
while (!(spi->SR & SPI_SR_RXNE)) { __NOP(); }
#endif
spi->CR1 = spi_param; //Восстанавливаем параметры spi
//Восстанавливаем 16-битный режим цвета
lcd->cs_control = 0; //Включаем управление линией CS со стороны интерпретатора управляющих строк
uint8_t color_restore[] = { LCD_UPR_COMMAND, 0x3A, 1, 0x55,
LCD_UPR_END };
LCD_String_Interpretator(lcd, color_restore);
}
Народ, просто уточнение - у автора не Уно, а ЕСП8266
Сообщение №30
@Valery22
Не считайте зазорным отвечать на вопросы, даже если вам их задают по два-три раза. Люди в ветку приходят и уходят, по мере того как сообщений все больше - многие теряют нить разговора.
Уточните, то у вас за файл - в файловой системе, образованной во флеше ЕСП8266?
Наверное можно. Я спросил у автора как читать данные пикселей из ILI9341 на UNO с использованием SPI.h. Он мне ответил - Не пользуюсь Ардуино и готовыми библиотеками для работы с дисплеями…на моём гитхабе есть демка чтения gram памяти.