ARDUINO Бегущая строка на сдвиговых регистрах - вывод котировок акций мосбиржи

Sonya

✩✩✩✩✩✩✩
2 Июн 2020
38
3
www.cctvsp.ru
Приветствую!

Есть бегущая строка от автобуса. Прошивки нет, микроконтроллер пуст. С других аналогичных устройств считать невозможно, так как контроллер защищён от копирования.

Решил отреверсить. Табло состоит из матричных светодиодных индикаторов FYM-23881LUHRUG-11, матрицы 8x8 из сдвоенных красно-зелёных светодиодов.
Управляются специализированной микросхемой DM114 - это сдвиговый регистр по типу 74HC595 (в регистрах хранятся данные текущего столбца), а перебор столбцов осуществляется демультиплексорами 74HC138, включенными в паре (итого за цикл динамической индикации пробегаем 16 столбцов)
Нарисовал кусок схемы одного блока (всего таких блоков 5), для упрощения понимания работы, отрисовал только один цвет (на самом деле, индикаторы двух-цветные, соответственно используется по два регистра на одну матрицу, или 4 регистра на блок).
Scheme.png
Попробовал написать скетч на ардуино для понимания принципа работы динамической индикации. Вроде бы что-то получилось, статическое изображение выглядит вполне прилично.
Ниже демонстрация работу динамической индикации, поставил очень большой период обновления. Это GIF-ка:
20220128_101236.gif
А также отрисовал схему примерный вариант индикации:
tablo-algoritm.png
 
Изменено:

Sonya

✩✩✩✩✩✩✩
2 Июн 2020
38
3
www.cctvsp.ru
Пробный скетч умеет выводить запрограммированное статичное изображение. Обновление динамической индикации сделал на прерываниях.
Вот пример кода, который выводит содержимое буфера:
Фрагмент кода динамической индикации:
ISR(TIMER1_A) {
  PORTD = PORTD & B11110111;  // устанавливаем бит 3 (PD3) в Low, что означает включение Latch
Bank0 = (Column + 64 + Index) & B01111111;
Bank1 = (Column + 48 + Index) & B01111111;
Bank2 = (Column + 32 + Index) & B01111111;
Bank3 = (Column + 16 + Index) & B01111111;
Bank4 = (Column + Index) & B01111111;

// Загрузка буфера в сдвиговые регистры
  shiftOut(dataPin, clockPin, MSBFIRST, FrameG[Bank0]); // Байт зелёного столбца
  shiftOut(dataPin, clockPin, MSBFIRST, FrameR[Bank0]); // Байт красного столбца

  shiftOut(dataPin, clockPin, MSBFIRST, FrameG[Bank1]);
  shiftOut(dataPin, clockPin, MSBFIRST, FrameR[Bank1]);

  shiftOut(dataPin, clockPin, MSBFIRST, FrameG[Bank2]);
  shiftOut(dataPin, clockPin, MSBFIRST, FrameR[Bank2]);

  shiftOut(dataPin, clockPin, MSBFIRST, FrameG[Bank3]);
  shiftOut(dataPin, clockPin, MSBFIRST, FrameR[Bank3]);

  shiftOut(dataPin, clockPin, MSBFIRST, FrameG[Bank4]);
  shiftOut(dataPin, clockPin, MSBFIRST, FrameR[Bank4]);


  PORTD = (PORTD & B00000111) | (Column << 4) | 0x08 ; // устанавливаем биты PD4-PD7 порта D, устанавливаем бит 3 (PD3) в High, что означает отключение Latch
  Column++;

  Column &= 0b00001111; //  if (Column > 15) Column = 0;
}

void setup() {
  //устанавливаем режим порта выхода
  pinMode(latchPin, OUTPUT);
  pinMode(clockPin, OUTPUT);
  pinMode(dataPin, OUTPUT);

  //  Задаём таймер прерываний
  Timer1.setPeriod(1000); // период таймера 1000мкс (минимальный интервал 250мкс)
  Timer1.enableISR(CHANNEL_A);


  DDRD = DDRD | B11111000;        // назначим выводы Arduino D3-D7 выходными, остальные не трогаем
  PORTD = 0x08; // все выводы в ноль, D3 (Latch) в 1
}


