Схема
Скетч
C++:
#include "Adafruit_NeoPixel.h"
#include "CyberLib.h"
#define DEBUG false //false true включить режим отладки
#define DIR_1 D8_High
#define STEP_1 D9_High
#define HAMMER_1 D10_High
#define DIR_0 D8_Low
#define STEP_0 D9_Low
#define HAMMER_0 D10_Low
#define WS2812_PIN 11 // выход для подключения ws2812
#define step_num 400 // количество шагов на 1 оборот ШД. включен полушаговый режим для снижения шума
#define note_num 7 // Количество нот-бокалов
#define step_note step_num/note_num // количество шагов двигателя между нотами. вычисляет автоматически
#define step_duration 950 // длительность шага влияет на скорость ШД. чем ниже значение тем быстрее , но есть вероятность пропуска шагов
#define ratio 1.3 // коэфициент делитель длительности отрицательного импульса шага
#define kick_duration 8 // длительность удара молоточка в мс
#define tact 6 // количество тактов за 1 сек. но нужно учитывать скорость перемещения ШД от ноты к ноте
#define tact_us 1000000/tact // длительность такта в мкс. вычисляет автоматически
#define NUM_PIX 1 // количество светодиодов в ленте
Adafruit_NeoPixel strip = Adafruit_NeoPixel(NUM_PIX, WS2812_PIN, NEO_GRB + NEO_KHZ800);
volatile uint8_t tact_num=0, int_state=0;
uint8_t sound=0;
int next_sound=0;
const uint8_t music[] PROGMEM = {255,255,0,1,2,255,2,255, 2,3,4,255,4,255,4,5, 3,255,3,255,2,1,1,2,
255,255,0,1,2,255,2,255, 2,3,4,255,4,255,4,5, 3,255,3,255,2,1,2,255,255,255 };
/* const uint8_t music[] PROGMEM = { 255,255,2,255,2,255,2,255, 255,255,2,255,2,255,2,255, 255,255,2,255,4,255,0,255,
255,1,2,255,255,255,255,255, 255,255,3,255,3,255,3,255, 255,2,3,255,2,255,2,255,
1,2,255,1,255,1,255,2, 255,1,255,255,255,4,255,255, 255,2,255,2,255,2,255,255,
255,2,255,2,255,2,255,255, 255,2,255,4,255,0,255,255, 1,2,255,255,255,255,255,255,
255,3,255,3,255,3,255,255, 2,3,255,2,255,2,255,255, 2,4,255,4,255,3,255,1,
255,0,255,255,255,255,255,255 };
const uint8_t music[] PROGMEM = { 255,255, 5,3,0,3,5,3,0,3, 5,2,0,2,5,2,0,2, 4,2,0,2,4,2,0,2, 4,2,0,2,4,2,0,2,
5,3,0,3,5,3,0,3, 5,3,0,3,5,3,0,3, 5,2,0,2,5,2,0,2, 5,2,0,2,5,2,0,2,
5,3,1,3,5,3,1,3, 5,3,1,3,4,3,1,3, 6,4,2,4,6,4,2,4, 6,2,0,2,5,2,0,2,
5,3,0,3,5,3,0,3, 6,3,0,3,6,3,0,3, 255,255,255,255 }; */
// const uint8_t music[] PROGMEM = { 255,0,1,2,3,4,5,6,5,4,3,2,1,0,255 }; // для расстановки бокалов
void setup()
{
D10_Out;
HAMMER_1; D8_Out; D9_Out; DIR_0; STEP_0; //Настраиваем пин D8, D9 и D10 на выход
D12_In; D12_High;
pinMode(12, INPUT_PULLUP);
randomSeed(analogRead(0)); // получаем начальное значение случайного числа
strip.begin();
strip.setBrightness(255); // яркость светодиода на максимум
strip.setPixelColor(0,0,0,0);
strip.show();
while(D12_Read){} // ждем нажатия кнопки
#if DEBUG // для отладки кода
Serial.begin(115200);
#endif
StartTimer1(tempo, tact_us); // запуск таймера, первый параметр это обработчик прерывания
}
void loop()
{
if( int_state ) // если было прерывание по таймеру
{
cli(); // запрет прерываний что бы избежать нарушений последовательности тактов
uint8_t tmpS = pgm_read_byte_near(&music[tact_num]); // текущая нота
uint8_t tmpN;
if(tact_num != sizeof(music)-1)
{
tmpN = pgm_read_byte_near(&music[tact_num+1]); // следующая нота
} else tmpN = pgm_read_byte_near(&music[0]); // иначе в начало массива
sei();
if(tmpN != 255) next_sound = tmpN; // проверяем следующий сэмпл на пустой такт
if(tmpS != 255) // если не пустой такт, то ударяем молоточком и перемещаемся на следующую ноту
{
HAMMER_0;
sound = tmpS;
uint8_t r=random(2); if(r) r=255;
uint8_t g=random(2); if(g) g=255;
uint8_t b=random(2); if(b) b=255;
if(r==0 && g==0) b=255;
strip.setPixelColor(0,r,g,b);
strip.show();
delay_ms(kick_duration); // длительность импульса удара. Можно регулировать силу удара
HAMMER_1;
}
#if DEBUG //для отладки кода
Serial.println(tact_num);
Serial.println();
#endif
steps((next_sound-sound) * step_note); // перемещаем на следующий бокал
int_state=0; // сбросить флаг прерывания
}
}
//******************выполнение шагов************************
void steps(int shag)
{
if(shag>0) { DIR_1; } else { DIR_0; shag=abs(shag); } // проверка направления вращения, убираем знак(-)
for(uint16_t i = 0; i < shag; i++) // Выполняем заданное количество шагов
{
STEP_1;
delay_us(step_duration); // длительность шага , влияет на скорость вращения
STEP_0;
// delay_us(step_duration/ratio);
}
}
//****************обработчик прерывания таймера1***********
void tempo()
{
int_state=1; // флаг прерывания установлен
tact_num++; // добавим 1 такт
if( tact_num > sizeof(music)-1) tact_num=0; //если достигли конца массива, то сбрасываем в начало
}
Изменено: