Задача: заставить микроконтроллер по нажатию кнопки издавать звуковой сигнал.
Схема для нашего примера здесь. Файлы проекта здесь.
Создаем в старом 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
}
Ну вот собственно и все. Надеюсь все понятно.
В следующий статье заставим микроконтроллер играть мелодию.
Comments
Все оказывается не так уж и сложно!
#pragma vector = TIMER0_COMP_vect
__interrupt void Timer0CompVect(void)
{
PORTD ^= (1
допустим МК подключён к com-порту
(WinForms)
private void button1_Click(o bject sender, EventArgs e)
{
????
}
в примере тут кнопка подсоединена прямо к МК, а мне хочется, чтобы это было, например, событие нажатия кнопки в моей проге на компьютере.
Нашел тут http://www.activexperts.com/files/activcomport/manual.htm#chap_using_activecomport какой-то фреймворк по COM-порту, но как им "достучаться" до МК, я без понятия.
мне нужно именно так
chipenable.ru/index.php/programming-c/43-avr-usart-uart.html
else
TIMSK = 0;
на
else{
TIMSK = 0;
ClearBit(PORTD, PD1);
}
а то иначе при отпускании кнопки регистр PD1 может оказаться в 1.
Физика в том, что вывод микроконтроллер а может работать как вход или как выход. Для настройки его режима работы есть два регистра DDRx и PORTx (вместо x - буквы A,B,C,D). Чтобы настроить пин на вход нужно в DDRx записать 0 в соответствующее место. Чтобы настроить пин на выход нужно записать 1. В режиме входа пин микроконтроллер а может находиться в третьем состоянии или быть подтянут с помощью внутреннего резистора к плюсу питания. Это выполняется записью 0 или 1 в регистр PORTx
здесь:
DDRD = (1 сдвиг влево PD1);
PORTD = (1 сдвиг влево PD0);
в предыдущем же уроке где задаются макроопределени я здорово всё объяснено:
reg |= (1 сдвиг влево bit)
Так вот не понятно почему здесь нет опреатора "|" ? Пишу сдвиг влево, потому что где стоит стрелка влево обрезает сообщение ;-)
Без нее компилятор выдает множество ошибок, а с ней все как по маслу.
DDRD = (0
DDRD.0=0;PORTD.0=1;DDDRD.1=1;
Я не ошибаюсь?
TCCR0 = (1
По поводу этой строчки.
Биты WGM01 и WGM00 определяют режим работы таймера - 10 это режим СТС. Биты CS02,CS01,CS00 определяют коэффициент предделителя. В данном случае он будет равен 8. Об этом всем написано в datasheet на микроконтроллер .
При возникновении прерывания скаже по событию А может ли его обработку перебить прерывание по тому же событию А. Или же сброшеный,в момент выполнения процедуры обработки прерывания, флаг I регистра SREG не даст этого сделать, игнорировав повторное прерывание А. :-?
Code:
#include <intrinsics.h>
вроде программные задержки не используются
Вот для байтового T2 - действительно, есть.
Либо я что-то не понимаю, либо надо поправить.
Например
Code:
TIMSK = (1<<OCIE0);
бит OCIE0 разрешает прерывание таймера Т0 по событию совпадение
Почему делим на 8.. и можно ли про систему расчета по подробнее
Code:
(0<<CS02)|(1<<CS01)|(0<<CS00);
означает, что тактовый сигнал таймера равен clk/8
//настраиваем порты ввода-вывода
DDRD = (0<<PD1)|(1<<PD0);
PORTD = (1<<PD1)|(0<<PD0);
Добрый вечер!на схеме PD0-кнопка а PD1-буззер.А в программе наоборот
И вот ещё что не понятно, каким образом частота пищания пьезодинамика будет равна 2,5 кГц. В протеусе не запищал, но сигнал пошёл.
Читая комментарии под статьями понял что вы делаете еще большую работу чем просто статьи, из за этого хочется аплодировать вам стоя.... Так держать!!!
Вот:
{
//настраиваем порты ввода-вывода
DDRD = (0
//настраиваем порты ввода-вывода
DDRD = (0
Объясните пожалуйсто ПОДРОБНО (на пальцах):
Code:
//настраиваем порты ввода-вывода
DDRD = (0<<PD1)|(1<<PD0);
PORTD = (1<<PD1)|(0<<PD0);
0
В avr studio и proteus программа не реагирует на нажатие кнопки (постоянно проверяется условие и прерывание не возникает). Я использовал PORTA, atmega16A
code if ((PIND & (1
Code:
if ((PIND & (1<< PD0)) == 0)
a
Code:
if ((PIND & (1<< PD0)) != 0)
Хотя могу ошибиться, сам разбраться пытаюсь.
[#define ENABLE_BIT_DEFINITIONS]
в начале проекта, зачем они это сделали не понятно, наверно мысля была. Если речь идет об IAR,неплохо бы это вставить в примеры, дабы народ не парился, как я.
Юзанием гугла кое как разобрался с прерываниями.
Для тех кто пишет в AVR Studio, вместо библиотеки нужно подключить библиотеку . Чтобы разрешить все прерывания используется функция sei().
Событие прерывания:
void TIMER0_COMP_vec t(void) __attribute__((interrupt))
И функция прерывания:
void TIMER0_COMP_vect(){...}
Объясните плиз что значит WGM01 и CS01?
Code:
DDRD=(1<<PD3)
так? и соответственно ниже тоже поменять соответственно после if и в прерывании?PORTD=(1<<PD2)
Вы пишете: Вывод, к которому подключена кнопка, нужно настроить на вход и включить подтягивающий резистор. т.е. при не нажатой кнопке на этом входе тестер должен показать 5v? Осцилограф у меня как-то остался в "прошлой жизни", пытаюсь тестером выявить неисправность. Монтаж исключается, файл использую Ваш, бузеров попробовал 3 разных. На выход этого приходит меандр 2.5 кГц, я правильно понял? ...Заблудился в трех соснаххххх
RSS feed for comments to this post