Бегущая точка в Max7219

Esteriman

✩✩✩✩✩✩✩
4 Июн 2025
45
2
Прошу помощи. Код зависает. Перед зависанием может отработать несколько раз, но при этом не гасит предыдущую точку. Пытаюсь сделать фейерверк на матрице 64х16. Код dotsec и dotOnLine взят из библиотеки GyverMax7219. Демо-примеры работают отлично (кроме кривой Безье).

C++:
#include <GyverMAX7219.h>
MAX7219<8, 2, 5> mtrx; // 8 по горизонтали, 2 по вертикали
                       // CS - D5
const uint8_t wM = 64, hM = 16; // ширина и высота
uint32_t t1=millis();

void setup() {
    mtrx.begin();       // запускаем матрицу
    mtrx.setBright(3);  // яркость 0..15
    mtrx.clear();
    randomSeed(analogRead(0)); // запускаем генератор случайных чисел
}

void loop() {
  if (millis()-t1 > 100) {
    t1 = millis();
    fireworks();
    mtrx.update();
  }
}

void dotsec(int x, int y, uint8_t fill) {
    if (x < 0 || x >= wM || y < 0 || y >= hM) return;
    mtrx.dot(x, y, fill);
}

uint8_t xprev=0, yprev=0;
bool dotOnLine(int x0, int y0, int x1, int y1, uint8_t fill = 1) {
  static uint8_t x=x0, y=y0;
  int8_t sx = (x0 < x1) ? 1 : -1;
  int8_t sy = (y0 < y1) ? 1 : -1;
  int dx = abs(x1 - x0);
  int dy = abs(y1 - y0);
  int e1 = dx - dy;
  static int e2 = 0;
  dotsec(xprev, yprev, 0);
  if (x == x1 && y == y1) return true;
  dotsec(x, y, 1);
  e2 = e1 << 1;
  if (e2 > -dy) {
      e1 -= dy;
      xprev = x;
      x += sx;
  }
  if (e2 < dx) {
      e1 += dx;
      yprev = y;
      y += sy;
  }
  return false;
}

void fireworks() {
static bool fire=true;
static uint8_t x0, y0, x1, y1;
  if (fire) {
    x0 = random(wM); y0 = random(hM);
    x1 = random(wM); y1 = random(hM);
    fire = false;
    mtrx.clear();
  }
  if (dotOnLine(x0, y0, x1, y1)) fire = true;
}
 

Bruzzer

★★★★✩✩✩
23 Май 2020
803
246
@Esteriman,
В вопросе нет никакого описания алгоритма предполагаемой работы. Ни текстом в сообщении ни комментариями к коду.
> Код dotsec и dotOnLine взят из библиотеки GyverMax7219
Я не нашел в в примерах к библиотеке GyverMax7219 функций dotsec и dotOnLine
Может быть трудно понять, почему не работает ваш код, когда неизвестно, что программа должна делать, и как вы пытаетесь это делать.
 

Esteriman

✩✩✩✩✩✩✩
4 Июн 2025
45
2
@Esteriman,
В вопросе нет никакого описания алгоритма предполагаемой работы. Ни текстом в сообщении ни комментариями к коду.
Да, сорри, если поподробнее то этот код должен провести точку по прямой линии со случайными координатами. Т.е. постоянно строить новые линии после того как закончится построения предыдущей. Фактически тот же алгоритм построения прямой, только по этой прямой должна пройти точка. В библиотеке GyverMax7219 позаимствованы функции line (здесь: dotOnLine) и dotSecure (здесь: dotsec).
 

Bruzzer

★★★★✩✩✩
23 Май 2020
803
246
Причина этого довольно проста.
Инициализация начальными значениями производится только один раз, и один сегмент (или луч) рисуется, а потом, в зависимости от конечного значения x и y предыдущего луча.
static uint8_t x=x0, y=y0;

Почему не гасится предыдущая точка и в целом почему алгоритм работает "криво", надо разбираться в алгоритме. Он не очевидный на первый взгляд.
 

Esteriman

✩✩✩✩✩✩✩
4 Июн 2025
45
2
Почему не гасится предыдущая точка и в целом почему алгоритм работает "криво", надо разбираться в алгоритме. Он не очевидный на первый взгляд.
Оказалось, что x и y в функции dotOnLine выходят за пределы диапазона и поэтому кажется, что программа «повисла». Вот что в мониторе:

