Сенсорные кнопки

     Управление с помощью компьютера  исполнительным механизмом или иным устройством не всегда удобно. Первые два дня это, конечно, интересно, но потом начинает напрягать, потому что приходится совершать много лишних действий – включать компьютер, запускать программу и т.д. Возникает вопрос, а  не изготовить ли для этих целей автономный управляющий модуль с клавиатурой и дисплеем?
   Так как делается это для себя, то требуется дешевое, но в то же время красивое решение. С выбором дисплея особо не помудришь, дешевле и удобнее символьных LCD с контроллером еще не придумали, а вот при выборе клавиатуры можно хорошенько сэкономить.
 Рассмотрим варианты. За готовую матричную клавиатуру придется выложить 100-200 рублей. Клавиатура из кнопок обойдется рублей в 36 (3р * 12 кнопок), не считая колпачков.   Сенсорную клавиатуру можно изготовить почти задаром из куска фольгированного текстолита, при этом в корпусе устройства не придется сверлить отверстия под кнопки. 
   По-моему выбор очевиден, осталось только понять, как с такой клавиатурой  работать. Но для начала нужно хотя бы разобраться с одиночной сенсорной кнопкой. 
   
  Одиночная сенсорная кнопка представляет собой металлическую площадку,  подключенную к выводу  микроконтроллера. Поскольку любое проводящее тело обладает емкостью, величина которой зависит от геометрии тела и окружающего его диэлектрика, такое подключение равносильно подключению к выводу конденсатора.    
   Человеческое тело тоже обладает емкостью (~100..300 pF), поэтому при поднесении пальца к сенсору суммарная емкость на входе микроконтроллера увеличится (ведь при параллельном соединении конденсаторов их емкости складываются). По именениям этой емкости и можно делать вывод о прикосновении к сенсору. 

Сенсорная кнопка, подключенная к микроконтроллеру

С1 — паразитная емкость вывода мк, С2 — емкость сенсора, С3 и С4 – емкости, вносимые прикосновением.  
  
   Поскольку все КМОП микросхемы болезненно переносят воздействие статического электричества, а человеческое тело ,обладая вышеуказанной емкостью, может заряжаться до нескольких тысяч вольт, схему следует дополнить небольшой защитной цепью. 
    Защита микроконтроллера от статического электричества
  
  При прикосновении к сенсору, накопленный телом заряд благополучно разрядится через один из диодов. В принципе такая защитная схема (не считая резистора) спрятана внутри микроконтроллера, но полагаться на нее не стоит, так как встроенные диоды выдерживают ток лишь в 1 ма.
   Кстати, внешние диоды добавят к входной емкости еще пару тройку пикофарад.

  Ну ладно, принцип понятен, а каким образом измерить емкость? Величину емкости можно оценить по времени ее заряда. Подключим к выводу микроконтроллера высокоомный подтягивающий резистор (смотри схему) и получим RC цепь. Манипулируя выводом микроконтроллера можно вызвать заряд и разряд нашего виртуального конденсатора, а контролирую потенциал вывода в процессе заряда емкости - оценивать ее величину. Чем больше емкость, тем медленнее она будет заряжаться.   

Вырисовывется следующий алгоритм работы:
 
1. Переключаем вывод микроконтроллера в режим выхода и сбрасываем в ноль. Паразитная емкость разрядится на внутренние цепи микроконтроллера.

2. Переключаем вывод микроконтроллера в режим входа в состоянии Hi-Z. Емкость начнет заряжаться через внешний подтягивающий резистор.

3. Контролируем состояние вывода и отсчитываем время. Как только емкость зарядится до напряжения логической единицы, снимаем показания счетчика.

4. Сравниваем полученное значение счетчика с заданной эталонной величиной и определяем, увеличилась ли емкость или нет. 
 
  Ничего сложного и можно перейти к делу.  Представляю сделанную на скорую руку, но, тем не менее, рабочую схему и исходники устройства.

Микроконтроллер и две сенсорные кнопки
 
Исходник не большой и, я думаю, main.c можно выложить полностью, он у меня хорошо прокомментирован.

//***************************************************************************
//
//  Author(s)...: Pashgan    http://ChipEnable.Ru, Petrov http://svyaz.blogspot.com/
//
//  Target(s)...: ATtiny2313
//
//  Compiler....: IAR EWA 5.11
//
//  Description.: ATtiny sensor
//
//  Data........: 19.01.11
//
//***************************************************************************
#include <ioavr.h>
#include <intrinsics.h>
#include "usart.h"
 
