Комментарии 30
На этом контроллере stm32f103c8t6 максимальная скорость передачи по SPI как раз без DMA.
Да и экран маленький, не надо ему столько. Попробуйте лучше с ILI 9341 SPI. И вытянуть 30 ФПС.
Кстати да тоже столкнулся с таким что ногодрыгом получается быстрее, ну и на самом деле для этого дисплея дма точно без надобности.
Если нагрузить МК параллельно вычислениями и работой с другой периферией, то с DMA будет быстрее, а так да, можно и на прерываниях сделать, работать будет так же.
Насчет ILI 9341 давно думал, надо заказать китайцам, но думаю 30 фпс по spi при полном обновлении экрана врятли получится, а вот через параллельный интерфейс вполне возможно.

Не отрицаю. Но я так и оставил ногодрыг у себя, а всю остальную периферию на дма выкинул. Я сейчас не помню точно, но вышло что скорость по дма была ниже чем мне требовалось

подтверждаю и хочу добавить что порой хватает только процессора на всё:
Я на подобном OLED дисплее и stm32f100 cделал «умные» часы в корпусе обычных наручных круглых часов: вообще всё реализовал без DMA и питались они от обычной часовой CR2032 батарейки и даже 8мгц хватало чтоб и флешку читать и 30фпс обновлять и отрисовывать интерфейс с плавными свайпами вправо и влево, при этом, во время свайпа, оба окна продолжали динамически обновляться (экранного буфера не было из-за 2к озу — рисовал динамически).
аж прям ностальгия ) картинка рисовалась в двух буферах и шло переключение между буферами в одном рисуем второй показываем и оставалось еще много ресурсов
На этом контроллере stm32f103c8t6 максимальная скорость передачи по SPI как раз без DMA.

Как это возможно?
uint16_t i;
for(i=0;i<SSD1306_HEIGHT*SSD1306_WIDTH;i++)
{
if(color==WHITE)
displayBuff[i]=0xFF;
else if(color==BLACK)
displayBuff[i]=0;
}
Это а ) ужасно. вынесите условие из цикла
б) неверно. В 8 раз больше чем нужно.
Более того, SSD1306_HEIGHT*SSD1306_WIDTH нужно вынести в отдельную константу
Это необязательно. А вот переменную цикла безопасней сделать не uint16_t а просто int. Потому что при копипасте на чуть больший дисплей например 320х240х16бит она уже переполнится. А 32 двух разрядной архитектуре stm32 все равно как индексировать массив по 32 бита нативной переменной или по 16 битной. То есть выигрыша от 16битного индекса нет, а опасность есть.
То есть я бы написал просто " int i"
Конечно не всегда. Но речь идёт именно о stm32.
На других платформах она как правило как минимум 16 на 8 и 16 разрядных машинах и 32 на 32 и 64 разрядных машинах. Впрочем по стандарту должно быть что то вроде size_t для индексов. И даже на 8 и 16 разрядных машинах лучше использовать int, а не int32_t.
Данный фрагмент кода никак не специфицен для stm32, его можно перенести на любую другую платформу без какой либо переделки. Поэтому, если вы уж хотите сделать код действительно безопасным, о таком типе данных как int следует забыть, и использовать типы данных объявленные в файле stdint.h.
Ваше же предложение о замене uint16_t на просто int (кстати, почему именно на int, а не на unsigned int?) снижает безопасность кода. Более того, так как стандарт C не определяет размер данных, вы можете даже просто сменив компилятор — словить массу проблем за счет использования int'ов вместо uint32_t. И в теории такое может произойти банально за счет обновления версии компилятора.
The actual size of the integer types varies by implementation. The standard requires only size relations between the data types and minimum sizes for each data type:

The relation requirements are that the long long is not smaller than long, which is not smaller than int, which is not smaller than short. As char's size is always the minimum supported data type, no other data types (except bit-fields) can be smaller.

The minimum size for char is 8 bits, the minimum size for short and int is 16 bits, for long it is 32 bits and long long must contain at least 64 bits.

