АУДИО Сенсорная USB MIDI клавиатура на STM32F103

Azq2

✩✩✩✩✩✩✩
28 Ноя 2018
8
5
Всегда было интересно поиграть на пианино, но всё никак не доходили руки купить MIDI клавиатуру.
Поэтому решил собрать её сам, естественно из говна и палок.

К сожалению, у меня не было даже игрушечной MIDI клавиатуры, чтобы переделать в нормальную. А самому делать механическую клавиатуру - очень сложно.
Поэтому решил остановиться на сенсорном варианте.

Идея простая - берём листовую жесть, вырезаем из неё кусочки по форме кнопок пианино, крепим на любую подходящую по размерам дсоку.... и всё, готово!

С электричествой точки зрения там всё просто. Берём кусочек фольги/жести, подключаем к GPIO и всё, готово:

scheme.png

Да, чтобы сделать сенсорную кнопку больше ничего не нужно. %)

Как же это работает?

На самом деле всё просто. МК в бесконечном цикле замеряет время, за которое пин gpio переходит из состояния 0 в 1.
Если пин gpio подключен не напрямую к питанию, а через резистор, то этот переход будет занимать достаточно большое время.

Весь алгоритм выглядит так:
  1. Переключаем пин в INPUT с PULL-UP
  2. Замеряем время, за которое пин перейдёт в состояние 1 (на этом этапе напряжение >1.88V)
  3. Далее переводим пин в OUTPUT=1, чтобы паразитный конденсатор полностью зарядился до 3.3V
  4. Переключаем пин в INPUT с PULL-DOWN
  5. Замеряем время, за которое пин перейдёт в состояние 0 (на этом этапе напряжение <1.23V)
  6. Далее переводим пин в OUTPUT=1, чтобы паразитный конденсатор полностью разрядился до 0V
В спокойном состоянии это время константно и практически не изменяется во время работы.
Но когда мы приближаем палец к сенсорной кнопке, происходит утечка тока + ловятся наводки (палец=антенна). Из-за этого время заряда/разряда начинает отклоняться в большую и меньшую сторону.
Вот наличие этого отклонения считаем за "нажатую кнопку".

Как видно из алгоритма, я использую встроенные pullup/pulldown резисторы.
Скорость работы STM32F103 позволяет так делать.
В ином случае случае мне пришлось бы к каждому "сенсору" припаивать более высокоомный резистор, который управлялся бы с дополнительного пина gpio.

У меня этот код выглядит так:
C++:
int App::measureRechargeTime(const Pin &p) {
    int start = 0, count = 0;
    for (int i = 0; i < SAMPLES_CNT; i++) {
        // Switch to input
        gpio_clear(p.port, p.pin);
        gpio_set_mode(p.port, GPIO_MODE_INPUT, GPIO_CNF_INPUT_PULL_UPDOWN, p.pin);
     
        // Charge to >1.88V and measure elapsed time
        start = DWT_CYCCNT;
        gpio_set(p.port, p.pin);
        while (!gpio_get(p.port, p.pin));
        count += DWT_CYCCNT - start;
     
        // Complete charge to 3.3V
        gpio_set_mode(p.port, GPIO_MODE_OUTPUT_50_MHZ, GPIO_CNF_OUTPUT_PUSHPULL, p.pin);
     
        // Discharge to <1.23V and measure elapsed time
        gpio_set_mode(p.port, GPIO_MODE_INPUT, GPIO_CNF_INPUT_PULL_UPDOWN, p.pin);
     
        start = DWT_CYCCNT;
        gpio_clear(p.port, p.pin);
        while (gpio_get(p.port, p.pin));
        count += DWT_CYCCNT - start;
     
        // Complete discharge to 0V
        gpio_set_mode(p.port, GPIO_MODE_OUTPUT_50_MHZ, GPIO_CNF_OUTPUT_PUSHPULL, p.pin);
    }
    return count / SAMPLES_CNT;
}
Остаётся только при включении устройства измерить эталонное время и далее сравнивать с ним текущее измеренное.
Далее добавить алгоритм дебоунсинга по вкусу и подобрать порог срабатывания. Вот и всё.

Разработка хардварной части

Естественно, рисовать вручную все эти кнопочки занятие долгое и неинтересное, поэтому я написал скрипт на PHP, который генерирует чертёж MIDI клавиатуры.

Найти его можно тут: https://github.com/Azq2/stm32-sensor-midi-keyboard/blob/master/gen-piano.php

