ARDUINO Ардуино играет музыку на бокалах

Ардуино играет музыку на бокалах


Схема

glass-harp.jpg



Скетч
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; //если достигли конца массива, то сбрасываем в начало
}
 
Изменено:

Комментарии

CyberLab

★✩✩✩✩✩✩
7 Сен 2018
48
25
Москва
Может ошибка как раз в отсутствии паузы ?
Сделать что то типа
STEP_1;
пауза 1-4 мкс.
STEP_0;
пауза пауза на движение.
Попробовал с паузой и без. нет разницы. Если тупо подать высокий уровень подождать 1000мкс и сбросить на низкий , то мотор так же отрабатывает 1 шаг как и с импульсом. Никакой разницы во врщении не заметил. Но думаю лучше сразу сделать как нужно, что бы в дальнейшем проблем не возникало
 

CyberLab

★✩✩✩✩✩✩
7 Сен 2018
48
25
Москва
Добился максимальной скорости, при задержке 780 мкс
Опорное напряжение 1В при R100

Видео тестов


Код который я использовал
C++:
#define STEP_1 PORTB |= B00000010
#define DIR_1  PORTB |= B00000001
#define STEP_0 PORTB &= B11111101
#define DIR_0  PORTB &= B11111110

#define step_num 400        // количество шагов на 1 оборот
#define step_impuls 10    // импульс для запуска шага в мкс
#define step_duration 800  // длительность шага

void setup()
{
 
  pinMode(8, OUTPUT); // DIR
  pinMode(9, OUTPUT); // STEP
  STEP_0;
  DIR_0; 
}

void loop()
{

steps(800);
delay(1000);
steps(-800);
delay(1000);
steps(400);
delay(1000);
steps(-400);
delay(1000);
}

//******************
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;
    delayMicroseconds(step_impuls);
    STEP_0;
    delayMicroseconds(step_duration-step_impuls);
  }
}
 
Изменено:

CyberLab

★✩✩✩✩✩✩
7 Сен 2018
48
25
Москва
Подправил код, что бы можно было использовать без библиотек
В полношаговом режиме задержка для 1 шага 1500 мкс
В полушаговом 800*2=1600 мкс
Разница во времени действительно небольшая.

Скрин из даташита на А4988
a4988.png
 
Изменено: