Содержание
Урок 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

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

  • admin, тот же самый эффект, что и с delay, вот код:
    #include
    int i;
    void wait(void)
    {
    TCNT1=0;
    while(TCNT1<750){};
    }
    interrupt [EXT_INT0] void ext_int0_isr(void)
    {
    i++;
    }
    void main(void)
    {
    #asm("sei")

    while (1)
    {

    while(i==1)
    {PORTA.2=0;
    PORTB.1=1;
    wait();
    PORTB.1=0;
    PORTB.2=1;
    wait();
    PORTB.2=0;
    PORTB.3=1;
    wait();
    PORTB.3=0;
    PORTB.4=1;
    wait();
    PORTB.4=0;
    PORTB.5=1;
    wait();
    PORTB.5=0;
    PORTB.6=1;
    wait();
    PORTB.6=0;
    PORTB.7=1;
    wait();
    PORTB.7=0;

    }

    if(i==2)
    {
    PORTB.2=1;
    wait();
    PORTB.2=0;
    wait();
    }

    if(i==3)
    {
    i=1;
    }

    };
    }

  • по нажатию кнопки нужно выставить флаг, а внутри wait по этому флагу делать break;

  • а как программно выставить флаг?

  • interrupt [EXT_INT0] void ext_int0_isr(void)
    {
    i++;
    flag = 1;
    }

    void wait(void)
    {
    TCNT1=0;
    while(TCNT1<750){};
    if(flag ==1)
    {flag = 0; break;}
    }

  • админчик, не работает, пишет «out of context», походу на break матерится

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

  • замените break на return

  • Доброго времени суток Admin. Помогите пожалуйста не могу понять как сделать так что при нажатие кнопки значение переменной увеличивалось n++1, не могу понять что не так делаю. Я знаю что уже была погожий комментарий. Посмотрите что не так пожалуйста . Текст программы
    #include
    #include
    #include
    #include
    #include
    #include
    #include
    #asm
    .equ __lcd_port=0x12 ;PORTD
    #endasm
    #include
    char lcd_buf[17];
    void main(void)
    {
    int set=0;

    PORTB=0x00;
    DDRB=0xFF;

    PORTD=0x00;
    DDRD=0x00;
    lcd_init(16);
    while (1)

    delay_ms(100);
    if(PINB.0==1);
    {
    delay_ms(50);
    set++;
    delay_ms(10);
    sprintf(lcd_buf,»ustanovka\n%d»,set );
    lcd_puts(lcd_buf);
    delay_ms(1000);
    lcd_clear();
    }
    }

  • Кнопку проверяют на наличие 0, при этом включают подтяжку, т.е. когда кнопка разомкнута, то на ней PINB==1

  • написал вместо break return, работает, но все равно как и прежде, то есть сначала до конца проходит первый эффект а потом только второй включается, вот код:
    interrupt [EXT_INT0] void ext_int0_isr(void)
    {
    i++;
    flag = 1;
    }
    void wait(void)
    {
    TCNT1=0;
    while(TCNT1<750){};
    if(flag ==1)
    {flag = 0; return;}
    }
    void main(void)
    {
    while (1)
    {

    if(i==1)
    {PORTA.2=0;
    PORTB.1=1;
    wait();
    PORTB.1=0;
    PORTB.2=1;
    wait();
    PORTB.2=0;
    PORTB.3=1;
    wait();
    PORTB.3=0;
    }
    if(i==2)
    {
    PORTB.2=1;
    wait();
    PORTB.2=0;
    wait();
    }

    if(i==3)
    {
    i=1;
    }

    };
    }

  • задача вроде и легкая а не пойму как реализовать

  • проверять флаг нужно внутри while

  • Снова здравствуйте,Я исправил ошибку, но все равно не работает. У меня при замыкание кнопки на порту которому подключил кнопку в Proteus появляется вместо синего цвета (уровень логической единицы) по условию PINB.0==0 должна изменятся значение переменной set, желтого цвета появляется на порту PORTB.0. Что я не так делаю? подключил так как у вас показано на рисунки. Помогите сделать прибор для производство. Поддержите отечественное производство.

  • Добавил резистор на один килоом между портом PORTB.2 и GROUD. Все равно состояние порта не меняется, так как там было единицы так и осталось. По идеи должно изменится на протиположное .Ещё раз текст моей программы
    #include
    #include
    #asm
    .equ __lcd_port=0x12 ;PORTD
    #endasm
    #include
    char lcd_buf[17];
    void main(void)
    {
    int set=0;

    PORTB=0x02;
    DDRB=0x01;

    PORTD=0x00;
    DDRD=0x00;
    lcd_init(16);
    while (1)

    delay_ms(100);
    if(PINB.0==0);
    {
    delay_ms(50);
    set++;
    delay_ms(10);
    sprintf(lcd_buf,»ustanovka\n%d»,set );
    lcd_puts(lcd_buf);
    delay_ms(1000);
    lcd_clear();
    }
    }

    По моей задумки должно работать как обычный счетчик при изменение состояние PORTB.2 показание должны увеличивается n=1 на lcd экрана должна увеличивать +1

  • Порой удивляюсь, откуда вы такое придумываете, в статье же все расписано, даже пример приведен — просто копируй и радуйся

    void main(void)
    {
    int set=0;

    //вот ту вы настраиваете порт b, 0x02 в шестнадцатеричной это 10 в двоичной (считается на калькуляторе)
    //значит вы записываете в PB1 единицу, что это значит зависит от строки DDRB
    PORTB=0×02;
    //1 в шестнадцатеричной это 1 в двоичной, таким образом ножка PB0 это выход, а раз PB1 равна нулю то значит вход, раз PORTB=0x2 значит вход подтянут.
    //таким образом имеем выход PB0 и подтянутый вход PB1.
    DDRB=0×01;

    ...
    while (1)

    //раз в 100 мс мы тупо ждем, это плохо, ибо нажатия на кнопку в эти моменты будут проскакивать
    delay_ms(100);

    //смотрим что у нас на нулевой ножке, но не забываем, что она у нас выход..
    //и на ней всегда висит 0, ибо так настроено выше, но никто не запрещает его читать
    //итого это условие бесполезно, ибо оно будет срабатывать всегда
    if(PINB.0==0);
    {
    //опять ждем, зачем?
    delay_ms(50);
    //увеличили, гуд
    set++;
    //опять ждем, снова зачем?
    delay_ms(10);
    sprintf(lcd_buf,»ustanovka\n%d»,set );
    lcd_puts(lcd_buf);
    //отлично сделали преобразование вывели на дисплей
    //потупили секунду зачем то
    delay_ms(1000);
    //и тут же стерли O_o, может правильнее очищать до того как вывести новые значения?
    lcd_clear();
    //тут было бы неплохо выставить флаг того что кнопка нажата
    }

    //а где то тут сбросить его, тогда когда кнопка отжата, т.е. PINB.2==1;
    }

    Можете еще параллельно кнопке кондер поставить на 0.1мкФ

  • СПАСибо, А сайт очень хороший, так держать!!!

  • Admin пожалуйста помоги не отказывай мне. За помощь готов оплатить. Не понимаю как сделать счетчик на Atmega8. Вроде как исправил что вы мне подсказали все равно не работает. Даже состояние порта не меняется. Я еще проведу текст своей программы. Вроде как порт которому мы подключаем кнопку мы настраиваем на вход. Схема проста между PORTB.0 и землей подключенная кнопка и к ней последовательно подключен резистор.

    #include // подключаем библиотеку содержающие информацию у микроконтреллере Atmega8
    #include // библиотечная функция содержающию задержку
    #asm // ассемблерная команда
    .equ __lcd_port=0x12 ;PORTD // адрес порта которому подключаем LCD дисплей
    #endasm // конец асемлеблерной команды
    #include // бибдиотечная функция жк дисплея
    char lcd_buf[17]; // массив для вывода текстовой информации
    void main(void) // главный цикл
    {
    int set=0; //переменная которая хранить и подсчитавать приходящие на PORTB.0 импульсы принажитие на кнопку переменная увеличивается на +1

    PORTB=0x00; // настройка PORTВ. между землю и PORTB.0 подсоедена кнопка и последовательно к этой кнопки соеденнен резистор на один 1 килоом
    //так нулевой разряд PORTB у нас должен насторен на вход, то мы должны записать 0.
    DDRB=0x00; // регистр который отвечать за направленние данных, то есть в него записаем ноль.

    PORTD=0x00;// к этому порту подключаем lcd Wh1602b
    DDRD=0x00;
    lcd_init(16);//
    while (1) // бесконечное цикл

    if(PINB.0==0); // это цикл который будет выполняется когда на PORTB.0 будет нажата кнопка, то изначально у нас PORTB у нас логическая единица
    // принажитие на кнопку состояние порта должно изменится на протиположное, то есть равен потенциалу земли.
    {
    PORTB.0=0;//

    delay_ms(50);// задержка от дребезгов контактов
    set++; // инкремент который увеличивать наше перемменое set в ней при каждой нажатие увеличиваеся на n+1
    }
    else // оператор протиположный else
    {
    PORTB=1;
    }
    sprintf(lcd_buf,»ustanovka\n%d»,set ); // функция для вывода по строчно информацию на дисплей
    lcd_puts(lcd_buf);// функция вывода на на LCD дисплей
    delay_ms(1000);// задержкжка через одно секунду будет обновлена информация на lcd экране
    lcd_clear();// очистка экрана

    }

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


    void main(void) // главный цикл
    {
    int set=0;
    // Input/Output Ports initialization
    // Port B initialization
    // Func7=In Func6=In Func5=In Func4=In Func3=In Func2=In Func1=In Func0=In
    // State7=T State6=T State5=T State4=T State3=T State2=T State1=T State0=P
    PORTB=0x01;
    DDRB=0x00;

    PORTD=0x00;// к этому порту подключаем lcd Wh1602b
    DDRD=0x00;
    lcd_init(16);//

    while (1)
    {

    if(PINB.0==0)
    {
    set++;
    delay_ms(50);
    sprintf(lcd_buf,"ustanovka\n%d",set );
    lcd_clear();
    lcd_puts(lcd_buf);
    }

    }

  • Спасибо за помощь. У меня все равно не работает. Собрал в Proteus схему и скопировал вас код.
    При нажатие на кнопки в PORTB.0 вместо синего цвета (уровень логический единица)
    желтый цвет. После после остановки симуляции Proteus пишет вот такое сообщение: Logic contention(s) detected on net GRD; и Logic contention(s) detected on #0000. Пожалуйста подскажи, кроме вас некому помочь.

  • Спасибо большое вам. Как мне вас отблагодарить? Сразу задам вопрос как вы это сделали это был так называемый флаг? Попробую сделать вторую кнопку которая уменьшала бы переменную.

  • hard_electron, небольшое уточнение касательно протеуса. Высокий уровень (уровень логиченой 1) — красный цвет. Низкий уровень (лог. 0) — синий цвет. Желтый цвет — это сигнал о том, что в данной точке схемы конфликт уровней. Например — порт МК настроен на выход с подтяжкой к питанию (должен быть красный квадратик), но к тому же порту подключено какое-то устройство, которое на эти пины выдает лог. 0. Т.е. получаеться, что в одной точке схемы одновременно присутствую два противоположных лог. уровня. Вот протеус и сообщает об этом желтым квадратиком и соответствующим сообщением в логе.

  • У меня вопрос.
    А как опросить весь порт сразу? Я думал так:
    i=PORTD;
    и решил вывести данные сразу же на другой порт :
    PORTB=i;
    не получилось. подскажите что не так!!!
    CVAVR

  • i = PIND;
    PORTB = i;

  • I=PIND;
    PORTB=I;

    DDRx настраивает как работает порт 1 выход 0 вход
    PORTx выводит данные
    PINx читает данные

  • Спасибо огромное! Очень хотел Вас попросить привести пример программы для работы iButton с сохранением ключа в eeprom. Очень надеюсь на Вашу помощь. За ранние спасибо!

  • А iButton работает по тому же принципу что и DS18B20 не сильно отличается. Берите документацию на батон и пример на термодатчик.

  • Я так и делал. С термодатчиком все получилось. А вот с iBittom не все. Не смог его данные в eeprom записать. А потом естественно и сравнить. Вот у специалистов пример и спрашиваю посмотреть как это делается. Если не трудно покажите пожалуйста.

  • Стоп. Вот тут по подробнее. Не смогли его номер записать в EEPROM? Может проблема не в Батоне, а в записи в EEPROM?

  • Советую внимательно и вдумчиво изучить сей трактат, дабы в комментах не распинаться снова. http://aterlux.ru/index.php?page=article&art=1wire

  • У меня получилось вот так:
    Я считываю ключи, записываю в память, сравниваю. Но есть несколько вопросов!!!
    За основу взял чью-то прогу и переделал, вопросы в тексте программы! Заранее спасибо!

    #include
    #include
    #include

    #asm
    .equ __w1_port=0x18; PORTB
    .equ __w1_bit=0
    #endasm
    #include
    #asm
    .equ __lcd_port=0x12;PORTD
    #endasm
    #include
    #define DS1990_FAMILY_CODE 1
    #define MAX_DEVICES 8
    unsigned char lcd_buffer[33];
    unsigned char rom_code[8];
    unsigned char f1,f2,f3;
    eeprom unsigned char code_eep1,code_eep2,code_eep3;

    void main (void)
    {
    unsigned char devices,a,b,c;
    PORTB=0x00;
    DDRB =0x00;

    PORTC=0x03;
    DDRC =0x00;

    PORTD=0x00;
    DDRD =0x00;
    lcd_init(16);
    w1_init();

    while(1)
    {

    f1=code_eep1;
    f2=code_eep2;
    f3=code_eep3;

    delay_ms(50);
    devices=w1_search(0xF0,rom_code);

    a=rom_code[1]; //?
    b=rom_code[2]; //? Зачем их бить на три? Можно ли считать в одну переменную?
    c=rom_code[3]; //? если можно, то как?

    sprintf(lcd_buffer,»Detected:%X\nCODE: %02X%02X%02X»,devices,a,b,c);
    lcd_clear();
    lcd_gotoxy(0,0);
    lcd_puts(lcd_buffer);
    delay_ms(50);

    if((a==f1)&&(b==f2)&&(c==f3)){PORTC.5=1;} else {PORTC.5=0;}

    if(PINC.0==0)
    {
    int i;

    //devices=w1_search(0xF0,rom_code);

    //a=rom_code[1];
    // b=rom_code[2];
    // c=rom_code[3];

    sprintf(lcd_buffer,»EEPROM:%X\nCODE: %02X%02X%02X»,devices,a,b,c);
    lcd_clear();
    lcd_gotoxy(0,0);
    lcd_puts(lcd_buffer);
    delay_ms(50);
    for (i=0;i<4;i++)
    {
    PORTC.3=1;
    delay_ms(500);
    PORTC.3=0;
    delay_ms(500);
    };
    code_eep1=a;
    code_eep2=b;
    code_eep3=c;

    }
    };

    }

  • Я сильно сомневаюсь что массив rom_code с типом unsigned char. Идентификатор равен 6 байтам и он просто не влезет в три однобайтовых ячейки массива. Либо Вы что-то перепутали. Скорее всего нужно использовать 6 ячеек массива или 3 ячейками но уже с типом int, даже скорее long. А почему нельзя в одну переменную. А как будет называться тип переменной длинной в 48 бит. )))

  • То есть у меня сейчас считывается и сохраняется только часть кода?

  • Вот функция devices=w1_search(0xF0,rom_code);. Как я понимаю она в переменную devices вернет состояние работы функции, а в массив rom_code номер ключа. Почему массив на 8 ячеек я не пойму. Достаточно 7. 6 для номера и 1 для нуля. Если кинуть после функции этот массив в терминал, что выдаст?

  • Поковырялся, почитал. Батон имеет 64 битный адрес. Функция devices=w1_search(0xF0,rom_code); возвращает в переменную devives количество найденных устройств, а в rom_code будет лежать адрес найденного устройства. Если устройств нет, то вернет 0.

  • Все, понял. Вот где косяк. rom_code состоит из 8 байт. 1-й CRC, 2…7 уникальный адрес, 8-й семейство (для ds1990 будет 01) Так что для сохранения адреса и его проверке, нужно читать 2,3,4,5,6,7 байты, это и есть его адрес. То есть rom_code[1…7].

  • Спасибо! с этим вроде понятно. Алексей хотел еще понять как правильно работать с eeprom? к примеру:

    eeprom usigned int eep_data1,eep_data2;
    usigned int x,y;

    if(PIND.0==0) {x=1234;}
    if(PIND.1==0) {y=4321;}

    eep_data1=x;
    eep_data2=y;

    а считываю так:

    x=eep_data1
    y=eep_data2

    У меня получается что сохраняю данных «x», а потом «y». При считывание считываются
    только «y». Такое ощущение что они поверх записались данных «x». Как eeprom оба данных записать?

  • Отладка на живом МК идет? Просто если такую программу залить в МК, то через 5 минут умрёт eeprom.

  • А можно прикрутить к этой схеме например если подается +5в(лог 1) , то if(PINB.1==1)

    то порт 2 например мигает светодиодом {
    PORTB.2=1;
    delay_ms(100);
    PORTB.2=0;
    delay_ms(100);
    } Но ножка уже подтянута к питанию внутренним резюком, подскажите пожалуйста как программно такое реализовать?

  • программно никак, но можно поставить внешний подтягивающий резистор к земле

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

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

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