Скрипт поддерживает MIDI клавиатуры: 13, 25, 37, 49, 61, 76 и 88 кнопок.
Так же внутри скрипта можно тюнить отступы и размеры кнопок.

Использование простое:

Bash:
# php gen-piano.php <number of keys>
php gen-piano.php 25
На выходе получаем три чертежа в /tmp

Вот пример для 25-клавишной клавиатуры (для этого проекта):

1. Чертёж ддя вырезания белых кнопок



Хвостики нужны чтобы было удобно устанавливать кнопки на доску + припаивать к ним провода.

2. Чертёж для вырезания чёрных кнопок



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

3. Чертёж для сверления доски



Здесь кнопки расположены уже с рабочими отступами и нарисованы метки для сверления.

Все чертежи для проекта можно найти тут: https://github.com/Azq2/stm32-sensor-midi-keyboard/tree/master/docs

Скрипт генерирует картинки с разрешением 300ppi, т.е. достаточно создать в фотошопе или GIMP лист A4 с 300ppi и вставить туда одну из картинок.
А далее распечатать и вырезать!

За основу клавиатуры я выбрал коробку от фруктов:

1.jpg

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

2.jpg

🤦‍♂️🤦‍♂️🤦‍♂️🤦‍♂️

В актуальных чертежах я исправил эту ошибку.

После окончательной установки выглядит так:

3.jpg

Далее необходимо припаять все 9999999 проводов к bluepill.
Порядок на самом деле не так важен, его можно потом исправить в коде, главное не использовать: PA9 и PA10 (USART1), PA11 и PA12 (USB)

Вот порядок, который изначально прописан в коде:

PinNote
PB13C3
PB14C3#
PB15D3
PB12D3#
PA8E3
PB8F3
PB9F3#
PB7G3
PB3G3#
PA1A3
PB1A3#
PA0B3
PB4C4
PB0C4#
PA7D4
PA2D4#
PA4E4
PB5F4
PB11F4#
PA5G4
PA15G4#
PA3A4
PA6A4#
PB6B4
PB10C5

На самом деле в этом списке нет никакой логики, т.к. паял я их от балды :)

Их слишком много, чтобы заудымваться о каком-либо логичном порядке))))

4.jpg

После этого нужно покрыть конакты чем-нибудь диэлектрическим.
Это на самом деле важный этап, чтобы все эти потанцевалы ~110V не спалили лапки микроконтроллёра.

Я пробовал эпоксидную смолу, но ничего хорошего с ней у меня не получилось из-за её текучести и скорости застывания.
Лучше всего себя показал обычный лак для ногтей. У него и слой тонкий получается и застывает быстро.

Главное чтобы девушка/жена/мамка не змаетила пропажу лака :)

Конечный вариант конструкции выглдяит так:



Not great not terrible)))))

Оставил место для будущих кнопок, если вдруг захочется расширить 37 или 49 клавиш.
Кстати, тут нехватает одной кнопки в конце, мне пока лень её вырезать 🤦‍♂️

Демо

Как доказательство того, что эта странная конструкция действительно работает, я записал демо-видео:


P.S. Играть я не умею, просто попытался сыграть начало рандомной композии, ровно до первого сложного момента 🤣

Настройка

Если вдруг кто-нибудь решиться повторить эту конструкцию, то, скорее всего, ничего у ничего не заработает с первого раз.
Получается, эта статья обман??? 😱
На самом деле нет!
Дело в том, что у разных конструкций будут разные физические параметры, разная паразитная ёмкость, разная площадь контактов, разная длина проводов... На самом деле это всё очень сильно влияет.

Сразу после сборки, скорее всего, придётся подтюнить несколько параметров:

ParameterDefaultDescription
SENSOR_WHITE_KEY_THRESHOLD1000Порог срабатывания, чтобы считать кнопку нажатой
Это значение на самом деле проценты умноженные на 100 (чтобы использовать целые числа вместо float)
100000=100%
100=1%
SENSOR_BLACK_KEY_THRESHOLD1300Порог срабатывания, чтобы считать кнопку нажатой
Это значение на самом деле проценты умноженные на 100 (чтобы использовать целые числа вместо float)
100000=100%
100=1%

Остальные настройки:

