Управление с помощью компьютера исполнительным механизмом или иным устройством не всегда удобно. Первые два дня это, конечно, интересно, но потом начинает напрягать, потому что приходится совершать много лишних действий – включать компьютер, запускать программу и т.д. Возникает вопрос, а не изготовить ли для этих целей автономный управляющий модуль с клавиатурой и дисплеем?
Так как делается это для себя, то требуется дешевое, но в то же время красивое решение. С выбором дисплея особо не помудришь, дешевле и удобнее символьных 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