Начальный код мы разобрали, самое время приступить ко второй части нашей задачи - распознаванию нажатой кнопки. Разберемся, какие напряжения будут на входе АЦП при нажатии кнопок, и какой цифровой код получится после аналого-цифрового преобразования.
При нажатии 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