Учебный курс. Что такое массив. Микроконтроллер играет мелодию.

03/10/2009 - 21:00

Массив

Массив – это однородная структура данных, широко применяемая в программировании. Все элементы массива имеют одинаковый тип данных (отсюда и название однородная структура) и к любому из них можно обратиться, используя имя массива и индекс элемента.
   Для того чтобы использовать массив, его нужно предварительно объявить – указать тип данных массива, имя и размер, который определяет, сколько элементов может содержать массив. Размер массива указывается в квадратных скобках [] после имени.

    unsigned char Array[12];  //массив из 12-ти элементов типа  unsigned char
    int OtherArray[8];             //массив из 8-ми элементов типа int

   Первому элементу массива соответствует индекс 0, второму – 1, третьему – 2 .. и так далее. Последний элемент имеет индекс – (размер массива -1). Может показаться, что это несколько неудобно... ничего, дело привычки.

    Array[0] = 255; //присвоить первому элементу массива значение 255
    Array[11] = 15; //присвоить двенадцатому элементу массива значение 15

   При объявлении массива его можно инициализировать, то есть присвоить конкретные значения его элементам. Значения элементов перечисляются в фигурных скобках {} через запятую. После последнего значения запятую ставить не нужно.

    unsigned char Data[3] = {13, 8, 57};  //объявляем массив из 3-ех элементов
                                                             //и присваиваем им всем значения
    
    unsigned char Counters[6] = {0, 0, 0, 0, 0, 0};
    //объявляем массив из 6-ти элементов и обнуляем все элементы

   Си допускает не указывать размерность массива при его объявлении, при этом размерность массива определяется автоматически по числу инициализированных элементов.

    unsigned char Counters[] = {0, 0, 0, 0, 0, 0};
    //размерность массива будет равна 6
        
   Массив может располагаться в ОЗУ, EEPROM или flash памяти микроконтроллера. По умолчанию компилятор размещает массив  в оперативной памяти. Если элементы массива неизменны на протяжении всей программы (константы) то flash память лучше всего подходит для его хранения. Для того чтобы указать компилятору IARа, что массив должен располагаться во flash памяти - перед типом данных массива нужно использовать ключевое слово __flash (два символа подчеркивания подряд!).

__flash unsigned char Array[] = {0xfe, 0xfd, 0xfb, 0xf7, 0xef, 0xdf, 0xbf, 0x7f};
  //объявляем массив из 8-ми элементов во flash памяти микроконтроллера

   Ключевое слово __flash автоматически определяет массив как константный. Если вы попытаетесь в программе изменить содержимое массива компилятор выдаст ошибку.

  Задача: написать программу проигрывающую мелодию по нажатию кнопки.
  Схема для нашего примера.

   Мелодия – это последовательность нот, и массив как нельзя кстати подходит для ее хранения.  Нота имеет две характеристики – длительность звучания и высоту тона. По идее для каждой ноты мелодии мы должны хранить в памяти микроконтроллера эти данные. Но я для простоты выбрал мелодию, где все ноты имеют одинаковую длительность, поэтому в массиве мы будем хранить только тон ноты.
   Каждой ноте соответствует своя частота колебаний. Для того чтобы микроконтроллер мог воспроизводить ноты, нужно пересчитать эти частоты в константы для таймера Т0. Как это делать мы уже разбирали.
   Я рассчитал константы для двух октав и записал их в отдельный файл note.h. Дополнительно добавил две константы pause и end. Думаю, что смысл их понятен из названия.

  Код нашей программы


//программирование микроконтроллеров AVR на Си
//микроконтроллер играет мелодию
//пример использования массива

#include <ioavr.h>
#include <intrinsics.h>
#include "note.h"
#include "bits_macros.h"

#define button  0
#define buzer   1

__flash unsigned char Sound[] = {n1E,n1Fd,n1G,n1A,n1B,n1G,n1B,n1B,
                                               n1Ad,n1Fd,n1Ad,n1Ad,n1A,n1F,n1A,pause,
                                               n1E,n1Fd,n1G,n1A,n1B,n1G,n1B,n2E,
                                                n2D,n1B,n1G,n1B,n2D,n2D,pause,pause,end};