Попробовал прикрутить кусок кода runningText из GyverStringDotmatrix, но бегущая строка выглядит некрасиво, размазывается в движении, вертикальные линии как бы утолщаются, выглядят состоящими их двух, ещё заметны рывки. В оригинале эта строка отображала бегущий текст гладко, плавно, "летящий" символ не раздваивался и был родной толщины.

Подскажите, что нужно сделать, чтобы получить такую же плавность движения?
Подозреваю, что нужно как-то ввести синхронизацию с обновлением динамической индикации... Я даже ввёл переменную Index, собирался изменять её из основного кода, смещая таким образом указатель внутри массива фреймбуфера, но это не решило проблемы прорисовки очередного символа.
В GyverStringDotmatrix при каждом сдвиге тупо перерисовывается весь экранный буфер (там-то это можно, так как до завершения обновления матрица показывает предыдущее изображение), а мне желательно прорисовывать очередной символ в невидимой части буфера и потом сдвигать.
 
Изменено:
  • Лойс +1
Реакции: bort707

bort707

★★★★★★✩
21 Сен 2020
2,859
849

@Sonya, для поддержки подобных дисплеев куча кода в инете, это называется "dmd matrix panel".

Я подобными экранами занимаюсь несколько лет, если хотите можем обсудить. Только проблема в том, что вариантов исполнения матриц десятки, для каждой нужен свой протокол. Так что если вам достался один случайный экран - особо упираться с разработкой софта смысла нет, работать это будет только на точно таком же экране, повторить никто не сможет.

Что касается вашего вопроса
В GyverStringDotmatrix при каждом сдвиге тупо перерисовывается весь экранный буфер (там-то это можно, так как до завершения обновления матрица показывает предыдущее изображение), а мне желательно прорисовывать очередной символ в невидимой части буфера и потом сдвигать.
то это решается с помощью двойной буферизации. Заводите два буфера размером с матрицу, пока один показывается, в другом обновляете картинку, потом меняете буферы местами.
 
Изменено:

Sonya

✩✩✩✩✩✩✩
2 Июн 2020
38
3
www.cctvsp.ru
для поддержки подобных дисплеев куча кода в инете, это называется "dmd matrix panel".
Да, я видел упоминание этой библиотеки, даже скачивал, но не смог разобраться, уж очень там навороченный функционал, а описания почти нет.


Я подобными экранами занимаюсь несколько лет, если хотите можем обсудить. Только проблема в том, что вариантов исполнения матриц десятки, для каждой нужен свой протокол.
Был бы признателен за подсказки.
Есть ещё мысль задействовать аппаратный SPI, тем более, что в данной схеме как раз и задействованы выводы аппаратного SPI.


Так что если вам достался один случайный экран - особо упираться с разработкой софта смысла нет, работать это будет только на точно таком же экране, повторить никто не сможет.
Смысл наверное есть, хотелось бы немного повысить свои навыки программирования под микроконтроллеры. А наработки потом тоже можно использовать и в других проектах.
Конечная цель моей затеи состоит в том, чтобы получать на ESP8266 котировки акций и потом по UART отправлять их на эту бегущую строку, но до этого пока далеко, я ещё не придумал, как вести обмен с ESP и парсить на arduino передаваемую строку (хотелось бы иметь возможность управлять цветом, раз строка это умеет).


Что касается вашего вопроса
то это решается с помощью двойной буферизации. Заводите два буфера размером с матрицу, пока один показывается, в другом обновляете картинку, потом меняете буферы местами.
У меня сейчас сделан буфер на 128 байт, это даже с запасом. 128 - так проще делать сдвиг внутри массива. Я хочу менять указатели на содержимое буфера, вместо полной его перерисовки.
Как я понимаю, эта строка сделана из 9 матриц, но имеет 10 сдвиговых регистров как раз по причине того, что последнее (невидимое) знакоместо предполагалось для отрисовки символа, а потом должен производиться сдвиг.

PS: Прилагаю свой вариант кода бегущей строки. Возможно, подскажете, что там нужно добавить/исправить.
 

Вложения

bort707

★★★★★★✩
21 Сен 2020
2,859
849
Да, я видел упоминание этой библиотеки, даже скачивал, но не смог разобраться, уж очень там навороченный функционал, а описания почти нет.
какой "этой"? Библиотек для dmd не меньше десятка...
Ваш код посмотрю вечером, сейчас пишу с телефона, неудобно.