//макросы для работы с битами
#define InvBit(reg, bit)  reg ^= (1<<(bit))
#define ClearBit(reg, bit)       reg &= (~(1<<(bit)))
#define SetBit(reg, bit)          reg |= (1<<(bit))
#define BitIsSet(reg, bit)       ((reg & (1<<bit)) != 0)
 
//сколько циклов опроса кнопка должна удерживаться
#define THRESHOLD 20
// величина которую, возможно придется подстраивать
#define TOUCH_COUNT 1 
//переменные для подсчета количества переполнений Т0
volatile unsigned char sub_time_pin0 = 0;
volatile unsigned char sub_time_pin1 = 0;
//переменная для защиты от дребезга
unsigned char comp = 0;
 
int main( void )
  USART_Init();
 
  // Инициализация таймера T0 для Tiny2313
  TIMSK = (1<<OCIE0A);    //разрешаем прерывание таймера T0 при событии совпадение
  TCCR0A = (1<<WGM01)|(0<<WGM00);
  TCCR0B = (0<<CS02)|(1<<CS01)|(0<<CS00);  //режим СТС, прескалер - 8
  TCNT0 = 0;              //обнуляем счетный регистр
  OCR0A = 0x32;            //прерывания каждые ~ 50мкс
 
  // Инициализация таймера T1 для Tiny2313
  TIMSK |= (1<<OCIE1A);                 //разрешаем прерывание таймера т1 при событии совпадение
  TCCR1A = (0<<WGM11)|(0<<WGM10);
  TCCR1B = (0<<WGM13)|(1<<WGM12)|(0<<CS12)|(1<<CS11)|(0<<CS10);  //режим работы СТС, прескалер - 8
  TCNT1 = 0; //обнуляем счетный регистр
  OCR1A = 0x2710; //прерывания  каждые 10мс
 
  //разрешаем прерывания  
  __enable_interrupt();
  USART_SendStr("Button: "); 
  DDRD |=(1<<PD2)|(1<<PD3)|(1<<PD4); //устанавливаем  PD2, PD3 и PD4 на выход
  PORTD = 0;
 
//++++++++++++++++++++++++++++++++++++++++++++++++++
  while(1){
  /*если на выводе PB0 единица и таймер переполнился 
  несколько раз -> увеличить антидребезговую переменную
  затем, если значение переменной comp превысило порог ->
  инвертировать пин PD2, послать по УАРТУ номер кнопки,
  обнулить антидребезговую переменную  */
  if (BitIsSet(PINB, PB0)) { 
     if (sub_time_pin0 > TOUCH_COUNT) { 
        comp++;   
        if (comp >= THRESHOLD){ 
           InvBit(PORTD,PD2); 
           USART_SendStr("1 "); 
           comp = 0;
        }
     }
     sub_time_pin0 = 0;
   }
  /* то же самое */
  if (BitIsSet(PINB, PB1)) {
     if (sub_time_pin1 > TOUCH_COUNT) {
       comp++;
       if (comp >= THRESHOLD){
          InvBit(PORTD, PD3);
          USART_SendStr("2 "); 
          comp=0;
       }
     }
     sub_time_pin1 = 0;
  }
//++++++++++++++++++++++++++++++++++++++++++++++++++    
  }
  return 0;
}
 
// Прерывание Таймера 0 используемого для сканирования пина
#pragma vector = TIMER0_COMPA_vect
__interrupt void Timer0Comp(void)
{
// Увеличиваем на 1 счетчики переполнений таймера  
  sub_time_pin0++;
  sub_time_pin1++;
  
//это для мониторинга в Протеусе
  InvBit(PORTD,PD4);
}
 
// Прерывание Т1
//Инициализация порта, к которому подключены сенсорные кнопки согласно алгоритму
#pragma vector = TIMER1_COMPA_vect
__interrupt void Timer1CompA(void)
{
  //переключаем порт в режим выхода и устанавливаем 0
  //разряжая паразитную емкость на внутренние цепи AVR
  DDRB = 0xFF;
  PORTB = 0x00;
  
  //переключаем порт в режим входа в состоянии Hi-Z
  DDRB = 0x00;
 
  //обнуляем количество срабатываний для нулевого и первого выводов PORTB
  sub_time_pin0 = 0; 
  sub_time_pin1 = 0; 
  
  TCNT0 = 0; // Обнуляем счетный регистр T0

У вас недостаточно прав для комментирования.