int main(void)
{
  unsigned char CurrentNote;
  unsigned char index;
 
  //инициализация периферии
  DDRD = (1<<buzer);
  PORTD = (1<<button);
  TIMSK = 0;
  TCCR0 = (1<<WGM01)|(0<<WGM00)|(0<<CS02)|(1<<CS01)|(1<<CS00);
  TCNT0 = 0;
  OCR0 = 0;
 
  //разрешение прерываний
  __enable_interrupt();
 
  //опрашиваем кнопку в бесконечном цикле
 //если кнопка нажата - микроконтроллер будет играть
  //мелодию пока не дойдет до конца

  while(1)
  {
    if (BitIsClear(PIND,button))
    {  
      index = 0;
      CurrentNote = Sound[index];
      while (CurrentNote != end)
      {
          if (CurrentNote != pause)
          {
            OCR0 = CurrentNote;        
            SetBit(TIMSK, OCIE0);
          }else{            
            TIMSK = 0;
          }
          index++;
          CurrentNote = Sound[index];
          __delay_cycles(2000000);
      }
    }    
  }
  return 0;
}

//обработчик прерывания таймера Т0
#pragma vector = TIMER0_COMP_vect
__interrupt void Timer0CompVect(void)
{
    InvBit(PORTD,buzer);
}


Пояснения к коду

   В начале программы мы как обычно подключаем заголовочные файлы. Стандартные IARовские и 2 своих.

  С помощью директивы define задаем символические имена выводам микроконтроллера к которым у нас подключены кнопка и бузер.
   Определяем константный массив во флэш памяти микроконтроллера, в котором содержится наша мелодия.

__flash unsigned char Sound[] = ....

   Пришлось немного помучаться, когда я переводил нотную партитуру. Не скажу, что это за мелодия, соберите макет и узнаете. Очень известная тема, слышал ее на уроках музыки, когда учился в школе.

В функции main() объявляем две переменных

  unsigned char CurrentNote;
  unsigned char index;

   В CurrentNote микроконтроллер считывает ноту мелодии, проверяет, что она не последняя (то есть не равна константе end) и не является паузой и загружает ее в регистр сравнения таймера Т0. Переменная index – нужна для доступа к очередной ноте мелодии (к элемент массива).   

   С инициализацией периферии все понятно.

   Разрешаем прерывания и попадаем в наш любимый while(1).

   В цикле AVR опращивает кнопку. Если кнопка нажата, то обнуляет переменную index, чтобы она указывала на первую ноту мелодии (на нулевой элемент массива), а в CurrentNote считывает из массива значение ноты.

while(1)
  {
    if (BitIsClear(PIND,button))
    {  
      index = 0;
      CurrentNote = Sound[index];
      
        ...
      }
    }    
  }


   Попадаем в другой цикл while (Current Note != end). Он выполняется пока микроконтроллер не дойдет до конца мелодии.
   В цикле микроконтроллер проверяет является Current Note нотой или паузой, и в зависимости от этого загружает переменную в регистр сравнения и разрешает прерывание  таймера Т0 или наоборот запрещает его.  


if (CurrentNote != pause){
            OCR0 = CurrentNote;        
            SetBit(TIMSK, OCIE0);
          }else{            
            TIMSK = 0;
          }

  Затем инкрементирует index, считывает следующую ноту и ждет. Программная задержка здесь определяет длительность ноты/паузы.

           index++;
          CurrentNote = Sound[index];
          __delay_cycles(2000000);

   В обработчике прерывания микроконтроллер дергает ножкой.

Схема для нашего примера
.
Ring-IAR.rar - проект для IAR
Ring-WinAvr.rar - проект для WinAvr

 

Comments   