C++:
x = 28 y = 9
xprev = 28 yprev = 0
x = 29 y = 9
xprev = 29 yprev = 0
x = 30 y = 9
xprev = 30 yprev = 0
x = 31 y = 9
xprev = 31 yprev = 0
...
xprev = 62 yprev = 0
x = 63 y = 9
xprev = 63 yprev = 0
x = 64 y = 9
xprev = 64 yprev = 0
x = 65 y = 9
xprev = 65 yprev = 0
x = 66 y = 9
xprev = 66 yprev = 0
x = 67 y = 9
xprev = 67 yprev = 0
x = 68 y = 9
Это при том, что индикатор 64х16 точек, размер задается константами wM, hM.
Также выяснилось, что условие
if (x == x1 && y == y1) return true;
не достигается. x, y выходят за пределы диапазона, затем обнуляются вследствии ограничений типа данных и все идет по новой.
Сам алгоритм, как мне сказал ИИ, это алгоритм Брезенхема для построения прямой линии по двум конечным точкам для матричного индикатора. Алгоритм работает. Но что-то я не учел.

Добавление.

Причина этого довольно проста.
Инициализация начальными значениями производится только один раз, и один сегмент (или луч) рисуется, а потом, в зависимости от конечного значения x и y предыдущего луча.
static uint8_t x=x0, y=y0;
Да, именно в этом дело оказалось в том числе. После того как построена первая прямая и идет вызов для следующей нужна новая инициализация x, y и остальных значений.

Еще одно добавление.
Все заработало. Дело было в том, что значения координат точки выходили за границы диапазона. Тому было несколько причин. Новый код с изменениями.

C++:
bool newLine = true;
bool dotOnLine(int x0, int y0, int x1, int y1, uint8_t fill = 1) {
  static int8_t xprev, yprev, x, y;
  int8_t sx, sy;
  uint8_t dx, dy;
  static int8_t err, e2;
  if (newLine) {
    x = x0; y = y0;
    sx = (x0 < x1) ? 1 : -1;
    sy = (y0 < y1) ? 1 : -1;
    dx = abs(x1 - x0);
    dy = abs(y1 - y0);
    err = dx - dy;
    newLine = false;
  }
  dotsec(xprev, yprev, 0);
  if ((x == x1 && y == y1) || (x >= wM) || (y >= hM) || (x < 0) || (y < 0)) return true;
  dotsec(x, y, 1);
  e2 = err << 1;
  xprev = x;
  yprev = y;
  if (e2 > -dy) {
      err -= dy;
      x += sx;
  }
  if (e2 < dx) {
      err += dx;
      y += sy;
  }
  return false;
}

void fireworks() {
static uint8_t x0=random(wM), y0=random(hM),
               x1=random(wM), y1=random(hM);
  if (dotOnLine(x0, y0, x1, y1)) {
    x0 = random(wM); y0 = random(hM);
    x1 = random(wM); y1 = random(hM);
    newLine = true;
    mtrx.clear();
  }
}
 
Изменено:

Bruzzer

★★★★✩✩✩
23 Май 2020
803
246
@Esteriman,
Сейчас ИИ очень хороший помощник в написании кода, особенно если есть понимание основных принципов нужного алгоритма.
Не знаю, дает ли он рекомендации по оформлению кода, напишу, что мне бросилось в глаза на примере функции dotOnLine.
Логика кода написана вперемешку. Например, строку 37 логично поставить в начало функции (в ее нынешнем виде) а не в середину
37 if (x == x1 && y == y1) return true;
Строки с 29 по 35 и строка 39 зависят только от входных параметров и логично заключить их в блок начальной настройки, выполняемый один раз.
Подход, когда значение вычисляется и выводится в разных циклах запутывает понимание. Это я про строку 38 dotsec(x, y, 1); которая "рисует" точку рассчитанную в предыдущем цикле.
Если нет особых требований со стороны алгоритма, то (по моему) более читаемо, когда значение рассчитывается и сразу выводится.
 

Esteriman

✩✩✩✩✩✩✩
4 Июн 2025
45
2
Выше привел код уже рабочий. Поставил блок начальной настройки. Согласен, что значение должно рассчитываться и сразу выводиться.
Код я пишу сам. Иначе не в кайф. ИИ только показывает как выглядят алгоритмы.