На первом уроке мы научились подавать напряжение ножкой микроконтроллера. Теперь нужно научиться управлять микроконтроллером без перепрошивки.
Зачем это нужно? Например, вы сделали часы на микроконтроллере, нужно выставить время, но очень не удобно каждый раз перепрошивать, когда собьется время. Намного удобнее пользоваться кнопками, например, одной менять часы, другой минуты.
Помните в первом уроке мы настраивали ножку как выход, т.е. мы могли ей подавать напряжение. Так вот, ножку можно настроить как вход. В таком режиме можно проверить есть ли на ней напряжение или нет.
Пример: создаем проект при помощи мастера проектов. Первую ногу настраиваем как выход, вторую как вход. При создании мастером проектов указываем, что ножка PB1 будет входом, на ней же включаем подтягивающий резистор, реализуется это так:
После создания проекта приведем код к такому виду:
#include <mega8.h> #include <delay.h> void main(void) { PORTB=0x02; DDRB=0x01; while (1) { if(PINB.1==0) { PORTB.0=1; delay_ms(100); PORTB.0=0; delay_ms(100); } }; } |
Как мы видим, по сравнению с первым уроком изменилась настройка порта
PORTB=0x02; DDRB=0x01; |
Подробнее о том что значат данные строки можно почитать тут
Также появилась новая строчка
if(PINB.1==0) {} |
данную строчку нужно читать так — если на ножке 1 порта В подключили землю (0 потенциал), то выполнить код в фигурных скобках. В нашем примере это код из первого урока. Если кнопка не замкнута, то ничего не делать. Промоделировать можно в Proteuse.
Вместо кнопки можно поставить датчик, реле и т.п., вместо светодиода — пищалку, получится сигнализация.
Архив с прошивкой и файлом протеуса доступен тут
Update1: Зачем нужна подтяжка порта?
У входа мк большое сопротивление, если будут течь даже микротоки вызванные помехами, то по закону Ома U=R*I это может привести к тому, что на входе появится лог 1. Чтобы не было таких проблем в AVR микроконтроллерах можно подключить ножку к плюсу питания, через подтягивающий резистор. В этом случае даже, логика работы меняется наоборот — но если появится помеха, нам это не важно, ведь у нас на входе уже логическая единица.
Почему подключение через резистор? Допустим мы подключили вход к плюсу напрямую без резистора. Когда кнопка сработает, она притянет вход к земле, поэтому на входе будет короткое замыкание между + и землей. Если же стоит резистор, то при замыкании кнопки с одной стороны он так и останется подключен к +, а со второй стороны на нем появится земля от кнопки. Через резистор потечет ток, но его величина будет не такой большой.
Update2: Добавлен тест, в котором вы можете проверить на сколько хорошо вы усвоили материал урока
А как опросить 3 кнопки-аналогично или как?
Аналогично
Здравствуте.
void main(void)
{
PORTB=0x02;
DDRB=0x05;
while (1)
{
if(PINB.1==0)
{
PORTB.0=! PORTB.0;
PORTB.2=! PORTB.2;
delay_ms(100);
при нажатии кнопки у меня подается еденица на «0» вывод и «2» вывод мк, при следующем нажатии встают в ноль, так вот не могу понять как сделать еще одно положение что бы 0 порт давал ипульсы а второй оставался без изменения.
спасибо за внимание
Сделайте переменную, которая будет увеличиваться по нажатию кнопки. В зависимости от величины переменной будут разные режимы работы
объясните пожалуйста, как организовать выбор эффектов, смену эффектов из нескольких, например из 10 одной кнопкой
через прерывание считайте длительность нажатия, длинное нажатие — подтверждение, кратковременное выбор.
void main(void)
{
char a = 0;
PORTB=0x01; // 0b 0000 0001
DDRB=0x06; // 0b 0000 0110
while (1)
{
if (PINB.0==0 && a==0)
{
PORTB.1=1;
PORTB.2=1;
delay_ms(100);
a=1;
}
if (PINB.0==0 && a==1)
{
PORTB.1=0;
PORTB.2=1;
delay_ms(100);
a=2;
}
if (PINB.0==0 && a==2)
{
PORTB.1=0;
PORTB.2=0;
delay_ms(100);
a=0;
}
}
}
подскажите как добавить мигание на PORTB.1 при if (PINB.0==0 && a==1) , я понимаю что при помощи таймера, через delay кнопка плохо отрабатывает, но ни как не понимаю как связать.
Изучайте таймеры и прерывания. Почитайте тут http://avr-start.ru/?p=1389
код для работы с кнопками не всегда стабильно работает при его написании на C компиляторах (codevision, avrstudio). При частом нажимании на кнопку происходит как будто дребезг (при отпускании светодиод горит), даже с подключением внутренних и внешних pull-up резисторов. А когда за дело берётся подробный ассемблер то все работает стабильно…
Помогите пожалуйста разъясните ситуацию.
Значит логика программы на C и асме отличаются. Это и есть дребезг, его нужно обрабатывать, я обычно сравниваю состояния кнопки нажато/отжато.
Здравствуйте. Чет не могу сообразить как кнопкой менять режим работы светодиода(ов). Не дадите подсказку?
создаете переменную, в зависимости от значения которой будет выбрано несколько режимов работы. значение переменной меняете кнопкой
if(regim==0)//если режим 0, то делаем..
{
PORTB=0xFF
…
}
if(PIND.0==0)//меняем режим
{
regim=regim+1;
}
Уважаемый Админ подскажите как сделать так чтобы при нажатие кнопки загарелся светадиод , а при пофторном нажатие погас . зарание спасибо
Смотри коммент выше, делаешь переменную с 2 состояниями, в зависимости от значения включать/выключать светодиод
ситуация: атмега128. зашита программа с 2 режимами свечения светодиодов. меняется режим удержанием кнопки. вопрос: можно ли переключать режимы просто нажав кнопку (не держа ее)? ка правильнее выполнить?
инвертируйте состояние по нажатию кнопки PORTB.1 = !PORTB.1
admin здравствуйте, подскажите как реализовать простой счетчик нажатий, т. е. нажал кнопку число увеличилось на 1 нажал еще +1 и т. д. и все это выводит на семисигментный дисплей, вот код при нажатии цифры просто пробегают до максимума.
#include
#include
void main(void)
{
int number = 0;
PORTB=0xFF;
DDRB=0xFF;
PORTC=0x00;
DDRC=0x00;
while (1)
{
switch(number)
{
case 0:{PORTB=0b00111111; break;}
case 1:{PORTB=0b00000110; break;}
case 2:{PORTB=0b01011011; break;}
case 3:{PORTB=0b01001111; break;}
case 4:{PORTB=0b01100110; break;}
case 5:{PORTB=0b01101101; break;}
case 6:{PORTB=0b01111101; break;}
case 7:{PORTB=0b00000111; break;}
case 8:{PORTB=0b01111111; break;}
case 9:{PORTB=0b01101111; break;}
}
delay_ms(100);
if (PINC.0==1)
delay_ms(50);
number++;
};
в скобки условие
теперь работает, спасибо, а как задать условие, что бы при нажатии на кнопку прибавлялось только 1 даже если кнопка продолжает быть нажатой, сейчас пока держишь кнопку получается идет инкриминирование параметра number.
OldState=NewState;
{
TCNT1H=0×00;
TCNT1L=0×00;
}
Данные строки нужно убрать, хорошо попробую.
у вас скобки не правильно расставлены
Теперь понял 😳 , как говориться смотришь в книгу, видишь …… еще раз за помощь.
Здравствуйте admin, проблема с ошибкой разрешилась но теперь при компиляции выпадает куча Варнингов, посмотрите пожалуйста что не так.
#include
#include
#include
int number = 0;
int number1 = 0;
int number2 = 0;
int i=0;
int vitok=0;
int NewState,OldState,upState,downState;
// Timer 1 output compare A interrupt service routine
interrupt [TIM1_COMPA] void timer1_compa_isr(void)
{
PORTD=0x00;
NewState=PINC & 0b00000011;
if(i==0)
{
number=number1;
PORTD.6=1;
i++;
}
else
{
number=number2;
PORTD.7=1;
i=0;
}
switch(number)
{
case 0:{PORTB=0b00111111; break;}
case 1:{PORTB=0b00000110; break;}
case 2:{PORTB=0b01011011; break;}
case 3:{PORTB=0b01001111; break;}
case 4:{PORTB=0b01100110; break;}
case 5:{PORTB=0b01101101; break;}
case 6:{PORTB=0b01111101; break;}
case 7:{PORTB=0b00000111; break;}
case 8:{PORTB=0b01111111; break;}
case 9:{PORTB=0b01101111; break;}
}
if(NewState!=OldState)
{
switch(OldState)
{
case2:
{
if(NewState==3) upState++;
if(NewState==0) downState++;
break;
}
case0:
{
if(NewState==2) upState++;
if(NewState==1) downState++;
break;
}
case1:
{
if(NewState==0) upState++;
if(NewState==3) downState++;
break;
}
case3:
{
if(NewState==1) upState++;
if(NewState==2) downState++;
break;
}
}
OldState=NewState;
}
TCNT1H=0x00;
TCNT1L=0x00;
}
void main(void)
{
PORTB=0xFF;
DDRB=0xFF;
PORTD=0xC0;
DDRD=0xFF;
PORTC=0x00;
DDRC=0x00;
// Timer/Counter 1 initialization
// Clock source: System Clock
// Clock value: 1000,000 kHz
// Mode: CTC top=OCR1A
// OC1A output: Discon.
// OC1B output: Discon.
// Noise Canceler: Off
// Input Capture on Falling Edge
// Timer 1 Overflow Interrupt: Off
// Input Capture Interrupt: Off
// Compare A Match Interrupt: On
TCCR1A=0x00;
TCCR1B=0x0A;
TCNT1H=0x00;
TCNT1L=0x00;
ICR1H=0x00;
ICR1L=0x00;
OCR1AH=0x03;
OCR1AL=0xE8;
OCR1BH=0x00;
OCR1BL=0x00;
// Timer(s)/Counter(s) Interrupt(s) initialization
TIMSK=0x10;
// Global enable interrupts
#asm(«sei»)
while (1)
{
if(upState >= 10)
{
vitok++;
upState=0;
}
if(downState >= 10)
{
vitok—;
downState=0;
}
number1=vitok%10;
number2=vitok/10;
delay_ms(50);
};
}
так и не могу победить эту проблему при компиляции вышевыложенного кода выдает вот такой Warning: label ‘case2’ was declared, but not referenced
label ‘case0’ was declared, but not referenced
label ‘case1’ was declared, but not referenced
label ‘case3’ was declared, but not referenced
что ни так, и соответственно энкодер не работает
после case должен быть пробел
спасибо, теперь компилирует без ошибок.
доброго времени суток admin, прошу еще помощи, хочу сделать что бы число увеличивалось не с каждый щелчком энкодера, а например через 6 импульсов происходило инкремироние числа, в положительную сторону у меня получилось, а как это же сделать что бы число декремировалось через 6 импульсов. Вот кусок цикла который работает только на инкремирование
while (1)
{
if(upState >= 4)
{
impuls++;
upState=0;
}
if(downState >= 4)
{
impuls—;
downState=0;
}
if(impuls >= 6)
{
vitok++;
impuls=0;
}
number1=vitok%10;
number2=vitok/10;
if(flag>=1000)
{
vitok_eep=vitok;
}
};
сделайте 2 разных переменных и обе увеличивайте до 6
в очередной раз спасибо, все получилось, как всегда все элементарно и просто было.
еще раз здравствуйте, в очередной раз нужна ваша помощи. Пытаюсь данный код переделать под ATTINY2313, а то на Mega 8 остается много пустых ног, нецелесообразно получается. Подключение к портам переделал, но при симуляции в протеусе ни чего не происходит даже динамическая индикация не работает, думал что то инициализацией таймера, насколько я смог проверить вроде все в порядке, подскажите что не так, за ранее еще раз спасибо вот код:
/*
микроконтроллер ATTINY2313
тактовая частота 1МГц, fuse по умолчанию
*/
#include
#include
#include
/*переменные для работы с динамической индикации*/
int number = 0;
int number1 = 0;
int number2 = 0;
int i=0;
/*переменные для подсчета импульсов энкодера и приращения их в кол-во витков*/
int impulsp,impulsl, vitok=0;
/*переменные для обработки энкодера*/
int NewState,OldState,upState,downState;
int k=6; //переменная для перевода кол-ва импульсов энкодера в обороты станка
int flag=0; //переменная задержки перед записью в eeprom
eeprom int vitok_eep; //переменная хранящая в eeprom последнее значение показаний счетчика
// Timer 1 output compare A interrupt service routine
interrupt [TIM1_COMPA] void timer1_compa_isr(void)
{
PORTD=0b00000000;
NewState=PINA & 0x03;
flag++;
/*обработка динамической индикации*/
if(i==0)
{
number=number1;
PORTD.5=1;
i++;
}
else
{
number=number2;
PORTD.6=1;
i=0;
}
switch(number)
{
case 0:{PORTB=0b00111111; break;}
case 1:{PORTB=0b00000110; break;}
case 2:{PORTB=0b01011011; break;}
case 3:{PORTB=0b01001111; break;}
case 4:{PORTB=0b01100110; break;}
case 5:{PORTB=0b01101101; break;}
case 6:{PORTB=0b01111101; break;}
case 7:{PORTB=0b00000111; break;}
case 8:{PORTB=0b01111111; break;}
case 9:{PORTB=0b01101111; break;}
}
/*обработка энкодера*/
if(NewState!=OldState)
{
switch(OldState)
{
case 2:
{
if(NewState==3) upState++;
if(NewState==0) downState++;
break;
}
case 0:
{
if(NewState==2) upState++;
if(NewState==1) downState++;
break;
}
case 1:
{
if(NewState==0) upState++;
if(NewState==3) downState++;
break;
}
case 3:
{
if(NewState==1) upState++;
if(NewState==2) downState++;
break;
}
}
OldState=NewState;
}
TCNT1H=0x00;
TCNT1L=0x00;
}
/*основная функция*/
void main(void)
{
PORTB=0xFF;
DDRB=0xFF;
PORTD=0b01100000;
DDRD=0b01100000;
PORTA=0b00000111;
DDRA=0x00;
// Timer/Counter 1 initialization
// Clock source: System Clock
// Clock value: 1000,000 kHz
// Mode: CTC top=OCR1A
// OC1A output: Discon.
// OC1B output: Discon.
// Noise Canceler: Off
// Input Capture on Falling Edge
// Timer 1 Overflow Interrupt: Off
// Input Capture Interrupt: Off
// Compare A Match Interrupt: On
TCCR1A=0x00;
TCCR1B=0x0A;
TCNT1H=0x00;
TCNT1L=0x00;
ICR1H=0x00;
ICR1L=0x00;
OCR1AH=0x03;
OCR1AL=0xE8;
OCR1BH=0x00;
OCR1BL=0x00;
// Timer(s)/Counter(s) Interrupt(s) initialization
TIMSK=0x10;
vitok=vitok_eep; //выводим последние показания счетчика записанные в eeprom
// Global enable interrupts
#asm(«sei»)
/*рабочий цикл*/
while (1)
{
if(upState >= 4) //если вращаем энкодер вправо,
{
impulsp++; // начинаем отсчитывать импульсы
upState=0;
}
if(impulsp >= k) // если насчитали 6 и более импульсов
{
vitok++; // увеличиваем число на индикаторе на 1
impulsp=0;
}
if(vitok >99) // если на индикаторе 99 и более,
{
vitok=0; // обнуляем показания
}
if(downState >= 4) // /если вращаем энкодер влево,
{
impulsl++; // начинаем отсчитывать импульсы
downState=0;
}
if(impulsl >= k) // если насчитали 6 и более импульсов
{
vitok—; // уменьшаем число на индикаторе на 1
impulsl=0;
}
if(vitok =1000) //ждем примерно 1 сек., если показания не поменялись
{
vitok_eep=vitok; //записываем последнее значения счетчика в eeprom
}
};
}
После изучения статьи и моделирования в протеус возник такой вопрос: строки
PORTB=0x02;
DDRB=0x01;
говорят от том, что ножка PB.1 настроена на вход и к ней подтянут внутренний резистор. Почему при моделировании на этой ножке по умолчанию стоит высокий логический уровень? 🙁 . Как мы его там задали и зачем? Или я что-то не так понял??? Admin, подскажите пожалуйста.
Обновлено
Уважаемый Админ подскажите
как сделать так чтобы при
нажатие кнопки загарелся
светадиод , а при пофторном
нажатие погас . зарание спасибо
очень прошу скиньте код на мыло, заранее благодарен!
почитайте комментарии выше, уже много раз этот вопрос ответили
админ здравствуйте, хочу одной кнопкой менять режимы мигания светодиодов. Я создал переменную, при каждом нажатии кнопки переменная увеличивается, соответственно под каждым значением переменной свой эффект мигания должен быть. В теории все логично и правильно, но как зациклить мигание светодиодов? Вот код, я делал с помощью внешнего прерывания:
interrupt [EXT_INT0] void ext_int0_isr(void)
{
i++;
if(i==1)
{
PORTD.0=1;
delay_ms(100)
PORTD.0=0;
delay_ms(100)
}
if(i==2)
{
PORTD.1=1;
delay_ms(100)
PORTD.1=0;
delay_ms(100)
}
if(i==3)
{
PORTD.2=1;
delay_ms(100)
PORTD.2=0;
delay_ms(100)
}
Представленный код работает при каждом нажатии кнопки соответственно, но допустим при i==1,PORTD.0 только включится и выключится а постоянно не мигает(так и с остальными ).
Логично добавить оператор while, пока i==1 — мигать светодиодом постоянно, как только i==2 перейти на другой светодиод. Вот код с оператором while:
interrupt [EXT_INT0] void ext_int0_isr(void)
{
i++;
while(i==1)
{
PORTD.0=1;
delay_ms(100)
PORTD.0=0;
delay_ms(100)
}
while(i==2)
{
PORTD.1=1;
delay_ms(100)
PORTD.1=0;
delay_ms(100)
}
но тут и проблема начинается, при первом нажатии кнопки при i==1 светодиод мигает постоянно как положено, а вот при втором нажатии,i==2, ни какой реакции нет, так и продолжает мигать первый светодиод. Получается на второе и последующее увеличение переменной никакой реакции нет. Вопрос, почему нет реакции на увеличение переменной? Или если я что то не так делаю, помогите пожалуйста справится с данной задачей
все что while в основной цикл
Админчик, благодарочка, все работает!!!!
Админ,а как теперь сделать что б переключение эффектов сразу менялось при нажатии кнопки а не ждать пока первый эффект пройдет до конца а потом переключается на второй? что то не могу сообразить, постоянно что не пробовал один и тот же результат.
как вариант сделать задержки на таймерах, а не на delay.