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

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

 

Комментарии   

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

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

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


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


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

Добавить комментарий

Защитный код
Обновить