Помогите с millis, чтобы получить точное время

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

    #include <Servo.h>

//defining the value of the notes
#define NOTE_C4  262
#define NOTE_D4  294
#define NOTE_E4  330
#define NOTE_F4  349
#define NOTE_G4  392
#define NOTE_A4  440
#define NOTE_B4  494
#define NOTE_C5  523
#define NOTE_D5  587
#define NOTE_E5  659
#define NOTE_F5  698
#define NOTE_G5  784
#define NOTE_A5  880
#define NOTE_B5  988

//order of the notes
int melody[] = {
   NOTE_E4, NOTE_G4, NOTE_A4, NOTE_A4, 0, 
   NOTE_A4, NOTE_B4, NOTE_C5, NOTE_C5, 0, 
   NOTE_C5, NOTE_D5, NOTE_B4, NOTE_B4, 0,
   NOTE_A4, NOTE_G4, NOTE_A4, 0,
   
   NOTE_E4, NOTE_G4, NOTE_A4, NOTE_A4, 0, 
   NOTE_A4, NOTE_B4, NOTE_C5, NOTE_C5, 0, 
   NOTE_C5, NOTE_D5, NOTE_B4, NOTE_B4, 0,
   NOTE_A4, NOTE_G4, NOTE_A4, 0,
   
   NOTE_E4, NOTE_G4, NOTE_A4, NOTE_A4, 0, 
   NOTE_A4, NOTE_C5, NOTE_D5, NOTE_D5, 0, 
   NOTE_D5, NOTE_E5, NOTE_F5, NOTE_F5, 0,
   NOTE_E5, NOTE_D5, NOTE_E5, NOTE_A4, 0,
   
   NOTE_A4, NOTE_B4, NOTE_C5, NOTE_C5, 0, 
   NOTE_D5, NOTE_E5, NOTE_A4, 0, 
   NOTE_A4, NOTE_C5, NOTE_B4, NOTE_B4, 0,
   NOTE_C5, NOTE_A4, NOTE_B4, 0,

   NOTE_A4, NOTE_A4, 

   NOTE_A4, NOTE_B4, NOTE_C5, NOTE_C5, 0, 
   NOTE_C5, NOTE_D5, NOTE_B4, NOTE_B4, 0,
   NOTE_A4, NOTE_G4, NOTE_A4, 0,

   NOTE_E4, NOTE_G4, NOTE_A4, NOTE_A4, 0, 
   NOTE_A4, NOTE_B4, NOTE_C5, NOTE_C5, 0, 
   NOTE_C5, NOTE_D5, NOTE_B4, NOTE_B4, 0,
   NOTE_A4, NOTE_G4, NOTE_A4, 0,
   
   NOTE_E4, NOTE_G4, NOTE_A4, NOTE_A4, 0, 
   NOTE_A4, NOTE_C5, NOTE_D5, NOTE_D5, 0, 
   NOTE_D5, NOTE_E5, NOTE_F5, NOTE_F5, 0,
   NOTE_E5, NOTE_D5, NOTE_E5, NOTE_A4, 0,
   
   NOTE_A4, NOTE_B4, NOTE_C5, NOTE_C5, 0, 
   NOTE_D5, NOTE_E5, NOTE_A4, 0, 
   NOTE_A4, NOTE_C5, NOTE_B4, NOTE_B4, 0,
   NOTE_C5, NOTE_A4, NOTE_B4, 0,

   NOTE_E5, 0, 0, NOTE_F5, 0, 0,
   NOTE_E5, NOTE_E5, 0, NOTE_G5, 0, NOTE_E5, NOTE_D5, 0, 0,
   NOTE_D5, 0, 0, NOTE_C5, 0, 0,
   NOTE_B4, NOTE_C5, 0, NOTE_B4, 0, NOTE_A4,

   NOTE_E5, 0, 0, NOTE_F5, 0, 0,
   NOTE_E5, NOTE_E5, 0, NOTE_G5, 0, NOTE_E5, NOTE_D5, 0, 0,
   NOTE_D5, 0, 0, NOTE_C5, 0, 0,
   NOTE_B4, NOTE_C5, 0, NOTE_B4, 0, NOTE_A4
};

// duration of each note
int duration[] = {
  125, 125, 250, 125, 125, 
  125, 125, 250, 125, 125,
  125, 125, 250, 125, 125,
  125, 125, 375, 125, 
  
  125, 125, 250, 125, 125, 
  125, 125, 250, 125, 125,
  125, 125, 250, 125, 125,
  125, 125, 375, 125, 
  
  125, 125, 250, 125, 125, 
  125, 125, 250, 125, 125,
  125, 125, 250, 125, 125,
  125, 125, 125, 250, 125,

  125, 125, 250, 125, 125, 
  250, 125, 250, 125, 
  125, 125, 250, 125, 125,
  125, 125, 375, 375,

  250, 125,

  125, 125, 250, 125, 125,
  125, 125, 250, 125, 125,
  125, 125, 375, 125, 
  
  125, 125, 250, 125, 125, 
  125, 125, 250, 125, 125,
  125, 125, 250, 125, 125,
  125, 125, 375, 125, 
  
  125, 125, 250, 125, 125, 
  125, 125, 250, 125, 125,
  125, 125, 250, 125, 125,
  125, 125, 125, 250, 125,

  125, 125, 250, 125, 125, 
  250, 125, 250, 125, 
  125, 125, 250, 125, 125,
  125, 125, 375, 375,
  
  250, 125, 375, 250, 125, 375,
  125, 125, 125, 125, 125, 125, 125, 125, 375,
  250, 125, 375, 250, 125, 375,
  125, 125, 125, 125, 125, 500,

  250, 125, 375, 250, 125, 375,
  125, 125, 125, 125, 125, 125, 125, 125, 375,
  250, 125, 375, 250, 125, 375,
  125, 125, 125, 125, 125, 3000
};

