Search
Write a publication
Pull to refresh

Брелок с LED матрицей 8x8 на CH32V003

Level of difficultyMedium
Reading time8 min
Views362

Введение

Решил наконец погрузиться в электронику как хобби. Изначальной целью была самодельная игровая консоль, но из-за сложностей я начал с более простого проекта — светодиодного брелка на микроконтроллере CH32V003F4P6. Почему именно он? Это дешевая (около 20 рублей за штуку) и доступная микросхема с 20 выводами — достаточно, чтобы управлять матрицей 8x8 без драйверов вроде MAX7219.

Возможно одним из первых на эту мысль меня натолкнула статья от Terrabyte про мини-консоль, в которой я узнал, что платы можно делать без химии, на фрезерном станке. Я вынашивал мысль о его покупке месяца три, а потом сдался. И пока я изучал как разводить платы и пользоваться станком (он, кстати, не сразу заработал, мучался месяц, пока не поменял плату управления на HBC-3U.J, но это другая история и лучше на все не отвелкаться), наткнулся на два ролика на YouTube, в которых был брелок со светодиодной матрицей. Первый ролик меня заинтреговал, но как раз во втором ролике видно, что плата делалась с помощью фрезировки. И тогда подумал, что это хорошая идея попробовать свои силы и все должно получиться.

Но самое главное, просто повторять такой же проект мне не хотелось и я решил попробовать сделать его на CH32V003, т.к. в последнее время это мой любимый микроконтроллер и как раз CH32V003P4F6 имеет 20 ножек, что должно хватить на подключения матрицы напрямую к МК, без посредственников (MAX7219), как делалось в роликах.

Из роликов на YouTube
Из роликов на YouTube

Светодиодная матрица 1088AB: основы

Картинок с 1088AB не нашлось, будет с 1088AS
Картинок с 1088AB не нашлось, будет с 1088AS

Матрица 1088AB — это сетка 8x8 синих светодиодов с общим катодом. 1088AS с красными светодиодами. Управление работает по принципу динамической индикации:

  1. Микроконтроллер поочередно активирует строки (или столбцы).

  2. Для каждой строки задается комбинация светящихся точек.

  3. При быстром переключении (от 100 Гц) человеческий глаз видит цельное изображение.

Схема подключения к CH32V003:

  • 8 выводов МК идут к строкам матрицы.

  • 8 выводов МК идут к столбцам матрицы (через токоограничивающие резисторы 220 Ом).

Схема 1088AS из даташита
Схема 1088AS из даташита

Прототип

Печатная плата

Как всегда, пока ждешь комплектующее, у меня все начинается с двух схем. Одна это трасировка или разводка печатной платы, PCB, а вторая это что-то вроде прототипа в SimulIDE.

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

Схема печатной платы
Схема печатной платы
Трассировка печатной платы
Трассировка печатной платы

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

И теперь если посмотрите на трассировку, то можете задаться вопросом как паять светодиодную матрицу, если батарейка находится на противоположной стороне, чем МК? Там получается, что медь находится на слое микроконтроллера, а на обратной стороне меди нет, т.к. плата одностороняя. А по-хорошему нужно, чтобы на стороне, где батарейка, тоже была медь.

Здесь есть несколько решений: 1. сделать метализацию отверстий или заказать плату у тех, кто сделает; 2. можно взять двухстороннюю плату и добавить дополнительные переходные отверстия, но размер устройства увеличится, что плохо и это еще сложно; 3. использовать латунные заклепки.

Заклепки - краеугольный камень данного проекта
Заклепки - краеугольный камень данного проекта

Я выбрал третий и как можете догадаться он сработал, но про них я расскажу поподробнее, когда перейду к разделу про фрезировку.

SimulIDE

Обычно, не обязательно делать схему в SimulIDE, но пока едет комплектующее очень полезно подготовить какую-ту базу. Для начала, хотя бы сделать бегущий светодиод.

Прототип или схема в SimulIDE
Прототип или схема в SimulIDE

Для бегущего светодиода адаптировал код из примеров Arduino.

Скрытый текст
#include <Arduino.h>

#define COLUMN1 0  // D0
#define COLUMN2 1  // D1
#define COLUMN3 2  // D2
#define COLUMN4 3  // D3
#define COLUMN5 4  // D4
#define COLUMN6 5  // D5
#define COLUMN7 6  // D6
#define COLUMN8 7  // D7

#define ROW1 8   // B0
#define ROW2 9   // B1
#define ROW3 10  // B2
#define ROW4 11  // B3
#define ROW5 12  // B4
#define ROW6 13  // B5
#define ROW7 14  // C0
#define ROW8 15  // C1

const int row[8] = {
  ROW1, ROW2, ROW3, ROW4, ROW5, ROW6, ROW7, ROW8
};

const int col[8] = {
  COLUMN1, COLUMN2, COLUMN3, COLUMN4, COLUMN5, COLUMN6, COLUMN7, COLUMN8
};

int pixels[8][8];

int x = 0;
int y = 0;

