Содержание
Урок 1. Первый проект
Урок 2. Управление кнопками
Урок 3. Подключение LCD
Урок 4. Использование ШИМ
Урок 5. Таймеры
Урок 6.1. Статическая индикация
Урок 6.2. Динамическая индикация
Урок 7.1. Генерация звука
Урок 7.2. Генерация звука. Продолжение
Урок 8.1. Передача данных через UART
Урок 8.2. Передача данных через UART. Продолжение»
Урок 9. Передача данных через SPI
Урок 10. Изучение АЦП. Простой вольтметр
Урок 11. Получение синуса при помощи ШИМ
Урок 12. Измерение температуры
Урок 13. Внешние прерывания.
Урок 14. Использование отладчика
Урок 15.1. Управление инкрементальным энкодером
Урок 15.2. Управление громкостью, при помощи энкодера
Урок 16. Управление RGB светодиодом
Урок 17. Использование ИК
Урок 18.1. Знакомство с графическим дисплеем
Урок 18.2 Вывод изображения на графический дисплей
Урок 18.3 Вывод русскоязычного текста
Урок 19. Формирование сигнала, при помощи ЦАП (R2R)
Урок 20. Опрос матричной клавиатуры
Урок 21. Сторожевой таймер
Урок 22.1 Воспроизведение wav. Введение.
Урок 22.2 Воспроизведение wav. Продолжение.
Урок 23.1 Работа с внешней памятью
Урок 23.2 Работа с файловой системой Fat

btnНа первом уроке мы научились подавать напряжение ножкой микроконтроллера. Теперь нужно научиться управлять микроконтроллером без перепрошивки.

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

Помните в первом уроке мы настраивали ножку как выход, т.е. мы могли ей подавать напряжение. Так вот, ножку можно настроить как вход. В таком режиме можно проверить есть ли на ней напряжение или нет.

Пример: создаем проект при помощи мастера проектов. Первую ногу настраиваем как выход, вторую как вход. При создании мастером проектов указываем, что ножка 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 микроконтроллерах можно подключить ножку к плюсу питания, через подтягивающий резистор. В этом случае даже, логика работы меняется наоборот — но если появится помеха, нам это не важно, ведь у нас на входе уже логическая единица.

Почему подключение через резистор? Допустим мы подключили вход к плюсу напрямую без резистора. Когда кнопка сработает, она притянет вход к земле, поэтому на входе будет короткое замыкание между + и землей. Если же стоит резистор, то при замыкании кнопки с одной стороны он так и останется подключен к +, а со второй стороны на нем появится земля от кнопки. Через резистор потечет ток, но его величина будет не такой большой.
btn_pup

Update2: Добавлен тест, в котором вы можете проверить на сколько хорошо вы усвоили материал урока

This movie requires Flash Player 9

222 комментария: Урок 2. Управление кнопками в AVR

  • А как опросить 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;
    }

  • Данные строки нужно убрать, хорошо попробую.

  • у вас скобки не правильно расставлены

  • Теперь понял 😳 , как говориться смотришь в книгу, видишь …… :mrgreen: еще раз за помощь.

  • Здравствуйте 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.

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

Ваш e-mail не будет опубликован. Обязательные поля помечены *

Свежие записи
Последние комментарии
  • Загрузка...
Счетчик
Яндекс.Метрика