# alexandershahbazov 2009-12-08 22:07
Можно ли расширить эту статью
"Микроконтролле р играет мелодию" .
# Pashgan 2009-12-09 19:19
Есть в мыслях.. полифонию забацать
# Guest 2010-04-22 20:47
__flash - есть ли шото подобное для вин авр. Делал шрифты в отдельной библиотеке для графического дисплея(776Байт ) - при подключении етой библиотеки размер занимаемой памяти програм выростал 790 байт и памяти данных(еепром) тоже увеличивался, думаю шо етот масив шрифтов компилятор пихал в обе ети памяти, хотелось бы избавится от етого недостатка.
# Pashgan 2010-04-23 09:54
Конечно.
В текст программы включается заголовочный файл pgmspace.h. Перед объявлением массива пишется PROGMEM. Для доступа к элементам массива используется макрос pgm_read_byte(&имя_переменной)

Например:
#include "pgmspace.h"
...
PROGMEM unsigned char array[]={10,2,5};
...
tmp = pgm_read_byte(& array);
# Guest 2010-05-26 18:29
К последнему комментарию. А как всё таки добраться, скажем, до третьего элемента массива array?
# Guest 2010-05-26 18:44
Сам отвечу:
pgm_read_byte(&array[2]);
# Pashgan 2010-05-28 03:10
Ага..
# Guest 2010-06-16 19:05
А если необходимо генерировать мелодию, принимаемую контроллером по SPI? как это будет реализовываться в коде?
# Pashgan 2010-06-17 14:58
А по SPI что будет передаваться - цифровой поток (wav, например) или высота нот и их длительность?
# Guest 2010-06-17 18:37
Да, тональность нот и их длительность
# Guest 2010-06-17 18:54
к примеру, попробовать принять по spi одноголосную мелодию, имеющую 16 нот и 7 разных длительностей, ну и вывести на динамик
# Энд 2010-11-24 12:46
Я прошиваю мега16, у меня слышны только хрипы, но есть немного похожее на мелодию. Как сделать четче мелодию. Может это из за того что у меня нет в схеме резонатора частоты (там же встроенный есть, да и прога со светодиодом работала- мигал как положено) ?????
# Pashgan 2010-11-30 18:13
А частота внутреннего генератора 8 МГц? Точно используешь пьезоизлучатель ?
# Энд 2010-12-10 18:31
Прописываю #define F_CPU 8000000UL // 8 MHz
все также, меняю на 10000000 // 1 MHz и ничег не изменяется... Я правильно задаю частоту внутреннего генератора?
# Pashgan 2010-12-12 19:29
Частота внутреннего генератора задается с помощью FUSE битов. Определения частоты с помощью #define внутри программы нужно для вычисления программных задержек и тому подобных вещей. Это никак не отражается на реальной частоте внутреннего генератора. Ты каким программатором пользуешься?
# Энд 2010-12-13 15:26
AVR8 Burn-O-Mat v2.... Я так и думал что это связано с FUSE битами, гдето же читал про это ))))
# Setap 2011-01-08 10:10
хотельсо бы увидеть текс программы в котором посностью бы обеснялось конфигурировани е таймера без ссылок на функции и вложенные библиотеки
# grbizly 2011-01-20 07:51
Спасибо за уроки, давно хотел по изучать IAR.
только возник вопрос, по отладке программ в Proteus. Как в IAR получить .coff, .asm файлы для отладки.
# ОлегН 2011-02-07 20:13
Объясните пожалуйста как можно мелодию,
запрограммированную таким образом
записать в файд какого нибудь аудио формата
# Oposition 2011-02-24 16:30
Отличные статьи!
Спасибо! Мало кто пользует IAR поэтому очень сложно учиться работать с AVR. Изза этого даже много раз пытался спрыгнуть на CV.
А где можно найти массивов с мелодиями?
# Pashgan 2011-02-24 22:37
Не знаю, я брал обычные ноты и перевел их в соответствующие частоты.
# Oposition 2011-02-24 16:59
Пока искал мелодии наткнулся на интересную статью, правда реализация на пике но всеравно все понятно. Вот тут http://www.beyondlogic.org/pic/ringtones.htm
Пользуют RTTL формат, то что было в старых сотиках аля нокия 3310
# SergeyB 2011-02-26 23:09
В IAR усть ограничение на размер массива? У меня не работает программа при размере массива больше присмерно 2200 элементов.
Если разбиваю этот массив на два массива, то программа работает. Т.е. дело не в нехватки памяти.