У меня сейчас сделан буфер на 128 байт, это даже с запасом. 128 - так проще делать сдвиг внутри массива. Я хочу менять указатели на содержимое буфера, вместо полной его перерисовки.
в принципе это то же самое. Делаете два буфера, показываете один, рисуете другой, потом меняете указатели буферов местами

Сорри. посмотреть код времени не хватило. Если есть конкретные вопросы - спрашивайте.
 

Bruzzer

★★★✩✩✩✩
23 Май 2020
312
100
@Sonya,
Могу только посоветовать решать проблемы по частям.
Статичный текст вы выводить научились.
Теперь занесите в ваш буфер статичный текст пригодный для кольцевой прокрутки. (или пилу или меандр ...) Проверьте для кольцевой прокрутки буфера - устраивает ли вас результат . Идея с таймером 1 мне не понятна. Динамическая индикация ведь должна вестись постоянно. Сдвиг начала буфера - возможно по таймеру, или по millis. Но если используете таймер, то рассчитайте, или произведите измерения - сколько занимает времени отработка прерывания таймера 1. (По моему даже при аппаратном SPI на малых частотах SPI скорости может не хватить). Впрочем, этот расчет (измерения) надо провести в любом случае
Переведите на аппаратный SPI.
(Зачеркнул позже. )
Если в результате циклический вывод текста (или пилы или ...) устраивает приступайте к изучению заполнения буфера.

К вопросу не относится, но не понял, почему вы в паре мультиплексоров используете только один столбец (т.е. один столбец из 16)? Почему не один столбец на мультиплексор (т.е один из 8),
 
Изменено:

Sonya

✩✩✩✩✩✩✩
2 Июн 2020
38
3
www.cctvsp.ru
в принципе это то же самое. Делаете два буфера, показываете один, рисуете другой, потом меняете указатели буферов местами
Как я понял из работы кода, прорисовка происходит очень быстро, в двух буферах нет смысла. Тут засада похоже в скорости движения, она должна быть связана с частотой развёртки. У меня пока синхронизации нет. В понедельник продолжу эксперименты.


Если есть конкретные вопросы - спрашивайте.
У меня пока вопросы по библиотекам dmd. Я нашёл на гитхабе dmd и dmd2 от freetronics, но вот документации и конкретных схем подключения там нет. Нашёл на стороннем ресурсе один пример, но там в схеме всего два бита для управления адресом, а у меня их аж 4.


@Sonya,
Статичный текст вы выводить научились.
Теперь занесите в ваш буфер статичный текст пригодный для кольцевой прокрутки. (или пилу или меандр ...) Проверьте для кольцевой прокрутки буфера - устраивает ли вас результат .
ОК, так и сделаю. Суну основной цикл динамической индикации переменную и поэкспериментирую с делителями.


Идея с таймером 1 мне не понятна. Динамическая индикация ведь должна вестись постоянно. Сдвиг начала буфера - возможно по таймеру, или по millis. Но если используете таймер, то рассчитайте, или произведите измерения - сколько занимает времени отработка прерывания таймера 1.
Какой же тогда таймер использовать? Я посмотрел, первый вроде бы нигде больше не используется.

(По моему даже при аппаратном SPI на малых частотах SPI скорости может не хватить). Впрочем, этот расчет (измерения) надо провести в любом случае
Переведите на аппаратный SPI.
Пока экспериментировал с софтверным SPI, минимальный допустимый период - 250мкс (это 4кГц частота таймера или 250Гц частота обновления всего дисплея), пока это предел. Сейчас стоит 1000мкс (1кГц), частота обновления всего дисплея 62.5Гц.
Кое-что всё-таки позаимствую из DMD, там как раз аппаратное управление SPI. Как я понял, вместо shiftOut нужно будет использовать SPI.transfer. Думаю, тогда можно будет поднять частоту развёртки. Возможно это поможет при скролле текста.

К вопросу не относится, но не понял, почему вы в паре мультиплексоров используете только один столбец (т.е. один столбец из 16)? Почему не один столбец на мультиплексор (т.е один из 8),
Похоже, я неправильно нарисовал схему. Хоть там и используется по два регистра на блок из двух матриц 8x8, оба этих регистра содержат информацию только об одном столбце (по байту на красный и зелёный цвета), поэтому и получается, что приходится перебирать все 16 столбцов.
 
