Вы находитесь на странице: 1из 6

Масиви

Создание мелодии
Для формирования мелодии очень удобно использовать массивы. Вывод
последовательности звуков реализуется простым циклом перебора массива нот с
отправкой текущего значения на динамик.
Массив представляет собой упорядоченную последовательность элементов
одного типа, которые связаны между собой. Группировка таких элементов —
идеальный формат для перебора.
Массив можно представить как нумерованный список. Каждый элемент
массива имеет индекс, который указывает его местоположение в списке. В нашем
примере в массиве хранится звуковая последовательность — список нот, которые
будем воспроизводить по порядку.
В Arduino при объявлении массива необходимо указывать его размер. Вы
можете это сделать, либо явно указав размерность массива, либо заполнив массив
всеми значениями. Например, если требуется, чтобы массив содержал четыре
элемента типа int, объявляем его в программе так:
int numbers[4];
При необходимости можно инициализировать массив значениями при
объявлении.
При объявлении массива таким способом указывать число элементов массива
необязательно, предполагается, что длина массива равна числу объявленных
элементов:
// Оба варианта объявления верны
int numbers[4] = {-7, 0, 6, 234};
int numbers[] = {-7, 0, 6, 234};
Обратите внимание, что массивы индексируются с нуля. Доступ к элементу
массива можно получить, поставив после имени массива в квадратных скобках
соответствующее значение индекса. Например, если требуется установить яркость
светодиода, подключенного к выводу 9 Arduino, равной значению третьего элемента
в массиве, то можно сделать это следующим образом:
analogWrite(9,numbers[2]);
Обратите внимание, что индекс 2 представляет собой третье значение в
массиве, поскольку нумерация начинается с нуля. Изменить одно из значений
массива можно так:
numbers[2] = 10;
Далее массивы потребуются нам, чтобы создать структуру, которая может
содержать последовательность нот, воспроизводимую на динамике и определение
их длительности звучания.
Для хранения информации о мелодии, которую вы хотите воспроизвести,
создадим два массива одинаковой длины. Первый содержит перечень нот, а второй
— список длительности звучания для каждой ноты в миллисекундах. Затем,
перебирая индексы этих массивов, воспроизведем мелодию (листинг 5.1).
// Массив нот
int notes[] = {
NOTE_A4, NOTE_E3, NOTE_A4, 0,
NOTE_A4, NOTE_E3, NOTE_A4, 0,
NOTE_E4, NOTE_D4/ NOTE_C4, NOTE_B4, NOTE_A4, NOTE_B4/ NOTE_C4, NOTE_D4,
NOTE_E4, NOTE_E3, NOTE_A4, 0
};
// Массив длительностей звучания нот в мс
int times [ ] = {
250, 250, 250, 250,
250, 250, 250, 250,
125, 125, 125, 125, 125, 125, 125, 125,
250, 250, 250, 250
Обратите внимание, что длина обоих массивов одинакова (20 элементов).
Некоторые ноты задаются в виде нулевых значений, — это музыкальные паузы.
Длительность звучания каждой ноты берем из второго массива. Для тех, кто знаком
с теорией музыки, обратите внимание, что я задал длительность четвертных нот 250
мс, а восьмых— 125 мс.
Послушать аудиозапись можно на странице
www.exploringarduino.com/content/ch5 или на сайте издательства Wiley.
Осталось написать программу для воспроизведения мелодии. С помощью цикла
выбираем значения нот и их длительность из массивов и реализуем воспроизведение
каждой ноты. Поскольку вы, вероятно, не захотите слушать мелодию снова и снова,
можно выполнить цикл в функции setup (). Чтобы возобновить воспроизведение,
нажмите кнопку Reset.
// Проигрывание мелодии на динамике
#include "pitches.h" // Заголовочный файл со значениями частоты нот
const int SPEAKER=9; // Вывод подключения динамика
// Массив нот
int notes[] = {
N0TE_A4, N0TE_E3, N0TE_A4, 0,
N0TE_A4, N0TE_E3, N0TE_A4, 0,
N0TE_E4, N0TE_D4, N0TE_C4, N0TEJB4, N0TE_A4/ N0TE_B4, N0TE_C4, N0TE_D4,
N0TE_E4, N0TE_E3, N0TE_A4, 0
// Массив длительностей звучания нот в мс
int times[] = {
250, 250, 250, 250,
250, 250, 250, 250,
125, 125, 125, 125, 125, 125, 125, 125,
250, 250, 250, 250
void setup()
II Выбор каждой ноты перебором массива нот
for (int i = 0; i < 20; i++)
{
tone(SPEAKER, notes[i], times[i]);
delay(times[i]);
void loop()
{
// Чтобы повторить воспроизведение, необходимо нажать кнопку Reset
Если вы захотите создать свою собственную мелодию, проследите, чтобы
массивы нот и длительностей имели равный размер, и правильно задайте верхнюю
границу для цикла перебора for (). Поскольку функция tone () может работать в
фоновом режиме, важно определить задержку delay о, чтобы следующая нота не
звучала, пока не закончится воспроизведение предыдущей.

Рассмотрим варианты создания (объявления) массива:


□ объявление массива без его инициализации:
int pin_leds[8];
□ можно объявить массив без непосредственного указания размера.
Компилятор
подсчитает количество элементов и создаст массив соответствующего размера:
int pin_leds [] = {5,6,7,8,9,10,11,12};
О можно одновременно инициализировать и указать размер создаваемого
массива:
int pin_leds [8] = {5,6,7,8,9,10,11,12};
□ объявление массива типа char:
char message[8] = "Arduino";
Учтите, что в таком случае необходимо предусмотреть еще один элемент для
хранения обязательного null-символа:
Теперь рассмотрим, как осуществляется доступ к элементам массива.
Индексация
в массивах начинается с нуля. То есть, первый элемент массива будет иметь
поряд-
ковый номер 0. Таким образом:
pin_leds[0] = 5;
pin_leds[l] = 6;
Как работать с массивами? Очень удобный метод работы с массивами —
циклы.
При этом счетчик цикла используется для индексации каждого элемента
массива.
Например, для создания массива с номерами пинов, задаваемыми командой
pinMode (), необходимо выполнить следующий код:
int i;
for (i = 0; i < 8; i = i + 1) {
pinMode(pin leds [i],OUTPUT);
98^ Часть III. Сопряжение Arduino со вспомогательными устройствами
Внесем соответствующие изменения в предыдущий скетч (см. листинг 6.3),
исполь-
зуя массивы, и получим скетч, представленный в листинге 6.4.
В нем, кроме массивов, мы предусмотрели для позиции горящего светодиода
пере-
менную pos, которая после каждой паузы станет увеличиваться на 1, а при
дости-
жении значения 8 будет обнуляться:
pos=(pos+l)%8;
При этом мы запускаем цикл for для массива pin_ieds[i] с пинами подключения
светодиодов, в котором для всех элементов массива с индексом, не равным
значе-
нию переменной pos, выполняем команду:
digitalWrite (pin_leds [i], LOW) ;
а для элемента массива с индексом, равным значению переменной pos,
команду:
digitalWrite(pin leds[i],HIGH);

Подключение к плате Arduino 8-ми светодиодов


// массив, элементы которого - пины
// подключения светодиодов
int pin_leds [8] - {12,11,10,9,8,7,6,5};
// позиция "горящего" светодиода
int pos=0;
// переменная для паузы (тип unsigned long)
unsigned long pause=1000;
void setup() {
// настроить выводы Arduino как OUTPUT
for (int i = 0; i < 8; i = i + 1) {
pinMode(pin_leds [i],OUTPUT);
}
// все светодиоды "потушить",
for (int i = 0; i < 8; i = i + 1) {
digitalWrite (pin_leds [i], LOW) ;
}
}
void loop() {
// все светодиоды "потушить",
for (int i = 0; i < 8; i = i + 1) {
// "зажечь" светодиод, если он =pos
if (i—pos)
digitalWrite(pin_leds [i],HIGH);
// все остальные "потушить"
else
digitalWrite (pin_leds [i], LOW) ;
}
delay(pause);
// изменить позицию "горящего" светодиода
pos=(pos+l)%8;

Вывод показаний потенциометра на светодиодную шкалу


// Аналоговый вход АО для подключения потенциометра
const int POT=0;
// переменная для хранения значения потенциометра
int valpot = 0;
// список контактов подключения светодиодов
const int pinsled[10]={4,5,6,7,8,9,10,11,12,13};
// переменная для хранения значения шкалы
int countleds = 0;
void setup () {
// запуск последовательного порта
Serial.begin(9600);
for(int i=0;i<10;i++) {
// Сконфигурировать контакты подсоединения шкалы как выходы
pinMode(pinsled[i],OUTPUT);
digitalWrite(pinsled[i],LOW);
}
}
void loop () {
// чтение данных потенциометра
valpot = analogRead(POT);
// масштабируем значение к интервалу 0-10
countled=map(valpot,0,1023, 0,10) ;
Serial.print("countled =");
Serial.println(countled);
// зажигаем количество светодиодов на шкале, равное countled
for(int i=0;i<10;i++) {
if(i<countleds) // зажигаем светодиод шкалы
digitalWrite(pinsled[i],HIGH);
else // гасим светодиод шкалы
digitalWrite(pinsled[i],LOW);
}
}

#define Директива, дающая команду препроцессору заменить указанное название


на указанное значение в текущем документе (напомню, что вкладки Arduino IDE
являются одним документом). Чаще всего таким образом объявляют константы:
 #define MOTOR_PIN 10 // пин мотора 10
 #define LED_PIN 3 // пин светодиода 3
После компиляции все встречающиеся в тексте программы слова MOTOR_PIN
будут заменены на цифру 10, а LED_PIN – на цифру 3. Такой способ хранения
констант не использует оперативную память микроконтроллера, а хранится
как код програмы на flash-памяти, это самый большой плюс данного способу.
Точка с запятой не ставится. Таким способом обычно указывают пины
подключения, настройки, различные величины и т.д. Также define позволяет делать
т.н. макрофункции. Например Ардуиновская функция sq (квадрат) является макро,
который при компиляции превращается в умножение:
 #define BTN_PIN 10
 #define DEFAULT_VALUE 3423

Управление кнопками количеством горящих светодиодов


// количество кнопок
#define COUNT_BUTTONS 2
// количество светодиодов
#define COUNTJLEDS 8
// Выводы Arduino для подключения
// список светодиодов
int pinleds[8]«{4,5, 6,7,8, 9,10,11} ;
// список кнопок
int pinbuttons[2]={2,3};
// список сохранения предыдущих состояний кнопок
int lastbuttons[2]={0,0};
// список сохранения текущих состояний кнопок
int currentbuttons[2]={0,0};
// переменные - количество горящих светодиодов
int countl=0;

void setup() {
// настроить выводы Arduino для светодиодов как выходы
for(int i=0;i<COUNT_LEDS;i++) {
pinMode(pinleds[i], OUTPUT);
}
// установить число светодиодов (0)
setleds(0);
}
void loop() {
// проверка нажатия кнопок выбора программ
for(int i=0;i<COUNT_BUTTONS;i++) {
currentbuttons[i] = debounce(lastbuttons[i],pinbuttons[i]);
// если нажатие...
if (lastbuttons[i] == 0 && currentbuttons[i] == 1) {
// реакция на нажатие - изменение count1
countl=change_countl(i);
// обновление состояния
setleds(countl);
}
lastbuttons[i] = currentbuttons[i];
}
}
// изменение количества светодиодов
int change_countl(int but) {
if(but==0) // добавить
return min(count1+1,COUNT_LEDS);
else // снять
return max(count1-1,0);
}
// (включение-выключение светодиодов)
void setleds(int cnt) {
// включение светодиодов от 0 до count1
for(int i=0;i<cnt;i++)
digitalWrite(pinleds[i],HIGH);
// включение светодиодов от 0 до cnt
for(int i=cnt;i<COUNT_LEDS;i++)
digitalWrite(pinleds[i],LOW);
}
/* Функция сглаживания дребезга
* Принимает в качестве аргумента предыдущее состояние кнопки
* и выдает фактическое.
*/
int debounce(int last,int pinl) {
int current = digitalRead(pinl); // Считать состояние кнопки
// если изменилось...
if (last != current) {
delay(5); // ждем 5мс
current = digitalRead(pinl); // считываем состояние кнопки
return current; // возвращаем состояние кнопки
}
}