Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Контрольная-весна.doc
Скачиваний:
0
Добавлен:
01.07.2025
Размер:
849.41 Кб
Скачать

Алгоритм работы программы

Алгоритм разрабатываемого устройства следующий. При замыкании контактов любого из датчиков микроконтроллер должен загрузить в регистр совпадения нужный коэффициент и подключить выход таймера к выводу ОС1В. При размыкании контактов датчика микроконтроллер должен отключить сигнал от внешнего вывода ОС1В и подать на него низкий логический уровень. Если контакты всех датчиков разомкнуты, то внешний вывод должен оставаться отключенным.

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

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

Договоримся, что датчику, подключенному к входу PD.0 (рис.1), будет соответствовать нота «До». Следующему датчику — нота «Ре», и так далее до ноты «Си». Коэффициенты деления для каждой из нот расчитываются по законам музыкального ряда исходя из частот звучания нот, указанных в табл.1.

Выполнение работы.

Разработка программы на языке С++

Возможный вариант программы на языке С++ приведен в листинге 1. В программе применяются следующие операторы.

for

Оператор цикла. Мы уже встречали оператор цикла while. Оператор for — это альтернативный способ организации циклов. В общем случае оператор for записывается следующим образом:

for (Выр1; Выр2; Выр3) {

Тело цикла;

}

Выр1, Выр2 и ВырЗ — любые корректные выражения языка С++. Каждое из этих выражений имеет свое определенное назначение.

Выр1 — это команда начальной-установки. Она выполняется один раз перед началом цикла.

Выр2 — условие выполнения цикла. Обычно это логическое выражение. Значение Выр2 проверяется в начале каждого прохода. Пока результат этого выражения «Истина» (не равен нулю), цикл продолжается. Как только результат Выр2 примет значение «Ложь» (станет равен нулю), цикл прекращается.

ВырЗ — операция, выполняемая с параметром цикла. Это выражение выполняется в конце каждого прохода. Обычно в качестве ВырЗ ставится команда, увеличивающая параметр цикла на единицу. Пример применения оператора for:

for (i=0; і<10; І++) {

Тело цикла;

}

Это простейший цикл с параметром і. Перед началом цикла параметру присваивается нулевое значение. Цикл выполняется до тех пор, пока і меньше десяти. Каждый раз после выполнения команд, составляющих тело цикла, оператор і++ увеличивает значение переменной і на единицу. Выражение і++ представляет собой одно из сокращений языка С++. В развернутом виде та же команда выглядит так: i=i+l.

goto

Команда безусловного перехода. Тот, кто знаком с языком программирования Basic, хорошо знает эту команду. Команда goto в языке С++ то же самое, что rjmp на Ассемблере. Она имеет всего один параметр — имя метки. В строке 19 нашей программы (листинг 1) команда goto передает управление к строке 14 (по метке ml).

К роме двух новых операторов, в программе появляется новое понятие: массив.

Перед тем, как использовать массив, его, как и переменную, нужно описать. Описание массива очень похоже на описание переменной. В общем виде это выглядит следующим образом: Тип Имя [Разм].

Как и в случае с переменной, сначала указывается тип массива, затем его имя:

  • тип массива может принимать те же значения, что и тип переменной;

  • имя массива выбирается в соответствии со стандартными правилами выбора имен.

В квадратных скобках указывается размер массива, то есть количество его элементов. В языке С++ при описании массивов необходимо обязательно указывать их размер, чтобы транслятор мог зарезервировать память для их размещения. Однако в любом месте программы размер массива допускается изменять. Это делается при помощи повторного описания массива, но уже с другим значением размера. Если новый размер окажется больше старого, массив просто пополнится новыми членами. При этом значения старых членов сохраняются. Если новый размер окажется меньше старого, все лишние члены удаляются. Значения удаленных членов массива будут утеряны.

Пример описания массива: int rabtab[5].

Описанный выше массив имеет имя rabtab, тип int и количество членов, равное пяти. Рассмотрим применения массивов. С любым элементом описанного выше массива можно работать как с отдельной переменной. Например, можно присвоить ему значение:

rabtab [1] = 231; // Первому члену массива присваивается

// значение 231

Можно наоборот, значение одного из элементов массива присвоить переменной:

х = rabtab[8]; // Переменной х присваивается значение

// восьмого элемента массива

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