Изменено:

bort707

★★★★★★✩
21 Сен 2020
2,859
849
У меня пока вопросы по библиотекам dmd. Я нашёл на гитхабе dmd и dmd2 от freetronics, но вот документации и конкретных схем подключения там нет. Нашёл на стороннем ресурсе один пример, но там в схеме всего два бита для управления адресом, а у меня их аж 4.
про схемы - в стандартных монохромных матрицах, с которыми работает библиотека DMD - выходы шифт регистров идут по горизонтали, а биты управления адресом - по вертикали. Горизонтальные строки соединены по 4 вместе.. Высота матрицы 16. соответственно вся матрица выводится в 4 приема (4 скана), поэтому там достаточно двух битов для управления адресом. Матрицы бывают разные, от 2х сканов до 32х, но общий принцип работы у всех одинаковый - задвинули в шифт регистр данные для очередного скана. переключили адрес, показали какое-то время. задвинули новый скан, опять показали...

Пока экспериментировал с софтверным SPI, минимальный допустимый период - 250мкс (это 4кГц частота таймера или 250Гц частота обновления всего дисплея), пока это предел. Сейчас стоит 1000мкс (1кГц), частота обновления всего дисплея 62.5Гц.
при расчетах не забывайте, что если вы хотите, чтобы программа могла делать что-то еще - она должна тратить на показ матрицы не более четверти процессорного времени. Иначе у вас получится, что матрица умеет показывать только заранее заготовленный текст, а ничего нового на нее вов ремя работы передать нельзя.
 
Изменено:

Bruzzer

★★★✩✩✩✩
23 Май 2020
312
100
@Sonya,
Про таймер1 был не прав. Зачеркнул в исходном сообщении. Про SPI возможно тоже.
 

Sonya

✩✩✩✩✩✩✩
2 Июн 2020
38
3
www.cctvsp.ru
Исправил схему в первом сообщении, чтобы не вводить в заблуждение. Организация дисплея такая: 5 блоков из сдвоенных матриц 8x8, динамически перебираются 16 столбцов, одновременно в каждом блоке. Отдельного управления включением матриц нет, подозреваю, что они сразу загораются после загрузки всех данных, как только Latch становится неактивным. Завтра отслежу по схеме.

А вот так выглядит внутренняя схема матрицы 8x8:
matrix.png

при расчетах не забывайте, что если вы хотите, чтобы программа могла делать что-то еще - она должна тратить на показ матрицы не более четверти процессорного времени. Иначе у вас получится, что матрица умеет показывать только заранее заготовленный текст, а ничего нового на нее вов ремя работы передать нельзя.
У меня сейчас так и получается, на динамическую индикацию тратится четверть работы. Надеюсь, что с аппаратным SPI это время ещё сократится.
 
Изменено:

Sonya

✩✩✩✩✩✩✩
2 Июн 2020
38
3
www.cctvsp.ru
Вести с полей.
Включил аппаратный SPI, работает аж на 8МГц (SPI_CLOCK_DIV2), пробовал после этого поднимать частоту прерываний, делал даже период обновления 50мкс - работает. Визуально ничего не изменилось, бегущая строка всё так же двоится и размазывается.

@Sonya,
занесите в ваш буфер статичный текст пригодный для кольцевой прокрутки. (или пилу или меандр ...) Проверьте для кольцевой прокрутки буфера - устраивает ли вас результат
Экспериментировал с делителями от обновления дисплея: нормальный результат достиг только при использовании делителя 15!
При любом другом делителе, пиксели налезают друг на друга или вылезают другие эффекты в виде рывков и лесенок.
Зато при делителе 15 текст выглядит плавным и соответствующим своему размеру. Могу объяснить это тем, что период развёртки составляет 16 циклов, и к началу нового цикла мы сдвигаем указатель ровно на единицу. Таким образом, каждый пиксель отображается на своём месте только один раз, что и создаёт такой эффект.
К сожалению, это ведёт к тому, что я ограничен в скорости движения текста, для нормальной читаемости пришлось снова снизить частоту прерываний до 1000мкс, при такой частоте скорость движения текста ещё позволяет успевать прочитать текст, а мерцания ещё не заметно.
 

poty

