Задача: заставить микроконтроллер по нажатию кнопки издавать звуковой сигнал.
Схема для нашего примера здесь. Файлы проекта здесь.
Создаем в старом workspace проект ring.
Задаем настройки проекта для конфигурации Release:
Выбираем тип микроконтроллера.
General Options > Target > Processor configuration
У меня это ATmega8535.
Разрешаем использование имен битов определенных в хидер файле
В General Options > System ставим галочку Enable bit definitions in I/O-Include files
До сих пор мы не пользовались именами битов, но сегодня они нам понадобятся.
Меняем тип выходного файла.
Linker > Output.
B поле Output file cтавим галочку Override default и заменяем расширение d90 на hex
В поле Format выбираем Other и в выпадающем меню Output format выбираем тип файла intel-standart
Сохраняем проект и workspace.
______________________________ Прерывание ___________________________
Довольно просто, но есть ряд принципиальных моментов.
Во-первых:
- вы делали свою работу
- параллельно кто-то покупал осциллографы
- по наступлению события «осциллографы закупили» - вы прерываете выполнение своей работы
- некоторое время вы занимаетесь другой работой – тащите осциллографы
- потом вы возвращаетесь на рабочее место и продолжаете делать свою работу с того места, на котором остановились
Во-вторых:
- вы вполне могли бы послать начальника и никуда не идти
- уйдя за осциллографами, вы могли задержаться там надолго, а то и вовсе не вернуться
- вернувшись на рабочее место, вы могли бы уже позабыть свои гениальные идеи
_______________________________________________________________
Теперь поговорим о таймере. ATmega8535 имеет на борту три таймера/счетчика - два восьмиразрядных (T0, T2) и один шестнадцатиразрядный (T1). Мы будем использовать восьмиразрядный таймер/счетчик T0. В состав этого таймера входят три регистра - регистр управления TCCR0, счетный регистр TCNT0 и регистр сравнения OCR0. Когда таймер запущен, счетный регистр TCNT0 увеличивает свое значение на единицу на каждый перепад тактового сигнала. Частота тактового сигнала выбирается из нескольких возможных значений в регистре управления TCCR0. Также с помощью этого регистра устанавливается режим работы таймера. Таймер T0 может вызвать прерывание по наступлению события “переполнение” – это когда переполняется счетный регистр TCNT0 и по наступлению события “совпадение” – это когда значение счетного регистра TCNT0 становится равным значению регистра сравнения OCR0. Флаги разрешающие эти прерывания находятся в регистре TIMSK.
Мы настроим таймер/счетчик Т0 так, чтобы он вызывал прерывание по событию “совпадение” с частотой 5 кГц. В функции обработчике будем инвертировать состояние вывода микроконтроллера, к которому подключен пьезодинамик. Таким образом, частота пищания пьезика будет равна 2,5 кГц. (Подключен именно пьезодинамик! Не перепутайте. У пьезодинамика сопротивление зависит от частоты и на 2,5 КГц оно обычно еденицы Ком, поэтому его можно подключать к выводу микроконтроллера напрямую, без ограничительного резистора).
Теперь о программе. Построчно писать программу уже не получится, поэтому я сразу приведу ее текст. Ниже мы последовательно разберем все ее строки, и все станет понятно. Макросы я намеренно не стал использовать, программа маленькая, не хочется ее загромождать.
//Подаем голос микроконтроллером AVR
#include <ioavr.h>
#include <intrinsics.h>
int main(void)
{
//настраиваем порты ввода-вывода
DDRD = (0<<PD1)|(1<<P0);
PORTD = (1<<PD1)|(0<<PD);
//настраиваем таймер Т0
TCCR0 = (1<<WGM01)|(0<<GM00)|(0<<CS02)(1<<CS01)(0<<CS00)
TCNT0 = 0;
OCR0 = 0xc8;
//разрешаем прерывания
__enable_interrupt();
//основной цикл программы – опрос кнопки
while(1){
if ((PIND & (1<<PD0)) == 0)
TIMSK = (1<<OCIE0);
else
TIMSK = 0;
}
return 0;
}
//обработчик прерывания таймера Т0
#pragma vector = TIMER0_COMP_vect
__interrupt void Timer0CompVect(void)
{
PORTD ^= (1<<PD1); //инвертируем сигнал на выводе PD1
}
Настройка портов
DDRD = (0<<PD1)|(1<<PD0);
PORTD = (1<<PD1)|(0<<PD0);
Настройка таймера
Режим работы таймера Т0 – СТС(сброс при совпадении), тактовый сигнал – clk/8. Отражаем это в регистре TCCR0
TCCR0 = (1<<WGM01)|(0<<WGM0)|(0<<CS02)(1<<CS01)(0<<CS00)
Обнуляем на всякий случай счетный регистр TCNT0
TCNT0 = 0;
В регистр сравнения OCR0 записываем 0xc8. Почему? Потому что я посчитал это на калькуляторе. Ну а на бумаге этот расчет выглядит так.
Тактовая частота микроконтроллера 8 МГц
Тактовый сигнал таймера равен 8000000 Гц/8 = 1000000 Гц.
Время одного такта таймера 1/1000000 = 1 мкс
Время одного такта нужной нам частоты 1/5000 Гц = 200 мкс
Сколько тактов таймера укладывается в 200 мкс? 200/1 = 200 тактов
200 в шестнадцатеричной системе = 0xс8
OCR0 = 0xс8;
Подробное описание таймера T0 смотрите в документации на ATMega8535.
Таймер мы настроили, разрешаем общее прерывание, используя встроенную функцию.
__enable_interrupt();
Опрос кнопки
while(1)
{
if ((PIND & (1<<PD0)) == 0){
//если кнопка нажата – микроконтроллер должен верещать
}
else {
//если нет - молчать как рыба
}
}
Не забывайте == это не оператор присваивания =.
Обработка нажатия/отпускания кнопки
TIMSK = (1<<OCIE1); // разрешаем прерывание таймера Т0 по событию совпадение
TIMSK = 0; //запрещаем прерывание
Поскольку мы используем всего один таймер, то нет нужды в установке или сбросе отдельных битов.
Функция прерывания
_____________________ Cинтаксис функции прерывания _____________________
Функция прерывания задается с помощью директивы #pragma vector= и служебного слова __interrupt. Функция должна иметь тип void и не должна принимать никаких параметров.
#pragma vector = Address
__interrupt void Name(void)
{
//здесь располагается наш код
}
Name – имя функции, выбираем на наше усмотрение
Address – адрес вектора прерывания, можно задавать числом, можно именами определенными в заголовочном файле микроконтроллера (iom8535.h – раздел Interrupt Vector Definitions)
______________________________________________________________
Для нашей задачи функция-обработчик прерывания выглядит так
#pragma vector = TIMER0_COMP_vect
__interrupt void Timer0CompVect(void)
{
PORTD ^= (1<<PD1); //инвертируем сигнал на выводе PD1
}
Ну вот собственно и все. Надеюсь все понятно.
В следующий статье заставим микроконтроллер играть мелодию.