Если размер массива равен N, то указатель массива может принимать значения от 0 до N-1. Если окажется, что значение указателя выходит за указанные пределы, то транслятор выдаст сообщение об ошибке. В качестве указателя массива можно использовать не только числовую константу. В квадратные скобки вы можете вписать любую переменную или любое выражение. Главное, чтобы значение этого выражения входило в область допустимых значений.

При описании массива можно одновременно производить его инициализацию. Под инициализацией понимается присвоение начальных значений всем элементам массива.

Строка описания массива, производящая его инициализацию, выглядит следующим образом:

int rabtab[5] = {23, 41, 52, 287, 40, 51};

char txtl[] = "Lugansk" ;

Начальные значения всех элементов записываются после знака «=» в фигурных скобках через запятую (числовые значения) либо в кавычках в виде символьной строки. В последнем случае в каждый элемент массива записывается код одного из символов.

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

Еще один аспект программирования на языке С++, который мы будем впервые использовать в программе, — это директивы локализации переменных и массивов. Так как наша версия С++ специально предназначена для составления программ для микроконтроллеров, она просто обязана иметь возможность явно указывать вид памяти, где нужно хранить ту либо иную переменную или массив.

По умолчанию транслятор сам решает эту задачу и размещает переменные и массивы по своему усмотрению. Но иногда вопрос размещения имеет принципиальное значение. В этом случае применяются специальные директивы. Эти директивы используются в процессе описания и указывают транслятору, в каком из видов памяти разместить данную конкретную переменную либо массив. Например, если вы желаете, чтобы значение вашей переменной хранилось в одном из регистров общего назначения, то при описании этой переменной вы должны использовать директиву register. Примеры таких описаний:

register int alpha; // Определение регистровой переменной alpha

register unsigned int beta // Определение регистровой переменной beta

register int gamma @10; // Определение регистровой переменной gamma

// с конкретным указанием регистра (R10), где она должна храниться

Второй тип памяти, где могут храниться переменные — это энергонезависимая память данных (EEPROM). Для размещения переменных в этой памяти используется директива eeprom. Причем в EEPROM могут храниться как переменные, так и массивы. Команда описания в этом случае будет выглядеть следующим образом:

eeprom char beta; // Определение переменной beta

// с размещением в EEPROM

eeprom long array1[5] ; // Определение массива array1 в EEPROM

eeprom char string[] = "Hello" // Определение текстового массива

При размещении массива в EEPROM необходимо учитывать, что указатель массива всегда должен иметь не менее 16 битов. То есть иметь тип int либо unsigned int.

Еще один тип памяти, где могут храниться данные,—это программная память. Но в этом случае мы уже не сможем в программе изменять значения переменных и элементов массива. Эти значения определяются один раз и только при инициализации. Для указания того факта, что массив или переменная должны храниться в программной памяти, используется директива flаsh.

В качестве примера описания массива в программной памяти обратимся к строке 2 программы (листинг 1). Массив или переменная с индексом flash могут быть только глобальными. Поэтому их описание всегда располагается в начале программы перед описанием всех функций.

Листинг 1.

1 #include <tiny2313.h>

// Объявление и инициализация массива коэффициентов деления

  1. flash unsigned int tabkd[7] = {4748, 4480, 4228,

3992, 3768, 3556, 3356};

  1. void main(void) {

  2. unsigned char count; // Определяем переменную count

  3. unsigned char temp, // Определяем переменную temp

  4. PORTB=0x00; // Инициализация порта РВ

  5. DDRB=0x08;

  6. PORTD=0x7F; // Инициализация порта PD

  7. DDRD=0x00;

  8. ACSR=0x80; // Инициализация (отключение) компаратора

  9. TCCR1A=0x00; // Инициализация таймера счетчика Т1

  10. TCCR1B=0x09;

  11. while (1){

  12. m1: temp=PIND;

  1. for (count=0; count<7; count++) {

// Цикл сканирования датчиков

//Проверка младшего бита переменной temp

16 if ((temp&1)==0) goto m2;

17 temp >>= 1; // Сдвиг содержимого temp

}

  1. TCCR1A=0x00; // Выключение звука

  2. goto m1; // Переход на начало

// Запись коэффициента деления таймера.

20 m2: OCR1A=tabkd[count];

21 TCCR1A=0x40; // Включение звука

};

}