★★★★★★✩
19 Фев 2020
2,956
886
@Sonya, мне вот интересно, Вам советуют прокрутить неизменный буфер, Вы это делаете? Или во время прокрутки Вы буфер меняете?
 

Sonya

✩✩✩✩✩✩✩
2 Июн 2020
38
3
www.cctvsp.ru
@Sonya, мне вот интересно, Вам советуют прокрутить неизменный буфер, Вы это делаете? Или во время прокрутки Вы буфер меняете?
Пробовал и статический буфер двигать, и менять его.
Только я не прокручиваю его, а просто меняю указатель на начало буфера при каждом сдвиге, и отрисовка динамической индикации происходит с нового места.
Размер 128 байт выбран не случайно, так легче всего его закольцевать:
После & B01111111 переполнение автоматически обходится.
Правда, при таком малом размере буфера, я могу и в цикле его двигать, и даже рендерить полностью - процессорного времени хватает, что и подтвердили эксперименты с runningText из примера GyverStringDotmatrix_v1.3. Пришлось правда доработать эту библиотеку, чтобы тоже понимала "переход через ноль".
Хотелось бы сделать всё красиво, отрисовывать в буфере только новый символ, когда необходимо его появление, но что-то Муза покинула меня.

Пока такой код:
Основной цикл обновления экрана:
  PORTD = PORTD & B11110111;  // устанавливаем бит 3 (PD3) в Low, что означает включение Latch
Bank0 = (Column + 64 + Index) & B01111111;
Bank1 = (Column + 48 + Index) & B01111111;
Bank2 = (Column + 32 + Index) & B01111111;
Bank3 = (Column + 16 + Index) & B01111111;
Bank4 = (Column + Index) & B01111111;

/*
// Загрузка буфера в сдвиговые регистры
*/
  SPI.transfer(FrameG[Bank0]);
  SPI.transfer(FrameR[Bank0]);
  SPI.transfer(FrameG[Bank1]);
  SPI.transfer(FrameR[Bank1]);
  SPI.transfer(FrameG[Bank2]);
  SPI.transfer(FrameR[Bank2]);
  SPI.transfer(FrameG[Bank3]);
  SPI.transfer(FrameR[Bank3]);
  SPI.transfer(FrameG[Bank4]);
  SPI.transfer(FrameR[Bank4]);

  PORTD = (PORTD & B00000111) | (Column << 4) | 0x08 ; // устанавливаем биты PD4-PD7 порта D, устанавливаем бит 3 (PD3) в High, что означает отключение Latch
  Column++;
//  Column--;
  Column &= 0b00001111; //  if (Column > 15) Column = 0;

  Divider++;
  if (Divider > 14) Divider = 0;
//    if  (Divider == 0) Index++;
  if (!Divider) fillString(runningText, 1);
Тут закомменчена строка if (Divider == 0) Index++;
С ней я и экспериментировал, тупо увеличивал индекс и таким образом двигал буфер по кругу.
Теперь аналогичным образом вызываю fillString(runningText, 1), которая при каждой итерации полностью прорисовывает буфер. Некрасиво и накладно в плане процессорных ресурсов, но работает.

А это сама runningText.ino:
int offset = WIDTH;

void fillString(String text, byte color) {
  if (loadingFlag) {
    offset = WIDTH;   // перемотка в правый край
    loadingFlag = false;
    fullTextFlag = false;
  }
    if (showText) {
//      matrix.fillScreen(LOW); // Быстрый способ очистить экран
memset(FrameG, 0, 128);
memset(FrameR, 0, 128);
      byte i = 0, j = 0;
      while (text[i] != '\0') {
        if ((byte)text[i] > 191) {    // работаем с русскими буквами!
          i++;
        } else {
          drawLetter(j, text[i], offset + j * (LET_WIDTH + SPACE), color);
          i++;
          j++;
        }
      }

      offset--;
      if (offset < -j * (LET_WIDTH + SPACE)) {    // строка убежала
        offset = WIDTH + 3;
        if (!autoFlag) showText = false;
      }
//      matrix.write(); // Когда закончили заполнять буфер, загрузите его в дисплей
    }
//  }
}

