6. Как подключить кнопку к AVR микроконтроллеру

Вариантов подключения существует несколько. Например такой

Резистор 10к является подтяжкой. Хотя внутри МК уже подключена внутренняя (pull-up) подтяжка, но всегда обычно используется дополнительно внешняя, для лучшей помехоустойчивости от наводок и ложных срабатываний. Резистор 1к защитный. Это если вдруг вывод МК по ошибке будет настроен как включенный выход и при этом нажать кнопку, то вывод сгорит. Чтобы этого не произошло и используется резистор в 1к. Но опытные радиолюбители его не используют, но помнить об этом надо. Конденсатор гасит дребезг при нажатии кнопки. Дребезг контакта кстати неизбежен. В момент замыкания и размыкания кнопки возникает состояние, когда кнопка еще на замкнута но и не разомкнута. Это вызывает многочисленные ложные срабатывания входа МК. Чтобы уменьшить этот эффект дребезга и используют конденсатор. Помимо конденсатора так же еще используют и программные возможности борьбы с дребезгом. Можно так же использовать между кнопкой и МК триггер Шмитта, который тоже успешно справляется с задачей устранения дребезга. Итак кнопку подключили, теперь напишем код.

#include <avr/io.h>

//Битовые макросы
#define ClearBit(reg, bit)       reg &= (~(1<<(bit)))
#define SetBit(reg, bit)          reg |= (1<<(bit))
#define BitIsClear(reg, bit)    ((reg & (1<<(bit))) == 0)
#define BitIsSet(reg, bit)       ((reg & (1<<(bit))) != 0)

int main(void)
{
	DDRA = 0<<PA4;  //Вывод PA4 будет входом
	PORTA= 1<<PA4;  //Включаем подтяжку питания МК к входу PA4
	
    while(1)
    {
		if (BitIsClear(PINA,PA4))
		{
			//если кнопка нажата то МК будет 
			//обрабатывать код в этом блоке
		} 
		else
		{
			//а если отжата, то только в этом блоке
		}
		
    }
}

Для наглядности работы с опросом кнопок и подобных вещей мы ввели битовые макросы. Их использование очень сильно упрощает и ускоряет написание кода. Выражение if (BitIsClear(PINA,PA4)) как раз таки и проверяет состояние кнопки, нажата она или отжата. При соответсвтующем состоянии кнопки выполняется одна из секций
        {
            //эта
        }
        else
        {
            //или эта
        }

Но обычно в главном цикле программы не используют имена регистров портов. Как правило делают так.

#include <avr/io.h>

//Битовые макросы
#define ClearBit(reg, bit)       reg &= (~(1<<(bit)))
#define SetBit(reg, bit)          reg |= (1<<(bit))
#define BitIsClear(reg, bit)    ((reg & (1<<(bit))) == 0)
#define BitIsSet(reg, bit)       ((reg & (1<<(bit))) != 0)

#define BUTTON PA4
#define PORT_BUTTON PORTA
#define PIN_BUTTON PINA
#define DDR_BUTTON DDRA

int main(void)
{
	DDR_BUTTON = 0<<BUTTON;  //Вывод PA4 будет входом
	PORT_BUTTON= 1<<BUTTON;  //Включаем подтяжку питания МК к входу PA4
	
    while(1)
    {
		if (BitIsClear(PIN_BUTTON,BUTTON))
		{
			//если кнопка нажата то МК будет 
			//обрабатывать код в этом блоке
		} 
		else
		{
			//а если отжата, то только в этом блоке
		}
		
    }
}

Зачем это надо? Такой шаг позволяет перенести вашу программу на другой МК быстро, экономя время. Предположим в другом проекте с кнопкой надо подцепить кнопку на другой пин, тогда пришлось бы везде в коде программы искать где есть PINA и PA4, и переименовывать это в другие имена регистров и пинов. А код программы может быть очень большой и длинный и разбит на много файлов. Можно пропустить регистр PINA, и где то его переименовали, а где то пропустили. И тогда будет глюк в программе, причем компилятор вам ничего не кажет, так как с точки зрения синтаксиса ошибок нету. Поэтому в идеале сам код программы должен быть изолирован от железа МК. Это и достигается применением директивы #define. В итоге вам надо будет только переименовать регистры в одном месте программы вот здесь:

#define BUTTON PA4
#define PORT_BUTTON PORTA
#define PIN_BUTTON PINA
#define DDR_BUTTON DDRA