ParameterDefaultDescription
NOTE_OFFSET48Нота MIDI для первой кнопки: 36 - C2, 48 - C3.
Вот тут полный список можно посмотреть: https://studiocode.dev/resources/midi-middle-c/
KEY_VELOCITY64Сила нажатия, 0-127.
DEBOUNCE_CYCLES1000000Время дебоунсинга в циклах CPU, нужно увеличить, если есть "дребезг" нажатия и уменьшить, если вдруг нехватает скорости нажатия одной кнопки.
(Для тех, кто хочет сыграть Bad Apple со скоростью 999999 нажатий в наносекунду? :D)
CALIBRATION_CYCLES100Кол-во циклов для усреднения значения при калибровке.
SAMPLES_CNT8Кол-во циклов для усреднения при чтении времени заряда/разряда паразитной ёмкости.
DEBUG_SORT_KEYSfalseРежим для генерации нового App::m_pins с правильной сортировкой.
DEBUG_PRESS_KEYSfalseРежим отладки, при котором выводится текущее отклонение от калибровочного значения и дельта между нажатиями
Используется для подбора порогов срабатывания и дебоунсинга.

Все эти настройки можно найти в файле src/App.h

Исправления порядка клавиш

1. Сначала устанавливаем DEBUG_SORT_KEYS=true и подключаемся по USART.
2. Нажимаем все кнопки по очереди, слева на право.
3. Копируем содержимое структуры, которое вывело в USART.
4. Заменяем содержимое структуры App::m_pins в файле: src/App.cpp

Настройка чувствительности

1. Сначала устанавливаем DEBUG_PRESS_KEYS=true и подключаемся по USART.
2. Нажимает на интересующие клавиши.
3. Если что-то не нравится, меняем SENSOR_BLACK_KEY_THRESHOLD и SENSOR_WHITE_KEY_THRESHOLD до тех пор, пока не начнёт работать нормально.
4. Если нажатия троят, то увеличиваем DEBOUNCE_CYCLES (если тормозят, то уменьшаем).

Сборка прошивки

Для сборки необходим любой дистрибутив Linux. Хоть даже WSL под Windows.
Для сборки необходимо установить компилятор arm-none-eabi: https://developer.arm.com/downloads/-/gnu-rm

Его нужно распаковать в /opt и прописать в конец файла ~/.bashrc строчку вида:
Bash:
export PATH="$PATH:/opt/gcc-arm-none-eabi-10.3-2021.10/bin"
Сама сборка:
Bash:
git clone https://github.com/Azq2/stm32-sensor-midi-keyboard
cd stm32-sensor-midi-keyboard
git submodule init && git submodule update
./build_libopencm3.sh
make
После сборки вы получите app.bin и app.elf
Вы можете прошить один из этих файлов любым удобным для вас способом.

В случае использования Black Magic Probe достаточно выполнить команду:
Bash:
make install
Как это использовать?

Под Linux достаточно просто подключить по USB и всё сразу заработает
А вот что делать под OSX/Windows я честно говоря понятия не имею :)

В коде я использую стандартные pid/vid для DIY MIDI устройств:

Код:
Bus 001 Device 019: ID 16c0:05e4 Van Ooijen Technische Informatica Free shared USB VID/PID pair for MIDI devices
Исходный код

Репозиторий проекта на github: https://github.com/Azq2/stm32-sensor-midi-keyboard
 
Изменено:

Эдуард Анисимов

★★★★★★✩
23 Сен 2019
2,304
949
58
Марий-Эл
У STM есть МК, в которых встроен контроллер сенсорной клавиатуры.
Может лучше на таких делать?
Там даже можно силу нажатия контролировать.
 

AZM

✩✩✩✩✩✩✩
17 Сен 2023
22
5
когда то очень давно в 90е годы делал сенсорную клавиатуру для компьютера самодельного. По одному транзистору на сенсор кт315 и одному резистору 100ком. база на площадку сенсорную, резистор между базой и эмиттером. в эмиттер подаешь импульс нулем, так потенциал +4 вольта торчит. Импульсы с дешифратора типа 155ид3 16 каналов. коллекторы обьедены и через резистор 10ком на +5вольт. Дергаем эимттер нулем, если дотронуться до сенсора даже через фотобумагу и пленку прозрачную, в коллекторе появляеться импульс, 0 вольт на короткое время. Его и фиксируем. Создавал матрицу из сенсоров 16х4=64 сенсора. Дергаешь эмиттер и снимаешь с коллекторов импульсы. Добавил кучу логики и получил последовательный код сработавшего сенсора в компьютер. Работало четко все и без проблем. Все это было нарисовано на 2 стороннем текстолите. с одной стороны сенсоры, с другой транзисторвы резисторы. smd тогда еще не было. Так можно делать разные клавиатуры для разных целей.
 

mehub

✩✩✩✩✩✩✩
12 Дек 2020
28
9
Лично по мне: двух октав - мало. Для большинства мелодий- четыре-вполне подойдут.