TFT на базе ST7735
- Войдите на сайт для отправки комментариев
Сб, 10/09/2016 - 01:56
А никто не пробовал читать из фреймбуфера дисплея на базе ST7735 при помощи AVR-овского MSPIM? Именно хардверным SPI, не программным.
А никто не пробовал читать из фреймбуфера дисплея на базе ST7735 при помощи AVR-овского MSPIM? Именно хардверным SPI, не программным.
Неужели никому не надо было спрайты по экрану гонять? Народ! Красиво же! А без чтения фреймбуфера никакой оперативки не хватит.
Э, наскольео я помню у большинства TFT буффером является регисры ввода/вывода, тоесть 16 бит и/или 8 бит, зависит от IM0 - 3. Судя по построению библиотек для TFT под AVR все делает м/к. Редкие TFT имеют две страници памяти и можно перед сменой экрана записывать невидимую страницу, а потом давать команду на ее отображение. Самый распрастраненный вариант это обновление определенной области. Если надо гонять по экрану спрайт, то затирают нужным спрайтом старую и выводят в новую. Если вы ведете речь о работе с TFT без использования встоенной памяти матрицы, то в этом случае все определяется объемом внешней памяти и алгоритмом програмы. Только с чего вы взяли что 7735 может работать с внешней памятью в даташите про это ни слово.
Только с чего вы взяли что 7735 может работать с внешней памятью в даташите про это ни слово.
При чем тут внешняя память? На борту у ST7735 есть фреймбуфер размером в 384 килобита. Именно в нем хранится отображаемое изображение в формате 18 бит на пиксель. То есть, в каком бы формате (12/16/18 бит на пиксель) мы не писали, там будет все равно пересчитанное в 18 бит на пиксель. Для чтения фреймбуфера есть команда RAMRD, позволяющая считать и передать в МК содержимое любого прямоугольника, любого размера в любом месте фреймбуфера.
Если мы работаем со спрайтом, то у нас два варианта:
1. Хранить копию фреймбуфера в памяти МК, восстанавливая на экране пиксели, ранее затертые спрайтом, используя эту копию. Это не наш путь, так как даже в случае 12 битного цвета для этого нужны будет десятки килобайт оперативки, которых у нас нет.
2. Перед рисованием спрайта, считывать из фрейбуфера только затираемые точки и хранить только их. При адекватном размере спрайта, например 16*16 это потребует 384 байта оперативки, что уже приемлемо. Вот только читать что-либо из ST7735 у меня пока получается только программным SPI.
Если коротко, суть задачи, двигать спрайт незначительного размера по экрану на фоне картинки, например, загруженной с SD карты. Проблема в том, что SPI то один, и если переключаться на SD карту для чтения части картинки при каждом передвижении спрайта, спрайт будет двигаться очень медленно. Ну и считывать с SD нужно будет намного больше, чем с фреймбуфера. Для экрана 128*160 с 16-ти битным цветом и спрайта 16*16 с SD нужно будет считать либо 4Кб одним запросом, либо по 32 байта 16-тью запросами. Тогда как из фреймбуфера в самом худшем случае, мы считываем только 768 байт, при перемещении спрайта на 1 пиксель по вертикали или горизонтали всего 48 байт и по диагонали двумя запросами 48 и 45 байт. Выгода очевидна.
Да все перкрасно понятно о чем вы говорите, эти грабри разложены для всех желающих любителей 8-ми битных контроллеров. Тут даже 16 бит параллельный интерфейс не спасет все будет тормозить. По этой причине и перешел на 32 разрядный камень, только и это не панацея, сижу оптимизирую библиотеку. Вобщем тут нужен другой алгоритм, считываем с полями запаса на передвижение спрайта из CD область фона и делаем наложение спрайта, выводим в указанную область экрана. В большинстве TFT можно сделать активной любую область, тогда можно не заморачиваясь выводить байт за байтомуказав только (x, y). Помойму самый оптимальный алгоритм. Вот что пока получилось на частоте такта 72МГц . http://178.130.34.198/tft_002.mp4
Вобщем тут нужен другой алгоритм, считываем с полями запаса на передвижение спрайта из CD область фона и делаем наложение спрайта, выводим в указанную область экрана. В большинстве TFT можно сделать активной любую область, тогда можно не заморачиваясь выводить байт за байтомуказав только (x, y). Помойму самый оптимальный алгоритм. Вот что пока получилось на частоте такта 72МГц .
Про это все я в курсе. В свое время делал аналогичное на Z80. Указывается не только точка начала, но и точка конца (диагональной вершины прямоугольника), что я и писал выше.. На самом деле даже на этом тормозе, при частоте SPI в 500КГц я успевал выводить спрайт размером 16х16 по tearing. То есть с частотой 60 кадров в секунду. А так как AVR поддерживает частоту SPI до 8МГц, есть шансы увеличить размер спрайта до 64х64, что более чем достаточно. А спрайт 16х16 при этом можно успеть вывести вообще только за время Vsync. Другое дело, что стандартные библиотеки ардуино настолько тормозные, что с их помощью и 16x16 на 4МГц SPI не успеваю вывести. А tearing вывода на моем TFT вообще нет, то есть его можно контролировать только считывая регистр из ST7735, что стандартные библиотеки вообще не позволяют и мы снова возвращаемся к возможности считывания из ST7735.
Ну а SPI бибилиотеку при любом раскладе переписывать для симлексного обмена, чтобы все же использовать буферизацию SPI в AVR, и в функции передачи байта ждать только, пока не освободится буфер трансмиттера, а не пока байт будет передан.
Кстати, судя по видео, tearing Вы не контролируете. А для динамических объектов это необходимо.
На всякий случай уточню, что на Z80 я выводил на обычный телевизор с кинескопом, накладывая рекламу на выводимое изображение в формате NTSC - 60 полукадров в секунду.
Это вывод примитива в виде математической модели, наверное если бы это был спрайт, то выглядело бы немного лучше. Работать с видеопамятью так же как Z80 вы не сможете импользуя стандартные библтотеки. надо искать свои решения.
Это вывод примитива в виде математической модели, наверное если бы это был спрайт, то выглядело бы немного лучше. Работать с видеопамятью так же как Z80 вы не сможете импользуя стандартные библтотеки. надо искать свои решения.
А я не с видопамятью на Z80 работал, а с тупой платой без МК по последовательной шине. На плате было 4 РУ6 фреймбуфера. В него и писал. И на Z80 стандартных библиотек не было у меня вообще. Ноль. Все что было - сам же и написал.
А я не с видопамятью на Z80 работал, а с тупой платой без МК по последовательной шине. На плате было 4 РУ6 фреймбуфера. В него и писал. И на Z80 стандартных библиотек не было у меня вообще. Ноль. Все что было - сам же и написал.
Да ну, а что по вашему эти 4 РУ6. Это и есть видеопамять, а передней на мультиплексорах собран модуль реализующий алгоритм чтение - модификация - запись.
Читать фреймбуфер удалось, но плохо. На частоте в 1МГц читает стабильно. Но мне надо на 8МГц и через USART, чтобы была буферизация передачи. А уже на 4 МГц периодически, прет 0 вместо 1 от ST7735. Ну не тянет, бедолага, еще и USB2COM конвертер через 1К резистор (
Буду ждать Pro Mini из Китая. Продолжу эксперименты уже на ней.
Удалось таки )
https://goo.gl/photos/xTjyYRWKER5nRCcg7
Интересно какой модуль экрана использован и как подключен. Ну и глянуть бы на скетч, если не комтайна. Сам сейчас играюсь с похожим экранчиком по SPI, только управляющий STM32. Нужно будет реализовать сдвиг части экрана влево-право на пару пикселей. У используемой библиотеки такой функционал отсутствует, так что пока храню фреймбуфер в контроллере.
Удалось таки )
https://goo.gl/photos/xTjyYRWKER5nRCcg7
Неплохая работа, но больно уж мала матрица. Такое уже на 340х320 будет тормозить.
Интересно какой модуль экрана использован и как подключен. Ну и глянуть бы на скетч, если не комтайна.
Модуль ST7735. Я же с ним и экспериментирую. Скетч тут мало важен. Потому как библиотеку работы с TFT сам пишу. Но библиотеку нужно еще допилить перед выкладываением, а скетч вообще тут демонстрационный и тоже недописанный. Но если нужно:
001
#include "st7735_spi.h"
002
003
typedef
struct
sprite_st {
004
uint8_t *sprite;
005
uint8_t width;
006
uint8_t height;
007
uint8_t x;
008
uint8_t y;
009
uint8_t *filled;
010
} sprite_t;
011
012
sprite_t my_sprite;
013
014
void
draw_sprite(sprite_t *sprite)
015
{
016
st7735_spi_tft_read_rect_short(sprite->x, sprite->width, sprite->y, sprite->height, sprite->filled);
017
st7735_spi_tft_write_rect_short(sprite->x, sprite->width, sprite->y, sprite->height, sprite->sprite);
018
}
019
020
// vector Up=1, UpRight=3, Right=2, DownRight=6, Down=4, DownLeft=12, Left=8, UpLeft=9
021
022
uint8_t move_sprite(sprite_t *sprite, uint8_t vector)
023
{
024
uint8_t row[sprite->width*2], col[sprite->height*2], tmp[sprite->height*2];
025
uint8_t i;
026
027
switch
( vector )
028
{
029
case
1:
030
if
( !sprite->y )
return
(4);
031
st7735_spi_tft_read_rect_short(sprite->x, sprite->width, sprite->y-1, 1, row);
032
sprite->y--;
033
st7735_spi_tft_write_rect_short(sprite->x, sprite->width, sprite->y+sprite->height, 1, sprite->filled+sprite->width*(sprite->height-1)*2);
034
memmove(sprite->filled+sprite->width*2, sprite->filled, sprite->width*(sprite->height-1)*2);
035
st7735_spi_tft_write_rect_short(sprite->x, sprite->width, sprite->y, sprite->height, sprite->sprite);
036
memcpy(sprite->filled, row, sprite->width*2);
037
break
;
038
case
2:
039
if
( sprite->x+sprite->width>=ST7735_SCREEN_WIDTH )
return
(8);
040
st7735_spi_tft_read_rect_short(sprite->x+sprite->width, 1, sprite->y, sprite->height, col);
041
for
(i=0;i<sprite->height;i++)
042
{
043
tmp[i*2]=*(sprite->filled+sprite->width*2*i);
044
tmp[i*2+1]=*(sprite->filled+sprite->width*2*i+1);
045
}
046
st7735_spi_tft_write_rect_short(sprite->x, 1, sprite->y, sprite->height, tmp);
047
sprite->x++;
048
memmove(sprite->filled, sprite->filled+2, sprite->width*sprite->height*2-2);
049
st7735_spi_tft_write_rect_short(sprite->x, sprite->width, sprite->y, sprite->height, sprite->sprite);
050
for
(i=0;i<sprite->height;i++)
051
{
052
*(sprite->filled+(sprite->width-1)*2+sprite->width*2*i)=col[2*i];
053
*(sprite->filled+(sprite->width-1)*2+sprite->width*2*i+1)=col[2*i+1];
054
}
055
break
;
056
case
4:
057
if
( sprite->y+sprite->height>=ST7735_SCREEN_HEIGHT )
return
(1);
058
st7735_spi_tft_read_rect_short(sprite->x, sprite->width, sprite->y+sprite->height, 1, row);
059
st7735_spi_tft_write_rect_short(sprite->x, sprite->width, sprite->y, 1, sprite->filled);
060
sprite->y++;
061
memmove(sprite->filled, sprite->filled+sprite->width*2, sprite->width*(sprite->height-1)*2);
062
st7735_spi_tft_write_rect_short(sprite->x, sprite->width, sprite->y, sprite->height, sprite->sprite);
063
memcpy(sprite->filled+sprite->width*(sprite->height-1)*2, row, sprite->width*2);
064
break
;
065
case
8:
066
if
( !sprite->x )
return
(2);
067
st7735_spi_tft_read_rect_short(sprite->x-1, 1, sprite->y, sprite->height, col);
068
for
(i=0;i<sprite->height;i++)
069
{
070
tmp[i*2]=*(sprite->filled+(sprite->width-1)*2+sprite->width*2*i);
071
tmp[i*2+1]=*(sprite->filled+(sprite->width-1)*2+sprite->width*2*i+1);
072
}
073
sprite->x--;
074
st7735_spi_tft_write_rect_short(sprite->x+sprite->width, 1, sprite->y, sprite->height, tmp);
075
memmove(sprite->filled+2, sprite->filled, sprite->width*sprite->height*2-2);
076
st7735_spi_tft_write_rect_short(sprite->x, sprite->width, sprite->y, sprite->height, sprite->sprite);
077
for
(i=0;i<sprite->height;i++)
078
{
079
*(sprite->filled+sprite->width*2*i)=col[2*i];
080
*(sprite->filled+sprite->width*2*i+1)=col[2*i+1];
081
}
082
break
;
083
}
084
return
(0);
085
}
086
087
void
setup
()
088
{
089
static
const
uint8_t buf[] =
"\x80\x1F\x80\x1F\x80\x1F\x80\x1F\x80\x1F\x80\x1F\x80\x1F\x80\x1F\x00\x1F\x00\x1F\x00\x1F\x00\x1F\x00\x1F\x00\x1F\x00\x1F\x00\x1F\x00\x1F\x00\x1F\x00\x1F\x00\x1F\x00\x1F\x00\x1F\x00\x1F\x00\x1F\x00\x1F\x00\x1F\x00\x1F\x00\x1F\x00\x1F\x00\x1F\x00\x1F\x00\x1F\x00\x1F\x00\x1F\x00\x1F\x00\x1F\x00\x1F\x00\x1F\x00\x1F\x00\x1F\x00\x1F\x00\x1F\x00\x1F\x00\x1F\x00\x1F\x00\x1F\x00\x1F\x00\x1F\x00\x1F\x00\x1F\x00\x1F\x00\x1F\x00\x1F\x00\x1F\x00\x1F\x00\x1F\x80\x1F\x80\x1F\x80\x1F\x80\x1F\x80\x1F\x80\x1F\x80\x1F\x80\x1F"
;
090
uint8_t i;
091
092
st7735_spi_tft_init();
093
st7735_spi_tft_fill_rectangle(0, 128, 0, 160, ST7735_RED);
094
095
096
for
( i=3;i<128;i+=8)
097
{
098
st7735_spi_tft_fill_rectangle(i, 1, 0, 160, ST7735_GREEN);
099
}
100
for
( i=3;i<160;i+=8)
101
{
102
st7735_spi_tft_fill_rectangle(0, 128, i, 1, ST7735_GREEN);
103
}
104
105
my_sprite.sprite=buf;
106
my_sprite.x=50;
107
my_sprite.y=60;
108
my_sprite.width=8;
109
my_sprite.height=8;
110
my_sprite.filled=malloc(my_sprite.width*my_sprite.height*2);
111
draw_sprite(&my_sprite);
112
return
;
113
}
114
115
void
loop
()
116
{
117
static
uint8_t dirx=2, diry=1;
118
uint8_t rc;
119
120
rc=move_sprite(&my_sprite, dirx);
121
if
( rc ) dirx=rc;
122
rc=move_sprite(&my_sprite, diry);
123
if
( rc ) diry=rc;
124
delay(2);
125
return
;
126
}
Удалось таки )
https://goo.gl/photos/xTjyYRWKER5nRCcg7
Неплохая работа, но больно уж мала матрица. Такое уже на 340х320 будет тормозить.
А в чем разница? Хоть 1920x1080. Обновляю я ведь, в любом случае, только спрайт и то, что затирал спрайт. Тут скорость от размера спрайта зависит, а не от размера матрицы. На текущей Delay(2) пришлось ставить, чтобы спрайт хоть увидеть можно было )
Нужно будет реализовать сдвиг части экрана влево-право на пару пикселей. У используемой библиотеки такой функционал отсутствует, так что пока храню фреймбуфер в контроллере.
Из известных мне контроллеров TFT я не знаю ни один, умеющий смещать экран аппаратно. А программная реализация требует или хранения всего фреймбуфера в МК, что, порой, вообще не реально, либо того, что делаю в этой теме я: читаю по аппаратному SPI из фреймбуфера контроллера TFT по той же шине, что и передаю ему команды и данные. Больше всего возни именно с чтением фреймбуфера получилось.
А покажите функцию чтения пикселя. Я еще сам не пробовал, только читал у забугорных товарищей, что там еще и резистор дополнительный впаивают что бы читать данные.
Удалось таки )
https://goo.gl/photos/xTjyYRWKER5nRCcg7
Неплохая работа, но больно уж мала матрица. Такое уже на 340х320 будет тормозить.
А в чем разница? Хоть 1920x1080. Обновляю я ведь, в любом случае, только спрайт и то, что затирал спрайт. Тут скорость от размера спрайта зависит, а не от размера матрицы. На текущей Delay(2) пришлось ставить, чтобы спрайт хоть увидеть можно было )
В том то и дело что для отображения спрайта того же размера понадобится буфер немного большего размера.
А покажите функцию чтения пикселя. Я еще сам не пробовал, только читал у забугорных товарищей, что там еще и резистор дополнительный впаивают что бы читать данные.
Да нет ни чего проще, задаем область спрайта (регистры 2Ah, 2Bh), регистр чтения пикселя(регистр 2Eh) и считываем данные, в этом случае два байта на пиксель если шина SPI или 8 бит и один байт если 16бит, умноженные на Hsprite * Vsprite.
ЗдОрово, если все так просто. А я вот смотрел на форуме AdaFruits (разработчики модулей и библиотеки к ним), та вот что сами разрабы сказали:
Re: 1.8" - ST7735 - reading a pixel from RAM
by adafruit on Sun Dec 11, 2011 5:29 pm
Да нет ни чего проще, задаем область спрайта (регистры 2Ah, 2Bh), регистр чтения пикселя(регистр 2Eh) и считываем данные, в этом случае два байта на пиксель если шина SPI или 8 бит и один байт если 16бит, умноженные на Hsprite * Vsprite.
Вы очень сильно не правы. Все существенно сложнее. Чуть позже напишу подробней.
О, жду с нетерпением ;)
Да нет ни чего проще, задаем область спрайта (регистры 2Ah, 2Bh), регистр чтения пикселя(регистр 2Eh) и считываем данные, в этом случае два байта на пиксель если шина SPI или 8 бит и один байт если 16бит, умноженные на Hsprite * Vsprite.
Вы очень сильно не правы. Все существенно сложнее. Чуть позже напишу подробней.
Может обработка считанного дампа и сложнее, а вот считать определенную область нет проще чем предложено. Иначе вы даташит читать не умеете. Такой метод особенно эффективен в случае если в м/к есть DMA. Почитайте на досуге даташит повнимательней. Читать по пиксельно, если вы о таком режиме, это добавлять лишние циклы, попробуйте вариант обработки спрайтов с использованием массивов. Понятно что надо иметь спрайт фона выделенной области и сам накладывамый спрайт. И не подлежит сомнению что перемещение спрайта происходит путем записи логически сложенного куска фона и самого спрайта. Помойму нет более быстрого вывода. И есть некое замечание, вы выделили память под буфер, а вот по окончании работы надо предусмотреть освобождение резервирования памяти, чтобы в последствии память можно было испоользовать для любых дручих целей.
А покажите функцию чтения пикселя. Я еще сам не пробовал, только читал у забугорных товарищей, что там еще и резистор дополнительный впаивают что бы читать данные.
Одним резистром там не обойдешься. Но чтобы понять, почему так, необходимо описать пройденный мной путь.
Для начала я повертел готовые библиотеки и убедился, что они жутко медленные. Естественно, захотелось понять, почему. Ну да, код не слишком оптимален, но вроде бы не настолько. При ближайшем рассмотрении, обнаружилось, что проблема то в реализации аппаратного SPI на ATMega328 и иже с ней. Дело в том, что SPI там реализован вообще без буферизации передатчика. И инициировать передачу следующего байта можно только по окончании передачи текущего. Из-за этого, попытка переключиться с 4МГц тактовой SPI на 8МГц не давала никакого видимого эффекта.
Однако, в AVR есть так же Master SPI mode у USART. Причем USART по жизни имеет буфера и для передатчика, и для приемника. А значит, позволяет передавать по SPI без пауз между байтами. Но, на имеющихся у меня Arduino UNO и Arduino Nano выход RX соединен через 1К резистор с выходом TX ATMega8U2. То есть, хоть в лоб, хоть по лбу, имеет устойчивые 5 вольт с током до 5ма (при нуле) на входе RX ATMega128.
Поэтому, кроме китайского модуля согласования уровней 3.3 и 5 вольт, пришлось городить целую схему:

