/* Задача:
1). Выбрать любую мелодию и написать программу для неё.
2). Создать электронную схему управления пищалкой и конвертер f>U для подачи на аналоговый вход МК.
3). Считывать аналоговый сигнал, который будет пропорционален частоте ноты.
4). Управлять с помощью широтно-импульсной модуляции (ШИМ) RGB светодиодом в зависимости от входного сигнала (а, следовательно, частоты нот).
Пришлось делать преобразование цифрового сигнала (частоты ноты) в аналоговое напряжение = f(частота ноты),
а потом опять в цифровой - ШИМ управление RGB светодиодном = f(изменение аналогового U).
5). Сделать возможным выбор по нажатию кнопки режима работы (выбор мелодии).
НЕПОНЯТНО! НА НАТУРАЛЬНОМ ОБРАЗЦЕ ВСЁ РАБОТАЕТ! А ЗДЕСЬ НЕТ!!!
*/
#define SPEAKER 7 // Определяем выход сигнала на пищалку
#define analogPin 0 // вход аналогового сигнала с RC-фильтра
#define redPin 9 // ШИМ выход на красный светодиод
#define greenPin 10 // ШИМ выход на зеленый
#define bluePin 6 // ШИМ выход на синий
#define button 2 // Прерывание INT0 будем ловить на пине 2
#define pinLedInterrupt 4 // Светодиод, символизирующий о наступлении прерывания
// Частоты нот для мелодии 1 ("ПОДМОСКОВНЫЕ ВЕЧЕРА")
#define Lya_little 220
#define Do 262
#define Mi 330
#define Re 294
#define Si_little 247
#define Sol 392
#define Fa 349
#define Fa_diez 370
#define Sol_diez 415
#define Si 494
#define Lya 440
volatile boolean flag = false; // Определяем переменную flag для изменения в прерывании
byte mode = 0; // Переменная выбора режимов (мелодий)
// Массив нот для мелодии 1. Для удобства представления разбиваем на такты в столбик.
int notes_Melody1[] = {
Lya_little, Do, Mi, Do, //1 первый такт, little - слева от первой октавы
Re, Do, Si_little, //2
Mi, Re, //3
Lya_little, //4
Do, Mi, Sol, Sol, //5
Lya, Sol, Fa, //6
Mi, //7
Fa_diez, Sol_diez, //8
Si, Lya, Mi, //9
Mi, Do, Lya_little, //10
Mi, Re, Fa, //11
Fa, Sol, Fa, //12
Mi, Re, Do, //13
Mi, Re, //14
Lya_little //15 последний такт
};
int takt_Melody1 = 1500; // Длительность такта в мс при спокойной игре (можно менять!). Это число принимается за целое.
// Массив длительностей звучания нот для мелодии 1 в мс
int times_Melody1[] = {
takt_Melody1 / 4, takt_Melody1 / 4, takt_Melody1 / 4, takt_Melody1 / 4, //1 первый такт
takt_Melody1 / 2, takt_Melody1 / 4, takt_Melody1 / 4, //2
takt_Melody1 / 2, takt_Melody1 / 2, //3
takt_Melody1, //4
takt_Melody1 / 4, takt_Melody1 / 4, takt_Melody1 / 4, takt_Melody1 / 4, //5
takt_Melody1 / 2, takt_Melody1 / 4, takt_Melody1 / 4, //6
takt_Melody1, //7
takt_Melody1 / 2, takt_Melody1 / 2, //8
takt_Melody1 / 4, takt_Melody1 / 4, takt_Melody1, //9 !! Увеличенная длительность
0, takt_Melody1 / 4, takt_Melody1 / 4, //10 !! Нулевая длительность, чтобы не было разрыва между нотами
takt_Melody1 / 4, takt_Melody1 / 4, takt_Melody1, //11 !! Увеличенная длительность
0, takt_Melody1 / 4, takt_Melody1 / 4, //12 !! Нулевая длительность
takt_Melody1 / 2, takt_Melody1 / 4, takt_Melody1 / 4, //13
takt_Melody1 / 2, takt_Melody1 / 2, //14
takt_Melody1 //15 последний такт
};
void setup() {
pinMode(redPin, OUTPUT); // Определяем, как выход
pinMode(greenPin, OUTPUT); // Определяем, как выход
pinMode(bluePin, OUTPUT); // Определяем, как выход
//Serial.begin(9600); // Для отладки, чтобы увидеть показания U через "монитор порта"
pinMode(button, INPUT_PULLUP); // Подключаем кнопку между портом D2 и землёй
attachInterrupt(0, buttonPress, FALLING); // функция обработки buttonPress, прерывание INT0 сработает при перепаде с 1 на 0
pinMode(pinLedInterrupt, OUTPUT); // Выход на светодиод для фиксации прерывания
}
// При нажатии на кнопку мелодия сразу не прерывается, а доигрывает до конца. И только потом переключается режим.
void loop() {
// Если было прерывание, увеличиваем номер режима на 1 и закольцовываем от 0 до 1
if (flag) {
mode++;
if (mode > 1) mode = 0;
flag = false;
}
// Выбираем режим
switch (mode) {
case 0:
Podmoskovnye_Vechera();
break;
case 1:
blink_LED();
break;
}
}
// Функция мелодии1 (в данном случае "Подмосковные вечера")
void Podmoskovnye_Vechera() {
digitalWrite(pinLedInterrupt, 0);
int note = 0; // Играем с 0 ноты до последней
melody(note); // Вызываем функцию мелодии
delay(500);
note = 18; // Это припев. Играем с 18 ноты до последней
melody(note); // Вызываем функцию мелодии
digitalWrite(redPin, 0); // Гасим RGB светодиод
digitalWrite(greenPin, 0); // Гасим RGB светодиод
digitalWrite(bluePin, 0); // Гасим RGB светодиод
delay(1000);
}
// Мигание светодиодом
void blink_LED() {
static uint32_t timer = 0;
static bool led_Flag = false;
if (millis() - timer >= 200) {
timer = millis();
led_Flag = !led_Flag;
digitalWrite(pinLedInterrupt, led_Flag);
}
}
// Функция обработки прерывания
void buttonPress() { // Дребезг контактов кнопки устраняется аппаратно, поэтому прогр. обработка не нужна
flag = true;
digitalWrite(pinLedInterrupt, 1); // Убеждаемся, что произошло прерывание
}
// Функция проигрыша мелодии, управление свечением LED RGB и вывода U в монитор для отладки
void melody(int note) {
for (int i = note; i < 38; i++) { // Играем всю мелодию до конца с 0 или с ноты note, которую получает функция.
tone(SPEAKER, notes_Melody1[i], times_Melody1[i]);
delay(times_Melody1[i]); // Задержка между нотами = длительности самой ноты
int volLevel = analogRead(analogPin); // Читаем аналоговое напряжение, которое конвертируется АЦП контроллера в (0...1023)
showRGB(volLevel); // Вызов функции RGB
//print_to_Monitor(volLevel); // Функция вывода вывода данных в монитор порта для отладки
}
}
// Функция вывода данных в монитор порта
void print_to_Monitor(int volLevel) {
float V = volLevel * (5.0 / 1024); // Пересчитываем значения АЦП в понятные и привычные вольты
Serial.print(volLevel); // вывод в монитор порта напряжения с выхода конвертера (0...1023)
Serial.print("\t");
Serial.print(V); // вывод в виде напряжения в вольтах
Serial.println("V");
}
void showRGB(int volLevel_color) { // Функция интенсивности свечения каждого цвета RGB, как f = (напряжение на аналоговом входе)
int redIntensity;
int greenIntensity;
int blueIntensity;
/* При R2C2 фильтре 10к и 10мкФ сигнал с выхода фильтра меняется не сильно.
Примерно от 660 до 740 единиц (от примерно 3,2 В до 3,6 В) при U пит. = 5В.
*/
volLevel_color = map(volLevel_color, 660, 740, 0, 767);
// Определяем, в какую из 3-х зон попадает сигнал volLevel_color из мах. 767 вариантов цвета.
if (volLevel_color <= 255) // зона 1
{
redIntensity = 255 - volLevel_color; // красный меняется из включенного на выключенный
greenIntensity = volLevel_color; // зеленый меняется из выключенного на включенный
blueIntensity = 0; // голубой всегда выключен
} else if (volLevel_color <= 511) // зона 2
{
redIntensity = 0; // красный всегда выключен
greenIntensity = 255 - (volLevel_color - 256); // зеленый от включенного к выключенному
blueIntensity = (volLevel_color - 256); // голубой от выключенного к включенному
} else if (volLevel_color >= 512) // зона 3
{
redIntensity = (volLevel_color - 512); // красный выкл. к вкл.
greenIntensity = 0; // зеленый всегда выкл.
blueIntensity = 255 - (volLevel_color - 512); // голубой от вкл. к выкл.
}
redIntensity = map(redIntensity, 0, 767, 0, 255); // Приводим интенсивность свечения в соответствие с ШИМ управлением (0...255)
greenIntensity = map(greenIntensity, 0, 767, 0, 255);
blueIntensity = map(blueIntensity, 0, 767, 0, 255);
analogWrite(redPin, redIntensity); // Зажигаем светодиод с нужным ШИМ-сигналом, который пропорционален аналоговому сигналу
analogWrite(greenPin, greenIntensity);
analogWrite(bluePin, blueIntensity);
}