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

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
Можно ли расширить эту статью
"Микроконтролле р играет мелодию" .
Reply | Reply with quote | Quote
# Pashgan 2009-12-09 19:19
Есть в мыслях.. полифонию забацать
Reply | Reply with quote | Quote
# Guest 2010-04-22 20:47
__flash - есть ли шото подобное для вин авр. Делал шрифты в отдельной библиотеке для графического дисплея(776Байт ) - при подключении етой библиотеки размер занимаемой памяти програм выростал 790 байт и памяти данных(еепром) тоже увеличивался, думаю шо етот масив шрифтов компилятор пихал в обе ети памяти, хотелось бы избавится от етого недостатка.
Reply | Reply with quote | Quote
# 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);
Reply | Reply with quote | Quote
# Guest 2010-05-26 18:29
К последнему комментарию. А как всё таки добраться, скажем, до третьего элемента массива array?
Reply | Reply with quote | Quote
# Guest 2010-05-26 18:44
Сам отвечу:
pgm_read_byte(&array[2]);
Reply | Reply with quote | Quote
# Pashgan 2010-05-28 03:10
Ага..
Reply | Reply with quote | Quote
# Guest 2010-06-16 19:05
А если необходимо генерировать мелодию, принимаемую контроллером по SPI? как это будет реализовываться в коде?
Reply | Reply with quote | Quote
# Pashgan 2010-06-17 14:58
А по SPI что будет передаваться - цифровой поток (wav, например) или высота нот и их длительность?
Reply | Reply with quote | Quote
# Guest 2010-06-17 18:37
Да, тональность нот и их длительность
Reply | Reply with quote | Quote
# Guest 2010-06-17 18:54
к примеру, попробовать принять по spi одноголосную мелодию, имеющую 16 нот и 7 разных длительностей, ну и вывести на динамик
Reply | Reply with quote | Quote
# Энд 2010-11-24 12:46
Я прошиваю мега16, у меня слышны только хрипы, но есть немного похожее на мелодию. Как сделать четче мелодию. Может это из за того что у меня нет в схеме резонатора частоты (там же встроенный есть, да и прога со светодиодом работала- мигал как положено) ?????
Reply | Reply with quote | Quote
# Pashgan 2010-11-30 18:13
А частота внутреннего генератора 8 МГц? Точно используешь пьезоизлучатель ?
Reply | Reply with quote | Quote
# Энд 2010-12-10 18:31
Прописываю #define F_CPU 8000000UL // 8 MHz
все также, меняю на 10000000 // 1 MHz и ничег не изменяется... Я правильно задаю частоту внутреннего генератора?
Reply | Reply with quote | Quote
# Pashgan 2010-12-12 19:29
Частота внутреннего генератора задается с помощью FUSE битов. Определения частоты с помощью #define внутри программы нужно для вычисления программных задержек и тому подобных вещей. Это никак не отражается на реальной частоте внутреннего генератора. Ты каким программатором пользуешься?
Reply | Reply with quote | Quote
# Энд 2010-12-13 15:26
AVR8 Burn-O-Mat v2.... Я так и думал что это связано с FUSE битами, гдето же читал про это ))))
Reply | Reply with quote | Quote
# Setap 2011-01-08 10:10
хотельсо бы увидеть текс программы в котором посностью бы обеснялось конфигурировани е таймера без ссылок на функции и вложенные библиотеки
Reply | Reply with quote | Quote
# grbizly 2011-01-20 07:51
Спасибо за уроки, давно хотел по изучать IAR.
только возник вопрос, по отладке программ в Proteus. Как в IAR получить .coff, .asm файлы для отладки.
Reply | Reply with quote | Quote
# ОлегН 2011-02-07 20:13
Объясните пожалуйста как можно мелодию,
запрограммированную таким образом
записать в файд какого нибудь аудио формата
Reply | Reply with quote | Quote
# Oposition 2011-02-24 16:30
Отличные статьи!
Спасибо! Мало кто пользует IAR поэтому очень сложно учиться работать с AVR. Изза этого даже много раз пытался спрыгнуть на CV.
А где можно найти массивов с мелодиями?
Reply | Reply with quote | Quote
# Pashgan 2011-02-24 22:37
Не знаю, я брал обычные ноты и перевел их в соответствующие частоты.
Reply | Reply with quote | Quote
# Oposition 2011-02-24 16:59
Пока искал мелодии наткнулся на интересную статью, правда реализация на пике но всеравно все понятно. Вот тут http://www.beyondlogic.org/pic/ringtones.htm
Пользуют RTTL формат, то что было в старых сотиках аля нокия 3310
Reply | Reply with quote | Quote
# 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 Кб
Reply | Reply with quote | Quote
# IseMan 2011-02-27 20:38
{
__flash unsigned char
{
почему здесь кавычек нет а в проекте есть ,и выдаёт ошибку о ковычках?
Reply | Reply with quote | Quote
# Pashgan 2011-03-06 21:33
Проверил проект - все работает. О каких кавычках идет речь?
Reply | Reply with quote | Quote
# ALEX1991 2011-03-21 14:42
Pashgan очень интересная статья и я хотел спросить а нету исходника на WINAVR(GCC) не знаю как подправить код IAR-a чтобы он у меня в AVR STUDIO компельнулся
Reply | Reply with quote | Quote
# IseMan 2011-06-16 18:09
Quoting IseMan:
{
__flash unsigned char
{
почему здесь кавычек нет а в проекте есть ,и выдаёт ошибку о ковычках?


В коде вашей программы ,работаю в CodeVisionAVR C Compiler
Reply | Reply with quote | Quote
# Димка 2011-07-09 06:29
Тут тоже и дребезг остался, и ещё во эта штука __delay_cycles( 2000000); затыкает микроконтроллер , а если ему надо в это время ещё что то делать?
Reply | Reply with quote | Quote
# 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.
Reply | Reply with quote | Quote
# Tiptop 2012-03-21 10:55
А на счёт прерывателя, то есть при записи ноты в регистр OCR0 ны выходе OCR будет высокий уровень пока счётчик не досчитает до конечного значения ноты, как тогда она может звучать если сигнал не меняется?
Reply | Reply with quote | Quote
# Tiptop 2012-03-21 10:57
ну в смысле на выходе OCR сигнал
Reply | Reply with quote | Quote
# ASDFG123 2013-05-18 10:21
как включить значение из массива в выражение сравнения? например: если нулевой (первый) элемент массива больше 511, то выполнить то то.
Reply | Reply with quote | Quote
# JoJo 2013-05-18 20:22
Точно так же как и обычную переменную..
Code:
unsigned int array[10];
....
if (array[0] > 511) {
...
}
Reply | Reply with quote | Quote
# Georgy 2013-12-01 12:40
Добрый день.
Если не сложно, то растолкуйте пожалуйста, что это за массив: tab[][8]={.....};?
Интересуют первые квадратные скобки.
С уважением Георгий.
Reply | Reply with quote | Quote
# Pashgan 2013-12-03 02:08
Так объявляются двумерные массивы. Аналог двумерного массива - это матрица из высшей математики.
Code:
tab[2][3] =
{
{1,2,3,}
{4,5,6,}
};


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

Add comment