Servo myservo;  // create servo object to control a servo

int servoPin = 9;
int ledPin = 11;
int buzzerPin = 3;
int sensorPin = A4;
int sweep = 0; // variable to control which direction the servo is moving
int sensorValue = 0;
int counter = 0; //a counter to keep track of which note the song is up to
float tempo = 1; // a variable for the tempo - lower number = faster temp (2 =  double speed)
unsigned long currentMillis = 0;
unsigned long previousMillis = 0;
int threshold = 500;

void setup()
{
  myservo.attach (servoPin);
  pinMode (ledPin, OUTPUT); 
  pinMode (buzzerPin, OUTPUT);
  pinMode (sensorPin, INPUT);
  Serial.begin(9600); 
}


void loop(){
  
sensorValue = analogRead (sensorPin);
if (sensorValue > threshold)
{
  
  if (sweep == 0)
  {
    
    for (int pos = 0; pos <= 180; pos ++) // goes from 0 degrees to 180 degrees in steps of 1 degree
    { 
      myservo.write(pos);              // tell servo to go to position in variable 'pos'
      analogWrite(ledPin, map(pos, 0, 180, 0, 255)); //mapping the value from the for loop to get the full range of PWM for led fading
        
        currentMillis = millis();
        if (currentMillis - previousMillis > duration[counter]/tempo)
        {
          tone(buzzerPin, melody[counter], duration[counter]/tempo);
          counter++;
          previousMillis = currentMillis;
        }
          
          if (counter == 196)
          {
            counter = 0; //reset the counter so the song starts again       
          }
            
            if (pos == 179)
            {
              Serial.println("SWEEP 1");
              sweep = 1;
            }
              
              sensorValue = analogRead (sensorPin);
              if (sensorValue < threshold)
                {
                  break;
                }
     }
  }
  
  if (sweep == 1)
  {
    
    for (int pos = 180; pos >= 0; pos --)  // goes from 180 degrees to 0 degrees
    {
      myservo.write(pos);              // tell servo to go to position in variable 'pos'
      analogWrite(ledPin, map(pos, 0, 180, 0, 255));
       
       currentMillis = millis();
       if (currentMillis - previousMillis > duration[counter]/tempo)
        {
          tone(buzzerPin, melody[counter], duration[counter]/tempo);
          counter++;
          previousMillis = currentMillis;
        }
          
          if (counter == 196)
          {
            counter = 0;       
          }
            
            if (pos == 1)
            {
              sweep = 0;
              Serial.println("SWEEP 2");
            }
              
              sensorValue = analogRead (sensorPin);
              if (sensorValue < threshold)
                {
                  break;
                }
    }
  }
}
   
   if (sensorValue <= threshold) //if the light is low turn the servo back to starting point and stop doing anything
   {
    Serial.println("OFF");
    noTone(buzzerPin);
    myservo.write(0);
    counter = 0;   
   }
}

, 👍2

Обсуждение

Какую плату вы используете? Попробуйте переместить светодиод на вывод 5, 6 или 10 (в Uno), " тон " и "Аналоговая запись" будут использовать один и тот же таймер на выводах 3 и 11 в Uno, что звучит проблематично., @Mat

Что сказал Мэт. Кроме того, время воспроизведения музыки находится внутри цикла for, который управляет сервоприводом; вы также можете отделить их и настроить таймер миллиса для музыки и таймер миллиса для сервопривода, чтобы цикл выполнял свою работу без цикла for. Это не решит проблему с таймером, но код будет намного короче и проще для чтения, а музыка и тайминги сервопривода будут независимыми (но настраиваемыми). Я опубликую код, который покажет, как это сделать, в ответе, если вы хотите., @ocrdu

@Mat Arduino pin 10 использует тот же таймер для ШИМ, что и библиотека сервоприводов (таймер 1). Поэтому я бы придерживался вывода Arduino 5 или 6 для светодиода., @Gerben

@Гербен: а, хорошая мысль. Или, может быть, просто поместите светодиод на тот же вывод, что и сервопривод, если он просто должен быть включен, когда сервопривод движется. Освобождает таймер :-), @Mat


1 ответ


1

Если вас все еще интересует переработанный код, вот ваш код с удаленными и реструктурированными циклами for, чтобы состояние отслеживалось/обновлялось соответствующим образом в цикле void (), как показано ниже:

[your variable declaration code as given]
...
unsigned long currentMillis = 0;
unsigned long previousMillis = 0;
int threshold = 500;

int pos = 0;
int dir = 1;


void setup()
{
  myservo.attach (servoPin);
  pinMode (ledPin, OUTPUT); 
  pinMode (buzzerPin, OUTPUT);
  pinMode (sensorPin, INPUT);
  Serial.begin(9600);
  pos = 0;
  dir = 1;
  previousMillis = millis();
}

void loop(){

    sensorValue = analogRead (sensorPin);
    if (sensorValue > threshold)
    {
        pos += dir;
        if (pos == 180) || (pos == 0)
        {
            dir *= -1;
        }
        
        myservo.write(pos);
        analogWrite(ledPin, map(pos, 0, 180, 0, 255));
        
        currentMillis = millis();
        
        if ((currentMillis - previousMillis) > duration[counter]/tempo)
        {
            tone(buzzerPin, melody[counter], duration[counter]/tempo);
            counter += (counter > 195) ? -196 : 1;
            previousMillis = currentMillis;
        }
    }
    else
    {
        Serial.println("OFF");
        noTone(buzzerPin);
        myservo.write(0);
        counter = 0; 
        pos = 0;
        dir = 1;
    }
}
,