void drawLetter(uint8_t index, uint8_t letter, int16_t offset, byte color) {
  int8_t start_pos = 0, finish_pos = LET_WIDTH;

  if (offset < -LET_WIDTH || offset > WIDTH) return;
  if (offset < 0) start_pos = -offset;
  if (offset > (WIDTH - LET_WIDTH)) finish_pos = WIDTH - offset;

  for (byte i = start_pos; i < finish_pos; i++) {
    int thisByte;
    if (MIRR_V) thisByte = getFont((byte)letter, LET_WIDTH - 1 - i);
    else thisByte = getFont((byte)letter, i);

//    for (byte j = 0; j < LET_HEIGHT; j++) {
//      boolean thisBit;

//      if (MIRR_H) thisBit = thisByte & (1 << j);
//      else thisBit = thisByte & (1 << (LET_HEIGHT - 1 - j));

FrameG[(offset + i)& B01111111] = thisByte;
FrameR[(offset + i)& B01111111] = 0;

      // рисуем столбец (i - горизонтальная позиция, j - вертикальная)
//      if (TEXT_DIRECTION) {
//        if (thisBit) matrix.drawPixel(offset + i, TEXT_HEIGHT + j, 1);
//        else matrix.drawPixel(offset + i, TEXT_HEIGHT + j, 0);
//      } else {
//        if (thisBit) matrix.drawPixel(i, offset + TEXT_HEIGHT + j, 1);
//        else matrix.drawPixel(i, offset + TEXT_HEIGHT + j, 0);
//      }
//    }
  }
}

// ------------- СЛУЖЕБНЫЕ ФУНКЦИИ --------------

// интерпретатор кода символа в массиве fontHEX (для Arduino IDE 1.8.* и выше)
uint8_t getFont(uint8_t font, uint8_t row) {
  font = font - '0' + 16;   // перевод код символа из таблицы ASCII в номер согласно нумерации массива
  if (font <= 90) return pgm_read_byte(&(fontHEX[font][row]));     // для английских букв и символов
  else if (font >= 112 && font <= 159) {    // и *****ц ждя русских
    return pgm_read_byte(&(fontHEX[font - 17][row]));
  } else if (font >= 96 && font <= 111) {
    return pgm_read_byte(&(fontHEX[font + 47][row]));
  }
}
 
Изменено:

Bruzzer

★★★✩✩✩✩
23 Май 2020
312
100
@Sonya,
нормальный результат достиг только при использовании делителя 15!
К сожалению, это ведёт к тому, что я ограничен в скорости движения текста, для нормальной читаемости пришлось снова снизить частоту прерываний до 1000мкс
Как я понял вы снижаете частоту прерываний потому, что текст движется слишком быстро? У вас есть возможность регулировать скорость движения текста кратностью делителя 15 30 45 .... И частотой прерываний.
Дополнено позже, наверное шаг делителя должен быть 16, т.е. 15 31 47 ...
 
Изменено:

Sonya

✩✩✩✩✩✩✩
2 Июн 2020
38
3
www.cctvsp.ru
@Sonya,
Как я понял вы снижаете частоту прерываний потому, что текст движется слишком быстро? У вас есть возможность регулировать скорость движения текста кратностью делителя 15 30 45 .... И частотой прерываний.
Дополнено позже, наверное шаг делителя должен быть 16, т.е. 15 31 47 ...
Вот я тоже раньше думал, что делитель должен быть кратен развёртке, поэтому брал числа, кратные 16, и постоянно получал какие-то визуальные глюки: раздвоение и движение "лесенкой", и только 15 дало нормальный эффект.
Кстати, при делителе 30 получаем визуальное удвоение пикселей по горизонтали и противное мерцание, так как каждый пиксель по два раза появляется на своём месте при динамической индикации.
 

Bruzzer

★★★✩✩✩✩
23 Май 2020
312
100
@Sonya,
А при делителе 31 лучше или хуже чем 30? Насчет теории противного мерцания не понял логики, но посмотреть не на чем, так что остается верить. (Но по этой логике - статичный текст должен быть мега противным, т.к. пиксель всегда "появляется на своем месте")
 

Sonya

✩✩✩✩✩✩✩
2 Июн 2020
38
3
www.cctvsp.ru
@Sonya,

Как я понял вы снижаете частоту прерываний потому, что текст движется слишком быстро?
Да, сейчас стоит период таймера 1мс, частота обновления всего кадра 62.5Гц. Меня это вполне устраивает.

У вас есть возможность регулировать скорость движения текста кратностью делителя 15 30 45 .... И частотой прерываний.
Дополнено позже, наверное шаг делителя должен быть 16, т.е. 15 31 47 ...
При делителе 31 текст как бы удваивается, видно на картинке (специально сделал выдержку побольше. С обычной выдержкой этот эффект на камеру почти незаметен, виден только глазом, на расстояниях до 2м). При других делителях дополнительно текст ещё и "ломается лесенкой".

20220131_122451.jpg

Насчет теории противного мерцания не понял логики, но посмотреть не на чем, так что остается верить. (Но по этой логике - статичный текст должен быть мега противным, т.к. пиксель всегда "появляется на своем месте")
Нет, статичный текст выглядит нормально при любом делителе, ведь глаз неподвижен. А при движущемся тексте, глаз следит за словом с постоянной скоростью. К сожалению, такой эффект не заснять на камеру.
Впрочем, на дальнем расстоянии эффект снижается.

Ковыряясь с этой бегущей строкой, я посмотрел, как работают другие строки с динамической индикацией, и оказалось, что у большинства из них при снижении скорости движения текста, появлялся такой же эффект удвоения. Так что, найденная мной скорость движения остаётся единственной подходящей.

Осталось теперь причесать код отображения символа и добавить возможность смены цвета. Хотелось бы использовать символы с кодами 1,2 и 3 для задания цвета, но тут используется UTF-8 - не знаю, как их сунуть в код.
 
Изменено:

bort707

★★★★★★✩
21 Сен 2020
2,859
849
Ковыряясь с этой бегущей строкой, я посмотрел, как работают другие строки с динамической индикацией, и оказалось, что у большинства из них при снижении скорости движения текста, появлялся такой же эффект удвоения. Так что, найденная мной скорость движения остаётся единственной подходящей.
что-то я такого на своих матрицах не замечал. При снижении скорости прокрутки текста строка начинает двигаться скачками, что закономерно, но никакого удвоения нет.
Наоборот, удвоение ( оптический обман) может быть видно при увеличении скорости - строка как бы размазывается на соседние пиксели.

А не может быть причиной вашего удвоения то, что при сдвиге строки вы не стираете старое положение пикселей? Для проверки попробуйте двигать строчку не на один, а на два пикселя за раз и посмотрите, что выйдет
 

poty

★★★★★★✩
19 Фев 2020
2,956
886
Сколько по времени идёт вывод строки на экран? Не меняете ли Вы указатель на начало отображения посередине развёртки строки на экран (иными словами, эти действия должны быть синхронизированы).
Регистры у Вас с защёлкой? Как управляется защёлка, если есть?
 

Sonya

✩✩✩✩✩✩✩
2 Июн 2020
38
3
www.cctvsp.ru
Сколько по времени идёт вывод строки на экран? Не меняете ли Вы указатель на начало отображения посередине развёртки строки на экран (иными словами, эти действия должны быть синхронизированы).
Картинка из буфера выводится за 16 тактов прерывания (столбцы перебираются по переменной Column). Изменение индекса тут происходит независимо от Column. Похоже с изменением указателя у меня и есть проблема.

Основной цикл обновления экрана:
  SPI.transfer(FrameG[Bank0]);
  SPI.transfer(FrameR[Bank0]);
  SPI.transfer(FrameG[Bank1]);
  SPI.transfer(FrameR[Bank1]);
  SPI.transfer(FrameG[Bank2]);
  SPI.transfer(FrameR[Bank2]);
  SPI.transfer(FrameG[Bank3]);
  SPI.transfer(FrameR[Bank3]);
  SPI.transfer(FrameG[Bank4]);
  SPI.transfer(FrameR[Bank4]);

  PORTD = (PORTD & B00000111) | (Column << 4) | 0x08 ; // устанавливаем биты PD4-PD7 порта D, устанавливаем бит 3 (PD3) в High, что означает отключение Latch
  Column++; // Инкрементирую указатель внутри блока из двух индикаторов 8x8
  Column &= 0b00001111; //  if (Column > 15) Column = 0;

  Divider++;
  if (Divider > 14) Divider = 0;
  if  (Divider == 0) Index++;