unsigned long prevTime;
unsigned long duration;

void setup() {
  for (int thisPin = 0; thisPin < 8; thisPin++) {
    pinMode(col[thisPin], OUTPUT);
    pinMode(row[thisPin], OUTPUT);
    digitalWrite(col[thisPin], HIGH);
    digitalWrite(row[thisPin], HIGH);
  }

  for (int x = 0; x < 8; x++) {
    for (int y = 0; y < 8; y++) {
      pixels[x][y] = LOW;
    }
  }

  prevTime = millis();
}

void loop() {
  unsigned long t = millis();
  duration += (t - prevTime);
  prevTime = t;
  if (duration > 100) {
    duration = 0;

    x += 1;
    if (x >= 8) {
      x = 0;
      y += 1;
      if (y >= 8) {
        y = 0;
      }
    }

    for (int i = 0; i < 8; i++) {
      for (int j = 0; j < 8; j++) {
        pixels[i][j] = x == j && y == i ? HIGH : LOW;
      }
    }
  }

  refreshScreen();
}

void refreshScreen() {
    for (int thisRow = 0; thisRow < 8; thisRow++) {
      digitalWrite(row[thisRow], LOW);

      for (int thisCol = 0; thisCol < 8; thisCol++) {
          int thisPixel = pixels[thisCol][thisRow];
          digitalWrite(col[thisCol], thisPixel);
          if (thisPixel == HIGH) {
              digitalWrite(col[thisCol], LOW);
          }
      }

      digitalWrite(row[thisRow], HIGH);
  }
}

Это базовый код, все остальное уже специфика, важная, но для понимания не критичная.

Видео с получившимся результатом:

Заказываю комплектующее: MAX7219

После того, как сделана подготовительная работа, можно спокойно заказывать комплектующее. И первым делом я заказываю матрицу 1088AB с управлящей платой на MAX7219, хоть я и решил напрямую подключать светодиодную матрицу, все же было важно посмотреть как это обычно делают, может пригодится. И пригодилось!

MAX7219
MAX7219

Только после того, как я поддержал MAX7219 в руках, понял как должна крепиться светодиодная матрица - через штырьевые разьемы, которые можете видеть на картинке выше по краям. И теперь все встало на места, а то я изначально хотел матрицу припаивать.

Разработка

Фрезеровка

Следующим по важности идет этап фрезеровки. Управляющую программу я подготавливаю во FlatCAM:

Отображение управляющей программы
Отображение управляющей программы

Про управляющую программу немного позже, а теперь настало время рассказать про ключевой элемент данного проекта - латуные заклепки.

Латунные заклепки это устаревший способ соединить два слоя печатной платы. Для наших целей есть заклепки диаметром отверстия М0.9, М1.3 и М1.5, дальше только больше диаметром (заклепок, например, М1.0, M1.1, M1.2 я не нашел) и продаются они только на Алиэкспресс, в других местах я не нашел. Еще можно выбрать длинну: 2.5 или 3 мм для нашего случая, есть только больше.

Я успел заказать все варианты этих заклепок, благо они стоят не дорого. Начал с М0.9 на 2.5 мм и они почти подошли, но штырьевые разьемы под светодиодную матрицу в них еле влезли, пришлось стучать молотком и я побоялся все сломать, поэтому стал пробовать другие варианты. Хотя, забегая вперед, другие варианты оказались не сильно лучше и можно было бы остаться на М0.9 и 2.5 мм.

Второй вариант был М1.5 на 3 мм и они совсем не подходят.

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

Как заклепки в итоге получились
Как заклепки в итоге получились

Возвращаясь к управляющей программе, можно разглядеть 5 слоев: изолирующий, для снятия лишней меди, два для отверстий и один для контура. В первых итерациях я пытался без двух слоев: для снятия лишней меди и отверстия с 1.3 мм. Но если не снимать лишнюю медь, заклепки будут касаться друг друга, а если отверстия 1.3 мм сверлить вручную, а не на станке, то медь оторвется. Поэтому все эти слои нужны.

Печатная плата после фрезеровки. Для следующего брелка
Печатная плата после фрезеровки. Для следующего брелка

Программирование

Проблемы тестового запуска и причем тут ресет

Все припаяно и готово к программированию
Все припаяно и готово к программированию

Прошло где-то месяц или два, когда у меня в руках появилось устройство брелка и можно приступать к программированию. Сначало все паяю и у меня словно 3 контакта не пропаялись, такое может быть, т.к. CH32V003FP6 в достаточно маленьком корпусе, TSSOP-20. Пропаиваю и отваливается теперь один контакт. Что я не делал, ничего не получалось, оказалось, что 4-я ножка, PD7, зарезервированна под ресет, NRST. Но проблема решается просто, нужно в WCH-LinkUtility выставить PD7 как GPIO. Теперь все светодиоды горят на матрице, затея удалась, можно приступать к наполнению кодом.

Как изменить ножку PD7 на GPIO, вместо NRST
Как изменить ножку PD7 на GPIO, вместо NRST

