Начальный код мы разобрали, самое время приступить ко второй части нашей задачи - распознаванию нажатой кнопки. Разберемся, какие напряжения будут на входе АЦП при нажатии кнопок, и какой цифровой код получится после аналого-цифрового преобразования.
При нажатии S1 напряжение на входе АЦП будет равно 5 В· 2 КОм/(2 + 2 + 2 + 2) КОм = 1,25 В
Для S2 - 5 · 4 /8 = 2,5 В
Для S3 – 5 · 6 /8 = 3,75 В
Для S4 – 5 В.
(Резистор R7 почти не влияет на наши расчеты, поэтому им можно пренебречь.)
Перевести напряжение на входе АЦП в цифровой код, можно по формуле
где n – разрядность АЦП, Uin – входное напряжение, Uref – напряжение опорного источника
АЦП AVRа 10-ти разрядный, но мы используем только 8 старших разрядов. Напряжение опорного источника равно 5 В.
((28-1) · 1,25)/ 5 = (255· 1,25) / 5 = 64
(255· 2,5) / 5 = 128
(255· 3,75) / 5 = 191
(255· 5) / 5 = 255
Теперь можно свести расчеты в табличку, а заодно и выбрать пороговые значения для нашей программы.
АЦП микроконтроллера AVR
//программирование микроконтроллеров AVR на Си - осваиваем АЦП
#include <ioavr.h>
#include <intrinsics.h>
#define StartConvAdc() ADCSRA |= (1<<ADSC)
#define KEY_NULL 0
#define KEY_S1 1
#define KEY_S2 2
#define KEY_S3 3
#define KEY_S4 4
//кнопочный буфер
volatile unsigned char KeyBuf = 0;
int main(void)
{
unsigned char tmp;
//настраиваем порты
DDRC = 0xff;
PORTC = 0xff;
//инициализируем АЦП
//ион - напряжение питания, выравнивание влево, нулевой канал
ADMUX = (0<<REFS1)|(1<<REFS0)|(1<<ADLAR)|(0<<MUX3)|(0<<MUX2)|(0<<MUX1)|(0<<MUX0);
//вкл. ацп, режим одиночного преобр., разрешение прер., частота преобр. = FCPU/128
ADCSRA = (1<<ADEN)|(1<<ADSC)|(0<<ADATE)|(1<<ADIE)|(1<<ADPS2)|(1<<ADPS1)|(1<<ADPS0);
//разрешаем прерывания и запускаем преобразование
__enable_interrupt();
StartConvAdc();
//основной цикл программы - опрос кнопочного буфера
while(1)
{
tmp = KeyBuf;
if (tmp)
{
tmp--;
PORTC = ~(1<<tmp);
}
else
PORTC = 0xff;
}
return 0;
}
#pragma vector=ADC_vect
__interrupt void adc_my(void)
{
//считываем старший регистр АЦП
unsigned char AdcBuf = ADCH;
//опеределяем в какой диапазон попадает его значение
if (AdcBuf > 240)
KeyBuf = KEY_S4;
else if (AdcBuf > 180)
KeyBuf = KEY_S3;
else if (AdcBuf > 120)
KeyBuf = KEY_S2;
else if (AdcBuf > 35)
KeyBuf = KEY_S1;
else
KeyBuf = KEY_NULL;
//запускаем преобразование и выходим
StartConvAdc();
}
Пояснения к коду
Номера кнопок
#define KEY_NULL 0
#define KEY_S1 1
#define KEY_S2 2
#define KEY_S3 3
#define KEY_S4 4
Переменная – кнопочный буфер
//кнопочный буфер
volatile unsigned char KeyBuf = 0;
Опрос буфера в основном коде программы
while(1)
{
tmp = KeyBuf;
if (tmp)
{
tmp--;
PORTC = ~(1<<tmp);
}
else
PORTC = 0xff;
}
Обработчик прерывания
KeyBuf = KEY_S4;
else
if (AdcBuf > 180)
KeyBuf = KEY_S3;
else
if (AdcBuf > 120)
KeyBuf = KEY_S2;
else
if (AdcBuf > 50)
KeyBuf = KEY_S1;
else
KeyBuf = KEY_NULL;
но согласитесь, что такая запись менее наглядна.
Устраняем ложные срабатывания
unsigned char comp = 0;
#pragma vector=ADC_vect
__interrupt void adc_my(void)
{
unsigned char AdcBuf;
unsigned char Key;
static unsigned char LastState;
//считываем старший регистр АЦП
AdcBuf = ADCH;
//проверяем в какой диапазон попадает его значение
if (AdcBuf > 240)
Key = KEY_S4;
else if (AdcBuf > 180)
Key = KEY_S3;
else if (AdcBuf > 120)
Key = KEY_S2;
else if (AdcBuf > 50)
Key = KEY_S1;
else
Key = KEY_NULL;
//если какая-нибудь из кнопка нажата
//сравниваем предыдущее и текущее состояние
//если совпадают – проверяем счетчик comp,
//если нет – обнуляем его
//кнопка считается нажатой, если она удерживается в течении 100
//преобразований АЦП
if (Key)
{
if (Key == LastState)
{
if (comp > 100)
KeyBuf = Key;
else
comp++;
}
else
{
LastState = Key;
comp = 0;
}
}
else
{
comp = 0;
KeyBuf = KEY_NULL;
LastState = KEY_NULL;
}
//запускаем преобразование и выходим
StartConvAdc();
}
Мы ввели три дополнительные переменные: comp, LastState и Key.
Итак, из этих двух статей вы узнали
- Основные характеристики АЦП- Как инициализировать и запустить АЦП
- Как перевести значение напряжения на входе АЦП в цифровой код
- Конструкция if (…) else if(…)
- Зачем нужен квалификатор volatile
- Как объявить статическую переменную и в чем ее особенность
- Алгоритм для борьбы с дребезгом кнопок
Файлы проекта - АЦП микроконтроллера AVR
Схема для нашего примера
Comments
Code:
unsigned char buttonPressed = (read_adc(0) + 25) / 51;
switch(buttonPressed)
{
case 0:
{
// ничего не выводим
}break;
case 1:
{
lcd_putsf("RIGHT");
}break;
case 2:
{
lcd_putsf("DOWN");
}break;
case 3:
{
lcd_putsf("LEFT");
}break;
case 4:
{
lcd_putsf("UP");
}break;
case 5:
{
lcd_putsf("OK");
}break;
}
цифровые коды при 5 кнопках - 51,102,153,204, 255
Эту строку надо вынести из обработчика прерывания, как и comp, так как область действия переменной - только функция обработчика прерывания и last state там никогда не сохранится.
Хочу выразить благодарность за статью, реально помогла понять принцип работы АЦП. Заюзал 8 кнопок на mega16. WinAVR+AvrStudi o.
Но у меня в WinAVR не заработало, пока не вынес наружу.
"Заметьте, что и Key и LastState не были инициализирован ы при объявлении. В них может находиться любое число, а вовсе не ноль, как иногда думают."
Это ошибка - статические неинициализиров анные переменные по стандарту языка C содержат на старте программы нулевые значения. И все известные мне компиляторы соблюдают это правило.
Или совмещают обнуление и инициализацию (из флеша) в чуть более сложную процедуру, которая готовит все сегменты по таблице.
Пример в яре - 'segment_init.c '. :-)
RSS feed for comments to this post