Один из выводов управляет, в каком направлении работает SPI шина. Когда на нем высокий уровень и на выходе TX низкий уровень, транзистор Q2 открыт и имеем низкий уровень на входе ST7735 (через преобразователь уровней). Когда на R/W высокий уровень, то имеем низкий уровень на входе ST7735, за счет 10К подтягивающего резистра на 3.3 вольта в преобразователе уровней.
Транзистор Q1 понадобился только из-за наличия резистора 1К к ATMega8U2, о чем писалось выше. Предполагаю, что на Arduino Pro Mini без этого транзистора вполне можно обойтись.
Теперь про чтение. Проблема в том, что в не зависимости от того, в каком формате мы отправляем цвет в ST7735, в его фреймбуфере он всегда хранится в 18-ти битном виде. И читать оттуда позволяется тоже исключительнов 18-ти битном виде. Так как 18-ти битный цвет занимает три байта на пиксель, теряя 6 бит в никуда, то и чтение его без преобразования потребовало бы буфера в полтора раза больше. Кроме того, его все равно пришлось бы потом перекодировать в 16-ти битный цвет. В связи с этим, я и реализовал при чтении перекодирование обратно в 16-ти битный цвет на лету.
Функцию я прилагаю, но толку с нее, без остального кода мало. А приведу весь код в порядок я не раньше следующих выходных. Во тогда и выложу на гитхаб. А сюда пихать около тысячи строк кода, на мой взгляд, как-то неправильно.
Вот функция:
001
void
st7735_spi_read_frame_memory(uint8_t *data, uint8_t status, uint8_t buffer_length)
002
{
003
uint8_t firstb, readb, lastb;
004
005
asm volatile ( "; \n\t \
006
cpi %[status], 0 ;1 \n\t \
007
breq FLUSH_RECEIVER%= ;1/2 \n\t \
008
clr %[status] ;1 \n \
009
; Rx = data, RxBuf = empty, RxEDR = data \n \
010
WAIT_UDREn_FOR_DATA%=: \n\t \
011
lds __tmp_reg__, %[UCSRnA_addr] ;2 \n\t \
012
sbrs __tmp_reg__, %[UDREn_bit] ;1/2 \n\t \
013
rjmp WAIT_UDREn_FOR_DATA%= ;2 \n \
014
sts %[UDRn_addr], %[filler] ;2 (8) \n \
015
; Rx = data, RxBuf = data, RxEDR = data \n\t \
016
WAIT_RXCn_FOR_DATA%=: \n\t \
017
lds __tmp_reg__, %[UCSRnA_addr] ;2 \n\t \
018
sbrs __tmp_reg__, %[RXCn_bit] ;1/2 \n\t \
019
rjmp WAIT_RXCn_FOR_DATA%= ;2 \n\t \
020
lds %[readb], %[UDRn_addr] ;2 \n \
021
; Rx = data, RxBuf = empty, RxEDR = data \n\t \
022
cpi %[status], 0 ;1 (9) \n\t \
023
brlt COLOR_LAST_BYTE%= ;1/2 \n\t \
024
breq COLOR_FIRST_BYTE%= ;1/2 \n\t \
025
lsl %[readb] ;1 \n\t \
026
swap %[readb] ;1 \n\t \
027
mov %[lastb], %[readb] ;1 \n\t \
028
andi %[readb], 0b00000111 ;1 \n\t \
029
or %[readb], %[firstb] ;1 \n\t \
030
ldi %[status], 0xFF ;1 \n\t \
031
RJMP CONTINUE_LOOP%= ;2 (20) \n \
032
COLOR_FIRST_BYTE%=: \n\t \
033
lsl %[readb] ;1 \n\t \
034
lsl %[readb] ;1 \n\t \
035
andi %[readb], 0b11111000 ;1 \n\t \
036
mov %[firstb], %[readb] ;1 \n\t \
037
inc %[status] ;1 \n\t \
038
RJMP WAIT_UDREn_FOR_DATA%= ;2 (17) \n \
039
COLOR_LAST_BYTE%=: \n\t \
040
lsr %[readb] ;1 \n\t \
041
andi %[lastb], 0b11100000 ;1 \n\t \
042
or %[readb], %[lastb] ;1 \n\t \
043
clr %[status] ;1 \n \
044
CONTINUE_LOOP%=: \n\t \
045
com %[readb] ;1 \n\t \
046
st %a[data]+, %[readb] ;2 \n\t \
047
dec %[buffer_length] ;1 \n\t \
048
brne WAIT_UDREn_FOR_DATA%= ;1/2 \n\t \
049
rjmp EXIT_ASM%= ;2 \n \
050
FLUSH_RECEIVER%=: \n\t \
051
lds __tmp_reg__, %[UCSRnA_addr] ;2 \n\t \
052
sbrs __tmp_reg__, %[UDREn_bit] ;1/2 \n\t \
053
rjmp FLUSH_RECEIVER%= ;2 \n\t \
054
sts %[UCSRnA_addr], __zero_reg__ ;2 \n\t \
055
ldi %[readb], %[rdmemcmd] ;1 \n\t \
056
cli ;1 \n\t \
057
sts %[UDRn_addr], %[readb] ;2 \n \
058
; Tx = any, TxBuf = rdmemcmd \n \
059
; Rx disabled \n \
060
WAIT_UDREn_FOR_COMMAND%=: \n\t \
061
lds __tmp_reg__, %[UCSRnA_addr] ;2 \n\t \
062
sbrs __tmp_reg__, %[UDREn_bit] ;1/2 \n\t \
063
rjmp WAIT_UDREn_FOR_COMMAND%= ;2 \n\t \
064
cbi %[dc_port], %[dc_pin] ;2 \n\t \
065
ldi %[lastb], %[TXCn_clear] ;1 \n \
066
WAIT_UDREn_BEFORE_DUMMY%=: \n\t \
067
lds __tmp_reg__, %[UCSRnA_addr] ;2 \n\t \
068
sbrs __tmp_reg__, %[UDREn_bit] ;1/2 \n\t \
069
rjmp WAIT_UDREn_BEFORE_DUMMY%= ;2 \n\t \
070
sts %[UDRn_addr], %[filler] ;2 \n\t \
071
sts %[UCSRnA_addr], %[lastb] ;2 \n \
072
; Tx = rdmemcmd, TxBuf = dummy \n \
073
; Rx disabled \n \
074
WAIT_UDREn_FOR_DUMMY%=: \n\t \
075
lds __tmp_reg__, %[UCSRnA_addr] ;2 \n\t \
076
sbrs __tmp_reg__, %[UDREn_bit] ;1/2 \n\t \
077
rjmp WAIT_UDREn_FOR_DUMMY%= ;2 \n \
078
; Tx = dummy, TxBuf = empty \n \
079
; Rx disabled \n \
080
sbi %[dc_port],%[dc_pin] ;2 \n\t \
081
cbi %[rw_port],%[rw_pin] ;2 \n\t \
082
sei ;1 \n\t \
083
ldi %[status], %[RX_Enable] ;1 \n\t \
084
WAIT_TXCn_FOR_DUMMY%=: \n\t \
085
lds __tmp_reg__, %[UCSRnA_addr] ;2 \n\t \
086
sbrs __tmp_reg__, %[TXCn_bit] ;1/2 \n\t \
087
rjmp WAIT_TXCn_FOR_DUMMY%= ;2 \n\t \
088
sts %[UCSRnA_addr], %[lastb] ;2 \n\t \
089
; Tx = dummy, TxBuf = dummy \n \
090
; Rx disabled \n\t \
091
sts %[UCSRnB_addr], %[status] ;2 \n\t \
092
clr %[status] ;1 \n \
093
rjmp WAIT_UDREn_FOR_DATA%= ;2 \n \
094
EXIT_ASM%=: \n \
095
"
096
: [data]
"+b"
(data), [status]
"+d"
(status), [buffer_length]
"+r"
(buffer_length),
097
[firstb]
"=r"
(firstb), [readb]
"=d"
(readb), [lastb]
"=d"
(lastb)
098
: [filler]
"r"
(0xFF), [rdmemcmd]
"I"
(_ST7735_SPI_COMMAND_RAMRD_CODE_),
099
[UCSRnA_addr]
"M"
(_ST7735_SPI_UCSRnA_ADDRESS_),
100
[UDREn_bit]
"I"
(UDRE0),
101
[UCSRnB_addr]
"M"
(_ST7735_SPI_UCSRnB_ADDRESS_),
102
[RX_Enable]
"M"
((0<<RXCIE0)|(0<<TXCIE0)|(0<<UDRIE0)|(1<<RXEN0)|(1<<TXEN0)),
103
[RX_Disable]
"M"
((0<<RXCIE0)|(0<<TXCIE0)|(0<<UDRIE0)|(0<<RXEN0)|(1<<TXEN0)),
104
[TXCn_bit]
"I"
(TXC0),
105
[RXCn_bit]
"I"
(RXC0),
106
[TXCn_clear]
"M"
(1<<TXC0),
107
[UDRn_addr]
"M"
(_ST7735_SPI_UDRn_ADDRESS_),
108
[dc_port]
"I"
(PIN_TO_PORTn_OFFSET(ST7735_SPI_DC_PIN_NO)),
109
[dc_pin]
"I"
(PIN_TO_PINn_BIT(ST7735_SPI_DC_PIN_NO)),
110
[rw_port]
"I"
(PIN_TO_PORTn_OFFSET(ST7735_SPI_RW_PIN_NO)),
111
[rw_pin]
"I"
(PIN_TO_PINn_BIT(ST7735_SPI_RW_PIN_NO))
112
:
"cc"
);
113
return
(status);
114
}
Да нет ни чего проще, задаем область спрайта (регистры 2Ah, 2Bh), регистр чтения пикселя(регистр 2Eh) и считываем данные, в этом случае два байта на пиксель если шина SPI или 8 бит и один байт если 16бит, умноженные на Hsprite * Vsprite.
Вы очень сильно не правы. Все существенно сложнее. Чуть позже напишу подробней.
вы даташит читать не умеете.
Из даташит:
10.1.22 RAMRD (2Eh): Memory Read
The data color coding is fixed to 18-bit in reading function.
И кто из нас не умеет читать? )))
И это не считая того, что:
1. Первый принимаемый байт пустой
2. Между приемом команды и началом передачи ST7735 пропускает один такт синхронизации и данные принимаются со смещением в 1 бит.
3. Для коректного завершения передачи необходимо не менее, чем на 350 нс передернуть CS
4. Все это удовольствие еще сопровождается выставлением D/C так, что бы до передачи последнего бита команды на нем был низкий уровень, а до передачи или приема последнего бита данных - высокий.
Вы бы сначала сами написали весь код, а потом бы уже учили тех, кто этот код не только написал, но и отладил.
Забыл добавить, что в данной схеме принимаемый по SPI код получается инвертированным, что следует учитывать в программе (45-ая строка функции). Все тестировалось на частоте SPI 8МГц. То есть, между приемом или передачей одного байта у меня было только 16 тактов процессора. Потому и свалился на ассемблер.
Да нет ни чего проще, задаем область спрайта (регистры 2Ah, 2Bh), регистр чтения пикселя(регистр 2Eh) и считываем данные, в этом случае два байта на пиксель если шина SPI или 8 бит и один байт если 16бит, умноженные на Hsprite * Vsprite.
Вы очень сильно не правы. Все существенно сложнее. Чуть позже напишу подробней.
вы даташит читать не умеете.
Из даташит:
10.1.22 RAMRD (2Eh): Memory Read
The data color coding is fixed to 18-bit in reading function.
И кто из нас не умеет читать? )))
И это не считая того, что:
1. Первый принимаемый байт пустой
2. Между приемом команды и началом передачи ST7735 пропускает один такт синхронизации и данные принимаются со смещением в 1 бит.
3. Для коректного завершения передачи необходимо не менее, чем на 350 нс передернуть CS
4. Все это удовольствие еще сопровождается выставлением D/C так, что бы до передачи последнего бита команды на нем был низкий уровень, а до передачи или приема последнего бита данных - высокий.
Вы бы сначала сами написали весь код, а потом бы уже учили тех, кто этот код не только написал, но и отладил.
Ну я например спокойно заисываю двубайтные значения цветов без дурацкого вида преобразований RGB18 -> RGB16. Есть такой регистр как 3Ah он определяет формат ввода вывода пикселя, он дает возможность не заниматься ананизмом.
Из даташит:
10.1.22 RAMRD (2Eh): Memory Read
The data color coding is fixed to 18-bit in reading function.
И кто из нас не умеет читать? )))
Ну я например спокойно заисываю двубайтные значения цветов без дурацкого вида преобразований RGB18 -> RGB16. Есть такой регистр как 3Ah он определяет формат ввода вывода пикселя, он дает возможность не заниматься ананизмом.
Вы утверждаете, что в даташит ложь? )))
Писать в ST7735 легко можно и в 12-ти битном цвете и 16-ти битном. Но читать он позволяет только в 18-ти битном. Могу сделать вывод, что Вы никогда не читали из него фреймбуфер, раз утверждаете обратное.
P.S. Спорить в лом. Код и схему подключения в студию. Рабочий. Чтобы можно было проверить.
Функцию записи в фреймбуфер я уже вроде бы публиковал, но не в этой теме. Повторю, не гордый:
01
/*-----------------12.09.2016 13:17-----------------
02
* Buffer send function:
03
* - status not updated
04
* - status have bits according to st7735_spi_buffer_status_t
05
* - function don't check dc_bit_set bit in status
06
* - send command with data => status = 0
07
* - send only data => status=1 or is_data_only=1
08
* - buffer_length=0 allow to send 256 bytes in buffer
09
* - function don't check buffer pointer; be carefull!
10
* --------------------------------------------------*/
11
12
void
st7735_spi_send_buffer(uint8_t *data, uint8_t status, uint8_t buffer_length)
13
{
14
uint8_t readb;
15
16
asm volatile ( "; \n\t \
17
ld %[readb], %a[data]+ ;2 \n \
18
WAIT_UDREn_ON_START%=: \n\t \
19
lds __tmp_reg__, %[UCSRnA_addr] ;2 \n\t \
20
sbrs __tmp_reg__, %[UDREn_bit] ;1/2 \n\t \
21
rjmp WAIT_UDREn_ON_START%= ;2 \n\t \
22
cli ;1 \n\t \
23
sts %[UDRn_addr], %[readb] ;2 \n\t \
24
ld %[readb], %a[data]+ ;2 \n\t \
25
sbrc %[status], 0 ;1/2 \n\t \
26
rjmp WAIT_UDREn_FOR_DATA%= ;2 \n \
27
; Set DC
for
command \n \
28
WAIT_UDREn_FOR_COMMAND%=: \n\t \
29
lds __tmp_reg__, %[UCSRnA_addr] ;2 \n\t \
30
sbrs __tmp_reg__, %[UDREn_bit] ;1/2 \n\t \
31
rjmp WAIT_UDREn_FOR_COMMAND%= ;2 \n\t \
32
cbi %[port],%[pin] ;2 \n\t \
33
dec %[buffer_length] ;1 \n\t \
34
BREQ EXIT_ASM%= ;1 \n\t \
35
ld %[status], %a[data]+ ;2 \n\t \
36
sts %[UDRn_addr], %[readb] ;2 (16) \n\t \
37
mov %[readb], %[status] ;1 \n \
38
WAIT_UDREn_FOR_DATA%=: \n\t \
39
lds __tmp_reg__, %[UCSRnA_addr] ;2 \n\t \
40
sbrs __tmp_reg__, %[UDREn_bit] ;1/2 \n\t \
41
rjmp WAIT_UDREn_FOR_DATA%= ;2 \n\t \
42
sbi %[port],%[pin] ;2 \n\t \
43
sei ;1 (8) \n \
44
DATA_LOOP%=: \n\t \
45
dec %[buffer_length] ;1 \n\t \
46
BREQ EXIT_ASM%= ;1 \n \
47
WAIT_UDREn_IN_DATA_LOOP%=: \n\t \
48
lds __tmp_reg__, %[UCSRnA_addr] ;2 \n\t \
49
sbrs __tmp_reg__, %[UDREn_bit] ;1/2 \n\t \
50
rjmp WAIT_UDREn_IN_DATA_LOOP%= ;2 (5) \n\t \
51
sts %[UDRn_addr], %[readb] ;2 (16) \n\t \
52
ld %[readb], %a[data]+ ;2 \n\t \
53
rjmp DATA_LOOP%= ;2 (12) \n \
54
EXIT_ASM%= : \n\t \
55
sei ;1 \n\t \
56
"
57
: [data]
"+b"
(data), [status]
"+d"
(status), [buffer_length]
"+r"
(buffer_length),
58
[readb]
"=r"
(readb)
59
: [UCSRnA_addr]
"M"
(_ST7735_SPI_UCSRnA_ADDRESS_),
60
[UDREn_bit]
"I"
(UDRE0), [UDRn_addr]
"M"
(_ST7735_SPI_UDRn_ADDRESS_),
61
[port]
"I"
(PIN_TO_PORTn_OFFSET(ST7735_SPI_DC_PIN_NO)),
62
[pin]
"I"
(PIN_TO_PINn_BIT(ST7735_SPI_DC_PIN_NO))
63
:
"cc"
);
64
}
Писать в ST7735 легко можно и в 12-ти битном цвете и 16-ти битном. Но читать он позволяет только в 18-ти битном.
А в чём вообще смысл таких извращений? Зачем все эти манипуляции с убогим ТФТ на убогом МК? Что это даёт? В чём тайный смысл, если отвратный результат заведомо известен?
Писать в ST7735 легко можно и в 12-ти битном цвете и 16-ти битном. Но читать он позволяет только в 18-ти битном.
Кстати, да, вот кусочек из ДШ:
Note2: The Command 3Ah should be set at 55h when writing 16-bit/pixel data into frame memory, but 3Ah should be re-set to 66h when reading pixel data from frame memory
Типа нас заставляют перейти в 18бит при чтении.
А в чём вообще смысл таких извращений? Зачем все эти манипуляции с убогим ТФТ на убогом МК? Что это даёт? В чём тайный смысл, если отвратный результат заведомо известен?
А в чем смысл задавать убогие вопросы и быть отвратным троллем на форуме? )))
Вопрос нормальный и очевидный. Отсутствие профита видно ещё на простейших функциях работы с ТФТ. А ваши портянки на АСМе это ваще смертельный приговор какому либо результату. Если вопрос этих телодвижений чисто теоретический, то это одно, иначе это просто пустая трата времени. Поэтому и спросил, в чём тайный смысл?
А ваши портянки на АСМе это ваще смертельный приговор какому либо результату.
Поэтому и спросил, в чём тайный смысл?
Только результат почему то есть на видео выше и меня вполне устраивает )
Сын хочет написать упрощенный аналог арканоида на ардуине. Стандартные библиотеки слишком медленные для такой игрушки. А моя вполне в состоянии обеспечить требуемую производительность. Я даже при полном выводе всего фреймбуфера добился почти 25 кадров в секунду. И спрайт на видео у меня летает с задержкой в 2 миллисекунды на цикл. Причем летает даже быстрее, чем нужно для этого арканоида, хотя и не оптимально. Для пробы реализовал только сдвиг или по горизонтали, или по вертикали. Если добавить диагональные смещения, то скорость увеличится еще в полтора раза. Значит запас на прочий просчет еще есть, и не маленький.
А в чём вообще смысл таких извращений? Зачем все эти манипуляции с убогим ТФТ на убогом МК? Что это даёт? В чём тайный смысл, если отвратный результат заведомо известен?
А в чем смысл задавать убогие вопросы и быть отвратным троллем на форуме? )))
Ну может с чтением вы и правы, но вот то что вы делаете полный изврат. Писал уже, берем область фона перекрывающий уже записанный спрайт, накладываем спрайт с заданным смещением, записывам в память TFT. Точка!!! остальное АНАНИЗМ. Чем вы и занимаетесь с пристрастием. Сравните вашу хвостатую камету по вашему алгоритму, когда след тянется на пол экрана и то что получается по может и идиотскому но более оптимальному агоритму. И не маловажный вопрос, что еще может делать м/к в момент когда гоняет квадратик по экрану, ответ очевиден, ни чего. И нахрена козе баян.
ptr , интересеная у вас реализация, использовать видео ОЗУ контроллера дисплея для передвижения кусочка изображения, надобности у меня нехватило это реализовать. Снимите видос на ютуб что у вас получилось, по перемещению небольшой картинки. Для себя пока, для немного похожей задачи, перешёл на Мегу , там объем ОЗУ позволил создать приемлемый видеобуфер из переменных byte для запоминания нескольких графиков для их последующего смещения.
немного не догнал, чем вам scroll из даташита не угодил? это же как раз перемещение куска изображения, разве что прямоугольного.
А вот если же двигать маленькую картинку на большой картинке-фоне , то либо весь фон надо загнать в ОЗУ , либо сохранять участок картинки-фона , куда будет перемещатся маленькая картинка, чтоб после её ухода, заново восстановить на этом месте фон. Тогда получиться нам нужна видеопамять по размеру чуть больше маленькой перемещаемой катринки.
с mcufriend kvb пока небыло времени разобраться, но похоже там scroll реализован , походу софтово, но как-то уж быстро
https://www.youtube.com/watch?v=FB_ZEFw86OU (на 9сек диагональное смещение)
https://youtu.be/w1XNoqz0UNY?t=36 (сдвиг слов и картинки тоже)
diger67, согласен, но надо добить именно этот способ до конца. все способы уникальны и интересны в своем роде. Надо тогда уж отдельную тему создавать по реализованным спрайтам, где будет сравнение способов, но для начала их надо практически реализовать. У меня нет что показать реализацию передвижения спрайта на ардуине, и посему поддерживаю пытливого ума ptr , который вот вот покажет на сколько fps можно выйти его способом.
хотелось бы узреть передвижение именно картинки , а не монотонного объекта когда достаточно дорисовывать фронт и затирать тыл для его визуального перемещения дабы не перерисовывать весь объект заново.
slider,так предлогал ему такой вариант. Ведь все будет работать гараздо быстрее, можно на меге еще 64к прицепить. Создать зеркало ОЗУ TFT, а заготовки разместить во внешней flash типа 25Qxxx. По сути один раз подготавливаем заплатку и гоняем ее так как на надо. Если графики много и она шыбко шустра, то лучше использовать ARM. Я подсел на STM, вот жду когда приедет F7, там уже вроде можно задействовать D2DMA не только с LTDC. Что сделает вывод на TFT еще эффективнее.
Ведь все будет работать гараздо быстрее
можно на меге
Ну не позорьтесь. Даже на меге тратить больше 40 килобайт RAM - непозволительная роскошь. И на Ваших любимых STM32 столько не всегда найдется. Получить несколько процентов прироста скорости для сферического коня в вакууме (где код?) ценой увеличения потребления RAM программой на порядки - смешно )
немного не догнал, чем вам scroll из даташита не угодил? это же как раз перемещение куска изображения, разве что прямоугольного.
Тем, что его там нет. Я вообще не встречал ни одиного контроллера tft такое умеющего.
А вот если же двигать маленькую картинку на большой картинке-фоне , то либо весь фон надо загнать в ОЗУ , либо сохранять участок картинки-фона , куда будет перемещатся маленькая картинка, чтоб после её ухода, заново восстановить на этом месте фон. Тогда получиться нам нужна видеопамять по размеру чуть больше маленькой перемещаемой катринки.
Так ведь именно так и сделано )
Так ведь именно так и сделано )
А я б не заморачивался с переводом 18 в 16 бит при считывании фона. Проще запомнить фон в 18 битах, а в момент восстановления фона (когда затираем старый спрайт) переключиться тоже на 18 бит и залить то, что ранее считали один к одному.
Ведь все будет работать гараздо быстрее
можно на меге
Ну не позорьтесь. Даже на меге тратить больше 40 килобайт RAM - непозволительная роскошь. И на Ваших любимых STM32 столько не всегда найдется. Получить несколько процентов прироста скорости для сферического коня в вакууме (где код?) ценой увеличения потребления RAM программой на порядки - смешно )
И ответте себе на вопрос, откуда быстрее считать данные из Flash или из TFT.
Из программной флеш памяти МК - быстрее. Но ее и так мало. Если речь же именно о файле, то есть о чтении с SD карты, то, очевидно, что из фреймбуфера считать существенно быстрее, чем с карточки.
Потому что:
1. Общение и с SD и с фреймбуфером осуществляется по одному и тому же SPI на одной и той же скорости.
2. При чтении из фреймбуфера у меня прямая адресация, при чтении же с SD все равно в ней читается целиком блок. Хорошо еще, если найдете старую карточку, этак на 32-128 мегабайт. На современных блок будет уже мегабайтного размера или более. Мне так ни разу еще не попадалась SD карточка, способная прочитать один байт быстрее, чем за одну миллисекунду.
3. Даже если файл не фрагментирован, он все равно или будет занимать больше одного кластера FAT, или будет лишние операции и расход памяти на FAT32.
3. Общение с SD картой требует дополнительных затрат памяти на открытые файлы и структуры FAT. Причем совсем не маленьких.
Итого, чтение из SD никак не может быть быстрее, а будет медленней. И при этом всяко потребует больше оперативной памяти, чем чтение из фреймбуфера.
А я б не заморачивался с переводом 18 в 16 бит при считывании фона. Проще запомнить фон в 18 битах, а в момент восстановления фона (когда затираем старый спрайт) переключиться тоже на 18 бит и залить то, что ранее считали один к одному.
[/quote]
В полтора раза больше расход оперативной памяти. По мне так, лучше потерять буквально несколько байт программной памяти и выиграть при этом в разы больше оперативной.
Из программной флеш памяти МК - быстрее. Но ее и так мало. Если речь же именно о файле, то есть о чтении с SD карты, то, очевидно, что из фреймбуфера считать существенно быстрее, чем с карточки.
Есть Flahs память с интерфейсом 8 бит, читается она как обычная RAM. Не пробовали смотреть в эту сторону. Я к примеру сейчас разбираюсь с samsung flahs на 1Gb. У нее как раз 8 бит шина. Или 25Qxxx, она работает по SPI и считывать надо будет два а не три байта на пиксель. Надо посчитать, может будет выйгрышь.
Из программной флеш памяти МК - быстрее. Но ее и так мало. Если речь же именно о файле, то есть о чтении с SD карты, то, очевидно, что из фреймбуфера считать существенно быстрее, чем с карточки.
Есть Flahs память с интерфейсом 8 бит, читается она как обычная RAM. Не пробовали смотреть в эту сторону. Я к примеру сейчас разбираюсь с samsung flahs на 1Gb. У нее как раз 8 бит шина. Или 25Qxxx, она работает по SPI и считывать надо будет два а не три байта на пиксель.
Это у вас есть вероятность манёвра, при любых раскладах. У ptr всё сложно и плохо, ему там и дёргаться особо некуда, "Шаг вправо, шаг влево..."(с).
В полтора раза больше расход оперативной памяти. По мне так, лучше потерять буквально несколько байт программной памяти и выиграть при этом в разы больше оперативной.
Вы как вчера родились, но учились в прошлом веке. Нахрен никому не интересна ваша "экономия" памяти, когда она копейки стоит, если разговор идёт о повышении производительности. Или вопросы оптимизации вами никогда в жизни не рассматривались?
Из программной флеш памяти МК - быстрее. Но ее и так мало. Если речь же именно о файле, то есть о чтении с SD карты, то, очевидно, что из фреймбуфера считать существенно быстрее, чем с карточки.
Есть Flahs память с интерфейсом 8 бит, читается она как обычная RAM. Не пробовали смотреть в эту сторону. Я к примеру сейчас разбираюсь с samsung flahs на 1Gb. У нее как раз 8 бит шина. Или 25Qxxx, она работает по SPI и считывать надо будет два а не три байта на пиксель.
Это у вас есть вероятность манёвра, при любых раскладах. У ptr всё сложно и плохо, ему там и дёргаться особо некуда, "Шаг вправо, шаг влево..."(с).
Можно взять не 328 мегу а 2560,ну да тормозно конечно будет, но появляется хардварная шина на 8 бит. Что дает поле для подергивания в сторону. Я то при работе с TFT смотрю в сторону ARM по причине более высокоо быстодействия и наличия, непобоюсь это сказать, огромного выборв в периферии. У F7 уже появился кэшь ядра и 99% команд выполняются за один такт.
Есть Flahs память с интерфейсом 8 бит, читается она как обычная RAM. Не пробовали смотреть в эту сторону. Я к примеру сейчас разбираюсь с samsung flahs на 1Gb. У нее как раз 8 бит шина. Или 25Qxxx, она работает по SPI и считывать надо будет два а не три байта на пиксель. Надо посчитать, может будет выйгрышь.
Я сейчас на ATMega328. У меня ног уже не хватит для параллельного интерфейса.
Вот когда из Китая прийдет STM32, тогда можно будет поэкспериментировать.
Вы как вчера родились, но учились в прошлом веке. Нахрен никому не интересна ваша "экономия" памяти, когда она копейки стоит, если разговор идёт о повышении производительности. Или вопросы оптимизации вами никогда в жизни не рассматривались?
Вы как с Луны свалились. За сколько копек вы расширите память в моей Ардуино UNO? Я готов целых 100 копеек Вам заплатить, если вы увеличите ее хотя бы на 16К! )))
Можно взять не 328 мегу а 2560
Смысла нет вообще. Ни 2560, ни, тем более, Due просто не рентабельны. В том смысле, что на ESP или STM32 решение будет дешевле не в ущерб функциональности