Код

Для программирования я использую PlatformIO и фреймворк ch32fun. По крайней мере, с этого начал, но проект в итоге превратился в монстра, которого проще было сделать, если начать с фреймворка Arduino, т.к. CH32V003 это поддерживает. Но как получилось, мне тоже нравится, потому что с ch32fun вышло в 2 раза меньше памяти как оперативной, так и флеш.

А почему с Arduino? Так потому что конечный проект может создавать прошивку не только под CH32V003, но и под SimulIDE. Надо только выбрать нужное окружение. Проще было бы, если был один фреймворк, но с двумя тоже получилось.

Была, конечно, мысль написать все с нуля, но в роликах на YouTube, с которых все начиналось, уже был код. Я его и взял. Сначало взял с первого, затем со второго. Во втором, конечно, много чего не было, но получилось восстановить. От себя я добавил обработку русского языка, получилось, конечно, плохо, не тестировал весь алфавит, но слово “Привет” пишет хорошо.

Ключевая особенность, которая отличает мой проект от их, это обработка кнопки и светодиодной матрицы. (Плюс у меня нету режима сна, т.к. вместо него у меня выключатель. Но и режим сна тоже можно всегда добавить.) Чтобы светодиодная матрица светилась, нужно вызывать каждый кадр функцию refreshScreen. У них этим пареллельно занимается MAX7219 и МК может спокойно заниматься своими делами. Вот только обработка кнопки у них была не удобная, не отзывчивая, я сделал так, что при нажатии на кнопку сразу меняется анимация, а не ожидается, когда закончится текущая.

И тут вылезает интересный момент как сделана анимация. Анимация работает по следующему алгоритму: устанавливаем кадр для отображения, устанавливаем яркость и сколько-то ждем, затем повторяем для следующего кадра - все. Только в нашем случае, т.к. матрицей управляем мы, а не MAX7219, если начать ждать, то ничего светиться не будет, потому что не вызывается refreshScreen, поэтому я сделал свою функцию delay, которая и ждет и обновляет матрицу каждый кадр, плюс, если нажалась кнопка, то все последующии delay сбрасываются и таким образом происходит переход к следующей анимации.

Нажмите, чтобы посмотреть код
bool App::updateInternal() {
    if (_buttonIsPressed) {
        return true;
    }

    ledMatrix.refreshScreen();
    if (digitalRead(BUTTON) == LOW && !_buttonIsPressed) {
        _buttonWasPressed = true;
        _buttonIsPressed = true;
    }

    return _buttonIsPressed;
}

void App::delay(uint32_t d) {
    uint32_t prevTime;
    uint32_t duration = 0;
    uint32_t d2 = Ticks_from_Ms(d);

    prevTime = SysTick->CNT;
    while (true) {
        uint32_t time = SysTick->CNT;
        uint32_t tmp = (time - prevTime);
        prevTime = time;
        duration += tmp;
        if (duration > d2) {
            break;
        }

        if (updateInternal()) {
            break;
        }
    }
}

Видео с финальном вариантом, на SimulIDE:

3д печать

Пришлось подумать над тем, как сделать корпус для брелка. Сначало было непонятно как крепить, в итоге нашел место под саморезы М2 и все остальное само решилось.

Модель корпуса во FreeCAD
Модель корпуса во FreeCAD

В роликах на YouTube был брелок в черном корпусе, я сначало сделал такой же, с толщиной стенки, где матрица, в 0.12мм, но через него очень слабо светит. Ситуация лучше, если питать плату напрямую, от программатора.

Через черный корпус, при дневном свете, ничего не видно
Через черный корпус, при дневном свете, ничего не видно
Если питать от программатора, то все лучше
Если питать от программатора, то все лучше

В белом корпусе все видно, даже когда питается от батарейки и при дневном свете. Слабо, но видно.

В белом корпусе таких проблем нет
В белом корпусе таких проблем нет

Результат

Финальное видео с физическим брелком:

Подводя итог, затея удалась, но некоторые проблемы остались. Основная - светит слабо. Возможно это из-за того, что использовал 220 Ом резисторы, в следующем брелке попробую 100, но не думаю, что станет лучше, но попробовать стоит. Возможно, это плохая батарейка, но уже попробовал две и результат не сказать, что стал заметно лучше. Скорее всего проблема связана с тем, что МК не выдерживает, т.к. в максимуме в один кадр может свтетится восемь светодиодов, хотя это не много, но все же, других везких причин у меня нет. Еще заметил, что если матрицу вставить по-другому, то на ней загораются проивзольные линии, но светит ярко, возможно, надо просто код еще посмотреть.

Но брелок же светится! Жалко, что не ярко, но все же.

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

Вот такая, казалось бы небольшая игрушка, обернулось многими сложностями, надеюсь моя статья была интересно и никого не отпугнет заниматься электроникой.

Все файлы к проекту можете найти в репозитории LedKeychain. Теперь при желании любой может повторить этот проект!

Tags:
Hubs:
+4
Comments0

Articles