en.wikipedia.org/wiki/C_data_types
Вы все еще хотите использовать тип данных int?
Именно int. Во первых есть куча старых 8-16 бит компиляторов, в которых просто нет stdint.h. Во вторых, на 16 битной платформе int32 и uint32 ужасно неэффективны, да и память доступна через окна. И в третьих можно на эту тему спорить без конца и остаться при своем мнении. Я не навязываю своего стиля, а высказываю мнение. С — древний язык, я пишу на нем лет 30 уже и рекомендации к стилям менялись неоднократно. Тип int и его арифметика реализованы на всех платформах с максимальной эффективностью для данной платформы. А его ограничения просто нужно держать в голове…
Увидел, плохо сделал.
Можно исправить так:
if(color==WHITE)
    memcpy(displayBuff,0xFF,BUFFER_SIZE);
else if(color==BLACK)
    memcpy(displayBuff,0x00,BUFFER_SIZE);
Memset а не memcpy. Но направление правильное. Впрочем и for нормально.
Или так
typedef enum COLOR
{
	BLACK=0x00,
	WHITE=0xFF
}COLOR;
....
memset(displayBuff,color,BUFFER_SIZE);
В свое время мне было лень писать самому графику для этого дисплея и я нашел в сети подходящую библиотеку. А вот с цветными дисплеями я играться люблю. Особенно, если он позволяет микроконтроллеру читать свою память. Так можно фреймбуфер держать только в дисплее и если нужно что-то там модифицировать, то достаточно прочесть интересующий блок памяти, изменить его и записать обратно.
Что то я не понимаю:
1. Вам нужно передать 128*64=8192 бита по SPI
2. SPI передает 1 бит за такт частоты 18 МГц.
3. Максимальная частота обновления 18000000/8192=2197Гц~2.2кГц
4. Откуда 4.2кГц?
Так вышло потому, что SPI тут сконфигурирован на частоте 36Мгц. SPI1 сидит на шине APB2, частота которой 72Мгц, делитель SPI выставлен на 2. При такой частоте и получаем 4.2кГц.
На самом деле я сам удивился, почему оно так заработало (Cube не позволяет сконфигурировать SPI на такой частоте, хотя через регистры все работает). Было бы конечно неплохо посмотреть с помощью осциллографа как там на самом деле, но такой возможности сейчас нет. Конечно никакого толку от такой частоты нет, все было сделано сугубо в академических целях в процессе обучения.
Ну тогда понятно, все сходится.
А что касается настроек через библиотеки (КУБ — это тоже библиотеки) и напрямую, то часто сохраняются старые ограничения, которые давно уже сняты. Единственное, чем следует руководствоваться — это документация на конкретный МК и, если она допускает высокие частоы, то их и следует использовать.
И это совсем не академические цели, а вполне себе продакшн — умение выжать все соки из конкретного МК.
Только в доке на SSD1306 максимальная частота клока 10 МГц.
А не подскажите страницу? У меня явного ограничения найти не получилось, да и все прекрасно работает.
Боюсь, что «все прекрасно работает» — это не аргумент.
Таблицы 13.3 и 13.4 дата — период тактовой частоты интерфейса SPI — не менее 100нс.
while((SPI1->SR & SPI_SR_BSY))
Кстати проверка флага SPI_SR_BSY при отправке данных, не лучшее решение, так вы проверяете есть ли еще данные в сдвиговом регистре, ну и как пишут в RM:
The BSY flag must be used with caution: refer to Section28.5.10: SPI status flags and
Procedure for disabling the SPI on page767.

Вместо этого лучше проверять освободился ли буфер — SPI_SR_RXNE, так по идее должно быть еще и быстрее (немного)
«SPI_SR_RXNE»
Это --Rx buffer not empty (RXNE)

правильно будет TXE перед отправкой:
Tx buffer empty flag (TXE)
Решил таки произвести замер скорости передачи строки в 1000 символов, разница… ну она все же есть:

SPI_SR_TXE
image

SPI_SR_BSY
image
Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.