И еще почему когда я создаю два массива общим колличеством элементов 2500 размером в один байт каждый (т.е. __flash char Array1[1872]=
{ 0xB5 , 0xD9 , 0xB5 , 0xB5 , 0x70 , 0x90 , 0x94 ...1872штук) и еще массив на 650 элементов ), заполнение памяти МК при этом увеличивается на 6Кб гдето, а должно ведь на 2,5 Кб
# IseMan 2011-02-27 20:38
{
__flash unsigned char
{
почему здесь кавычек нет а в проекте есть ,и выдаёт ошибку о ковычках?
# Pashgan 2011-03-06 21:33
Проверил проект - все работает. О каких кавычках идет речь?
# ALEX1991 2011-03-21 14:42
Pashgan очень интересная статья и я хотел спросить а нету исходника на WINAVR(GCC) не знаю как подправить код IAR-a чтобы он у меня в AVR STUDIO компельнулся
# IseMan 2011-06-16 18:09
Quoting IseMan:
{
__flash unsigned char
{
почему здесь кавычек нет а в проекте есть ,и выдаёт ошибку о ковычках?


В коде вашей программы ,работаю в CodeVisionAVR C Compiler
# Димка 2011-07-09 06:29
Тут тоже и дребезг остался, и ещё во эта штука __delay_cycles( 2000000); затыкает микроконтроллер , а если ему надо в это время ещё что то делать?
# andre1i 2011-07-20 13:13
Опять у меня проблемы:
Если заменить строчку if (BitIsClear(PIN D,button)) на if (BitIsClear(POR TD,button)) в AVR Studio вроде нормально симулируется (нажатие кнопки,т.к в первом варианте PIND0 всегда =0). Так же неработает строчка OCR0=CurrentNot e; (OCR0 всегда равен 0). Первый раз(проход F11 в avr studio) прерывание возникает - потом нет. Навернека потому, что OCR0 не изменяется. Процессор Atmega16a.
# Tiptop 2012-03-21 10:55
А на счёт прерывателя, то есть при записи ноты в регистр OCR0 ны выходе OCR будет высокий уровень пока счётчик не досчитает до конечного значения ноты, как тогда она может звучать если сигнал не меняется?
# Tiptop 2012-03-21 10:57
ну в смысле на выходе OCR сигнал
# ASDFG123 2013-05-18 10:21
как включить значение из массива в выражение сравнения? например: если нулевой (первый) элемент массива больше 511, то выполнить то то.
# JoJo 2013-05-18 20:22
Точно так же как и обычную переменную..
Code:
unsigned int array[10];
....
if (array[0] > 511) {
...
}
# Georgy 2013-12-01 12:40
Добрый день.
Если не сложно, то растолкуйте пожалуйста, что это за массив: tab[][8]={.....};?
Интересуют первые квадратные скобки.
С уважением Георгий.
# Pashgan 2013-12-03 02:08
Так объявляются двумерные массивы. Аналог двумерного массива - это матрица из высшей математики.
Code:
tab[2][3] =
{
{1,2,3,}
{4,5,6,}
};


В квадратных скобках задается размерность массива.
# katonik 2014-03-10 20:54
Спасибо за интересные и нужные статьи.
Только начал изучать АВР,и такой вопрос,а есть полный вариант этой проги для ВинАвр?
# Pashgan 2014-03-18 20:11
В смысле полный? Есть ли версия проекта для WinAvr?
# katonik 2014-03-19 08:00
Да,интересует проект для WinAvr/
# Pashgan 2014-03-19 10:05
Добавил в конце статьи.
# FreshMan 2014-05-07 14:40
как составлять массивы нот ?
# Pashgan 2014-05-08 09:49
Я брал ноты для пианино, смотрел высоту каждой ноты по шпаргалке и записывал в массив. Но мелодия должна быть "однородной". То есть либо все восьмыми, либо шестнадцатыми.. Потому что в программе нельзя задать длительность звучания.
# vovchik 2017-10-14 08:00
не подскажете как реализовать тоже но с 2,3...10 мелодиями. Интересует именно момент как переключать мелодии, допустим по нажатии кнопки.

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