Регистры у Вас с защёлкой? Как управляется защёлка, если есть?
Имеете в виду, вход Latch? Да, он есть и управляется, как раз после загрузки всех регистров переключаю в неактивный.
А вот вход Enable постоянно сидит на земле, то есть то, что загружено в регистре, всегда выводится. Поначалу меня это удивило, я подумал, что при медленной загрузке в регистры весь процесс будет виден, но попробовав на статическом изображении, не обнаружил проблем.

что-то я такого на своих матрицах не замечал. При снижении скорости прокрутки текста строка начинает двигаться скачками, что закономерно, но никакого удвоения нет.
Наоборот, удвоение ( оптический обман) может быть видно при увеличении скорости - строка как бы размазывается на соседние пиксели.

А не может быть причиной вашего удвоения то, что при сдвиге строки вы не стираете старое положение пикселей? Для проверки попробуйте двигать строчку не на один, а на два пикселя за раз и посмотрите, что выйдет
Похоже, я где-то накосячил, надо было выводить пилу или синус: пока выводил буквы, было незаметно, но потом обнаружил, что у некоторых букв не хватает пикселей. Попробовал движение строки в другую сторону, там уже для плавности движения пришлось сделать делитель 17, и тут я увидел, что на тех же буквах в тех же местах удвоение пикселей.
 

Вложения

Изменено:

poty

★★★★★★✩
19 Фев 2020
2,956
886
похоже с изменением указателя у меня и есть проблема
Указатель должен изменяться как часть процесса вывода (развёртки) в самом конце перед началом новой развёртки.
Не уверен, что про защёлку мы говорим об одном и том же. Есть сдвиговые регистры, которые не имеют буфера и отображение идёт непосредственно со сдвигового регистра, а есть те, у которых при подаче на вход (STCP для 595-ой серии) сдвиговый регистр буферизуется целиком в регистр хранения (буфер) из которого потом отображается.
В любой случае, нужно "стробить" вывод, чтобы бОльшую часть времени он демонстрировал пусть и только одну строку, но уже полностью сформированную, без сдвига.
 

bort707

★★★★★★✩
21 Сен 2020
2,859
849
Имеете в виду, вход Latch? Да, он есть и управляется, как раз после загрузки всех регистров переключаю в неактивный.
А вот вход Enable постоянно сидит на земле, то есть то, что загружено в регистре, всегда выводится. Поначалу меня это удивило, я подумал, что при медленной загрузке в регистры весь процесс будет виден, но попробовав на статическом изображении, не обнаружил проблем.
аообще по моему латч надо не переключить в неактивный, а дать по нему полный импульс. Но это надо смотреть на матрице.
А вот с Enable вы работаете неверно. На время импульса латч и перeключения мультиплексов Enable надо выключать ( переводить в High), иначе будут артефакты на матрице.
 

Sonya

✩✩✩✩✩✩✩
2 Июн 2020
38
3
www.cctvsp.ru
аообще по моему латч надо не переключить в неактивный, а дать по нему полный импульс. Но это надо смотреть на матрице.
А вот с Enable вы работаете неверно. На время импульса латч и перeключения мультиплексов Enable надо выключать ( переводить в High), иначе будут артефакты на матрице.
dm114.png

Действительно, latch надо дёргать в конце. Но этот алгоритм не я придумал, а взял из примеров работы с регистром 595.
Похоже, в этом и есть причина задвоения - latch у меня сбрасывается в Low (получается, защёлкивается) уже в начале нового такта и тут же меняется адрес. Также поэтому я отстаю на такт.
Завтра переделаю.

А вот Enable у меня не управляется, сидит на земле. Такая схема матрицы. И ведь работало! Значит, после защёлки сразу идёт вывод на выходы регистра. Я поначалу боялся, что в процессе наполнения сдвигового регистра весь этот сдвиг будет виден на экране, но обошлось, там всё чётко.
 
Изменено:

poty

★★★★★★✩
19 Фев 2020
2,956
886
@Sonya, если есть latch, то сдвиг осуществляется "в тёмную". На выходы он попадает как раз по фронту импульса latch.
 

bort707

★★★★★★✩
21 Сен 2020
2,859
849

@Sonya,
диаграмма выходов out0 - out7 у вас тоже не такая, как на больших матрицах. У вас тут идут относительно короткие импульсы, если верить картинке, а на тех матрицах выходы out находятся в фиксированном положении все время показа очередного канала.