Сенсорные кнопки удобны в работе. Нет подвижных деталей, герметичный корпус, технологичное изготовление любой клавиатуры. Мой опыт работы с встроенным контроллером сенсорных кнопок на STM32F051 показал, что сенсорные кнопки экономичны. На трёх кнопках можно одновременно сделать клавиатуру из трёх, пяти, шести кнопок, а также линейный или кольцевой слайдер. Стандартная библиотека TSL с её двумя килобайтами пожираемой памяти меня совсем не устроила, и я написал свою небольшую программу работы с контроллером сенсорных кнопок.
Пришло время работать с простым микропроцессором STM8S105, в котором нет такого контроллера, а к сенсорным кнопкам я привык.
Штатная библиотека сенсорных кнопок STM8_TSL_RC основана на принципе измерения времени заряда RC цепочки, состоящей из ёмкости сенсорного датчика и резистора подтяжки.
Для измерения ёмкости сенсора в пределах 5-20пФ требуется большая величина подтягивающего резистора. В некоторых конструкциях используется сопротивление до 1-5 мегаом. Кто внимательно прочитал даташит, тот понял, что при большом сопротивлении резистора подтяжки кнопка становится чувствительной к наводкам, и требуется организация «плавающего экрана» - а это дополнительный вывод процессора. К тому же, такой метод измерения ёмкости требует высокой тактовой частоты процессора для того, чтобы точно подсчитать длительность цикла заряда RC цепочки. При низкой тактовой частоте этот способ вообще не работает.
Метод измерения ёмкости сенсорного датчика с переносом заряда не зависит от скорости работы процессора, так как измеряемой величиной является не время заряда RC цепочки, а количество циклов накачки накопительного конденсатора. Разрешающая способность при таком методе измерения зависит только от отношения ёмкости накопительного конденсатора к ёмкости сенсорного датчика. Значит, при соответствующем выборе величины накопительной ёмкости мы сможем измерять очень незначительные колебания ёмкости датчика. А это позволит использовать датчики меньшего размера и сделать их более чувствительными. У меня получилась корректная работа с сенсорными кнопками на расстоянии до 4-х миллиметров. Иными словами, сенсорная клавиатура на обратной стороне 2-х миллиметрового стеклотекстолита или оргстекла работает с большим запасом.
Как реализована работа сенсорных датчиков?
Мой вариант сенсорного датчика с переносом заряда требует два вывода микропроцессора на одну кнопку. Это не больше, чем вариант с активным экраном. На сенсоре из трёх датчиков с шестью используемыми выводами можно получить шесть кнопок, а это уже совершенно приемлемо по соотношению количества выводов микропроцессора на одну виртуальную кнопку.
Измерительная схема:
На рисунке схематично показано два вывода микропроцессора. Выходные МОП транзисторы нарисованы как выключатели с двумя состояниями. Вывод А используется и для управления, и для оценки напряжения зарядки накопительного конденсатора C1. Вывод B используется только как ключ, замыкающийся на массу, поэтому в его качестве можно использовать вывод с открытым стоком (в STM8S их два).
Принцип измерения. Весь цикл можно разделить на шесть фаз.
Фаза 1. Подготовка к измерению. Разрядка конденсатора.
Оба вывода замкнуты на массу. Конденсатор C1 разряжается до нуля.Фаза 2. Пауза. Размыкание ключей.
Этот момент нужен для подготовки к зарядке ёмкостного датчика. Оба вывода переведены в разомкнутое состояние.
Фаза 3. Зарядка сенсора.
В этот момент ёмкостной датчик Cx заряжается до напряжения питания через вывод порта A. Поскольку порт B находится в разомкнутом состоянии, напряжение на накопительном конденсаторе C1 не изменяется.
Фаза 4. Пауза.
Снова размыкаем выводы обоих портов. Разница между этим моментом и фазой 2 в том, что конденсатор ёмкостного датчика заряжен до напряжения питания.
Фаза 5. Зарядка накопительного конденсатора.
В этот момент накопительный конденсатор подключается параллельно емкостному датчику. Заряд ёмкостного датчика распределяется между конденсатором и датчиком. Напряжение, которое получится после зарядки накопительного конденсатора, зависит от отношения ёмкости датчика и конденсатора. В моём случае ёмкость конденсатора примерно в 200 раз больше, чем ёмкость датчика. Значит, полученное напряжение в 200 раз меньше напряжения питания микроконтроллера. Этого недостаточно для измерения. Но самое главное, это напряжение прямо зависит от ёмкости самого сенсорного датчика!
Фаза 6. Измерение напряжения.
В этот момент не происходит переключение портов, просто анализируется уровень напряжения на ножке A. Если уровень меньше порогового, цикл повторяется с фазы 2 до тех пор, пока напряжение на выводе А не достигнет порога переключения логического входа.
Результатом измерения является количество циклов.
На временном графике показаны все фазы и напряжения на выводах микроконтроллера:
А вот диаграмма напряжений на выводе A, снятая осциллографом, в большем временном отрезке:
Здесь видно количество циклов измерения и постепенное увеличение напряжения на накопительном конденсаторе.
Программа управления написана на Си. С небольшими правками её можно перенести на любую платформу и любой микроконтроллер.
Управлять выводами контроллера просто, но есть одна особенность: в STM8S есть такое сочетание управляющих регистров, которое вызывает прерывание по порту. Пришлось внимательно следить за последовательностью переключения регистров, чтобы избежать этой ситуации.
Для независимой работы от остальной части программы я применил конечный автомат. Вот текст программы драйвера кнопки:
/******************************************************************************
* процедура измерения ёмкости кнопки.
* Вход: сообщение (M_BUTTON_START)
* Выход - функция оценки кнопки t_button ()
******************************************************************************/
void touch_proc (void)
{
typedef enum touch_modes
{
T_INIT = 0,
T_COUNT,
T_ESTIMATION,
T_WAIT
} touch_modes;
static touch_modes mode = T_INIT; // режим работы над кнопкой
static uint8_t cap = 0;
switch (mode)
{
case T_COUNT:
do
{
// состоит из 5-ти фаз
// фаза 1 - пауза, все входы высокоомные
SENS1C_PORT = 1;// освободили пины конденсатора
SENS1_DDR = 1; // перевели ножку из входа в выход OD slow mode с уровнем 1
SENS1_CR2 = 1; // перевели ножку в выход OD fast mode с уровнем 1
// фаза 2 - накачка конденсатора сенсорной панели
SENS1_CR1 = 1; // перевели ножку в режим PP fast mode, заряд конденсатора высоким уровнем
nop();
nop();
SENS1_CR1 = 0; // пины перевели в OD fast mode
SENS1_CR2 = 0; // пины перевели в OD slow mode
// фаза 3 - переключение ножки на вход
SENS1_DDR = 0; // пины выхода перевели на вход
// фаза 4 - перенос заряда на накопительный конденсатор
SENS1C_PORT = 0; // притянули конденсаторы к земле
// фаза 5 - проверим уровень напряжения на входе измерительной ножки
cap++;
if (cap >= TS_COUNT_MAX)// счётчик переполнен
{
break; // Выход по переполнению счётчика
}
}
while (!SENS1_PIN);
mode = T_ESTIMATION; // завершить измерение, переход к оценке
break;
case T_ESTIMATION:
// разрядка конденсаторов
SENS1_DDR = 1;
SENS1C_DDR = 1; // выходы в OD slow mode
SENS1_CR1 = 1; // SENS_PINx - PP,
SENS1C_CR1 = 0; // SENS_PINxC - OD
SENS1_CR2 = 1; // SENS_PINx - fast mode
SENS1_PORT = 0;
SENS1C_PORT = 0; // Ноль
// функция оценки кнопки
t_button (cap);
mode = T_WAIT;
break;
case T_WAIT:
if (get_message (M_BUTTON_START))
{
mode = T_INIT;
}
break;
case T_INIT:
default:
cap = 0;
// подготовка ножек к измерению
SENS1_CR1 = 0; // пин входа перевели в OD fast mode
SENS1_CR2 = 0; // пин входа перевели в OD slow mode
SENS1_DDR = 0; // пин выхода перевели на вход
SENS1_PORT = 1; // подали 1 на выходной регистр
mode = T_COUNT;
break;
}
}
Программа периодически вызывается в теле основного цикла программы. Как только получили сообщение от таймера о запуске цикла измерения, программа отработает цикл измерения ёмкости и запустит анализ кнопки. Это отдельная программа и её работа не будет описана в этой статье.
Для правильной компиляции нужно объявить ножки процессора с нужными присваиваниями. Например:
#define SENS_PORT1 GPIOA // порт сенсорного контакта первой кнопки
#define SENS_PIN1 3 // пин сенсорного контакта первой кнопки
#define SENS1_PORT PORTA3
#define SENS1_PIN PINA3
#define SENS1_DDR DDRA3
#define SENS1_CR1 CR1A3
#define SENS1_CR2 CR2A3
#define SENS_PORT1C GPIOB // порт конденсатора первой кнопки
#define SENS_PIN1C 4 // пин конденсатора первой кнопки
#define SENS1C_PORT PORTB4
#define SENS1C_PIN PINB4
#define SENS1C_DDR DDRB4
#define SENS1C_CR1 CR1B4
#define SENS1C_CR2 CR2B4
И ещё. Для ножек, у которых альтернативной функцией определен вход АЦП, можно отключить входной триггер Шмидта для устранения его влияния на заряд конденсатора.
Конструкция ёмкостного датчика может быть различной. Главное учитывать то обстоятельство, что измеряется ёмкость между сенсорным датчиком и общим проводом. Это значит, что на передней панели недалеко от датчика должен быть полигон земли, чтобы касание пальца вносило дополнительную ёмкость между полигоном датчика и полигоном общего провода. А то некоторые жалуются на плохую работу сенсоров при питании от батарей, а про общий провод забыли. Если у самого устройства ёмкость общего провода маленькая относительно земли, то откуда взяться большой ёмкости при касанию к датчику?
Как настраивать такую систему? Ёмкость накопительного конденсатора выбирается такой, чтобы не происходило переполнения счётчика при наименьшей ёмкости датчика, но была достаточна для распознавания даже лёгких касаний. Не забудьте, что число циклов обратно пропорционально ёмкости датчика – при касании число циклов уменьшается.
Таким нехитрым способом возможно уловить изменение ёмкости на 0,05пФ, что недостижимо для метода RC цепочки. Также, этот метод устойчив к помехам и наводкам. Объединение трёх датчиков позволило сделать линейный и кольцевой слайдер с очень хорошей чувствительностью и разрешающей способностью.
Пришло время работать с простым микропроцессором STM8S105, в котором нет такого контроллера, а к сенсорным кнопкам я привык.
Штатная библиотека сенсорных кнопок STM8_TSL_RC основана на принципе измерения времени заряда RC цепочки, состоящей из ёмкости сенсорного датчика и резистора подтяжки.
Для измерения ёмкости сенсора в пределах 5-20пФ требуется большая величина подтягивающего резистора. В некоторых конструкциях используется сопротивление до 1-5 мегаом. Кто внимательно прочитал даташит, тот понял, что при большом сопротивлении резистора подтяжки кнопка становится чувствительной к наводкам, и требуется организация «плавающего экрана» - а это дополнительный вывод процессора. К тому же, такой метод измерения ёмкости требует высокой тактовой частоты процессора для того, чтобы точно подсчитать длительность цикла заряда RC цепочки. При низкой тактовой частоте этот способ вообще не работает.
Метод измерения ёмкости сенсорного датчика с переносом заряда не зависит от скорости работы процессора, так как измеряемой величиной является не время заряда RC цепочки, а количество циклов накачки накопительного конденсатора. Разрешающая способность при таком методе измерения зависит только от отношения ёмкости накопительного конденсатора к ёмкости сенсорного датчика. Значит, при соответствующем выборе величины накопительной ёмкости мы сможем измерять очень незначительные колебания ёмкости датчика. А это позволит использовать датчики меньшего размера и сделать их более чувствительными. У меня получилась корректная работа с сенсорными кнопками на расстоянии до 4-х миллиметров. Иными словами, сенсорная клавиатура на обратной стороне 2-х миллиметрового стеклотекстолита или оргстекла работает с большим запасом.
Как реализована работа сенсорных датчиков?
Мой вариант сенсорного датчика с переносом заряда требует два вывода микропроцессора на одну кнопку. Это не больше, чем вариант с активным экраном. На сенсоре из трёх датчиков с шестью используемыми выводами можно получить шесть кнопок, а это уже совершенно приемлемо по соотношению количества выводов микропроцессора на одну виртуальную кнопку.
Измерительная схема:
На рисунке схематично показано два вывода микропроцессора. Выходные МОП транзисторы нарисованы как выключатели с двумя состояниями. Вывод А используется и для управления, и для оценки напряжения зарядки накопительного конденсатора C1. Вывод B используется только как ключ, замыкающийся на массу, поэтому в его качестве можно использовать вывод с открытым стоком (в STM8S их два).
Принцип измерения. Весь цикл можно разделить на шесть фаз.
Фаза 1. Подготовка к измерению. Разрядка конденсатора.
Оба вывода замкнуты на массу. Конденсатор C1 разряжается до нуля.Фаза 2. Пауза. Размыкание ключей.
Этот момент нужен для подготовки к зарядке ёмкостного датчика. Оба вывода переведены в разомкнутое состояние.
Фаза 3. Зарядка сенсора.
В этот момент ёмкостной датчик Cx заряжается до напряжения питания через вывод порта A. Поскольку порт B находится в разомкнутом состоянии, напряжение на накопительном конденсаторе C1 не изменяется.
Фаза 4. Пауза.
Снова размыкаем выводы обоих портов. Разница между этим моментом и фазой 2 в том, что конденсатор ёмкостного датчика заряжен до напряжения питания.
Фаза 5. Зарядка накопительного конденсатора.
В этот момент накопительный конденсатор подключается параллельно емкостному датчику. Заряд ёмкостного датчика распределяется между конденсатором и датчиком. Напряжение, которое получится после зарядки накопительного конденсатора, зависит от отношения ёмкости датчика и конденсатора. В моём случае ёмкость конденсатора примерно в 200 раз больше, чем ёмкость датчика. Значит, полученное напряжение в 200 раз меньше напряжения питания микроконтроллера. Этого недостаточно для измерения. Но самое главное, это напряжение прямо зависит от ёмкости самого сенсорного датчика!
Фаза 6. Измерение напряжения.
В этот момент не происходит переключение портов, просто анализируется уровень напряжения на ножке A. Если уровень меньше порогового, цикл повторяется с фазы 2 до тех пор, пока напряжение на выводе А не достигнет порога переключения логического входа.
Результатом измерения является количество циклов.
На временном графике показаны все фазы и напряжения на выводах микроконтроллера:
А вот диаграмма напряжений на выводе A, снятая осциллографом, в большем временном отрезке:
Здесь видно количество циклов измерения и постепенное увеличение напряжения на накопительном конденсаторе.
Программа управления написана на Си. С небольшими правками её можно перенести на любую платформу и любой микроконтроллер.
Управлять выводами контроллера просто, но есть одна особенность: в STM8S есть такое сочетание управляющих регистров, которое вызывает прерывание по порту. Пришлось внимательно следить за последовательностью переключения регистров, чтобы избежать этой ситуации.
Для независимой работы от остальной части программы я применил конечный автомат. Вот текст программы драйвера кнопки:
/******************************************************************************
* процедура измерения ёмкости кнопки.
* Вход: сообщение (M_BUTTON_START)
* Выход - функция оценки кнопки t_button ()
******************************************************************************/
void touch_proc (void)
{
typedef enum touch_modes
{
T_INIT = 0,
T_COUNT,
T_ESTIMATION,
T_WAIT
} touch_modes;
static touch_modes mode = T_INIT; // режим работы над кнопкой
static uint8_t cap = 0;
switch (mode)
{
case T_COUNT:
do
{
// состоит из 5-ти фаз
// фаза 1 - пауза, все входы высокоомные
SENS1C_PORT = 1;// освободили пины конденсатора
SENS1_DDR = 1; // перевели ножку из входа в выход OD slow mode с уровнем 1
SENS1_CR2 = 1; // перевели ножку в выход OD fast mode с уровнем 1
// фаза 2 - накачка конденсатора сенсорной панели
SENS1_CR1 = 1; // перевели ножку в режим PP fast mode, заряд конденсатора высоким уровнем
nop();
nop();
SENS1_CR1 = 0; // пины перевели в OD fast mode
SENS1_CR2 = 0; // пины перевели в OD slow mode
// фаза 3 - переключение ножки на вход
SENS1_DDR = 0; // пины выхода перевели на вход
// фаза 4 - перенос заряда на накопительный конденсатор
SENS1C_PORT = 0; // притянули конденсаторы к земле
// фаза 5 - проверим уровень напряжения на входе измерительной ножки
cap++;
if (cap >= TS_COUNT_MAX)// счётчик переполнен
{
break; // Выход по переполнению счётчика
}
}
while (!SENS1_PIN);
mode = T_ESTIMATION; // завершить измерение, переход к оценке
break;
case T_ESTIMATION:
// разрядка конденсаторов
SENS1_DDR = 1;
SENS1C_DDR = 1; // выходы в OD slow mode
SENS1_CR1 = 1; // SENS_PINx - PP,
SENS1C_CR1 = 0; // SENS_PINxC - OD
SENS1_CR2 = 1; // SENS_PINx - fast mode
SENS1_PORT = 0;
SENS1C_PORT = 0; // Ноль
// функция оценки кнопки
t_button (cap);
mode = T_WAIT;
break;
case T_WAIT:
if (get_message (M_BUTTON_START))
{
mode = T_INIT;
}
break;
case T_INIT:
default:
cap = 0;
// подготовка ножек к измерению
SENS1_CR1 = 0; // пин входа перевели в OD fast mode
SENS1_CR2 = 0; // пин входа перевели в OD slow mode
SENS1_DDR = 0; // пин выхода перевели на вход
SENS1_PORT = 1; // подали 1 на выходной регистр
mode = T_COUNT;
break;
}
}
Программа периодически вызывается в теле основного цикла программы. Как только получили сообщение от таймера о запуске цикла измерения, программа отработает цикл измерения ёмкости и запустит анализ кнопки. Это отдельная программа и её работа не будет описана в этой статье.
Для правильной компиляции нужно объявить ножки процессора с нужными присваиваниями. Например:
#define SENS_PORT1 GPIOA // порт сенсорного контакта первой кнопки
#define SENS_PIN1 3 // пин сенсорного контакта первой кнопки
#define SENS1_PORT PORTA3
#define SENS1_PIN PINA3
#define SENS1_DDR DDRA3
#define SENS1_CR1 CR1A3
#define SENS1_CR2 CR2A3
#define SENS_PORT1C GPIOB // порт конденсатора первой кнопки
#define SENS_PIN1C 4 // пин конденсатора первой кнопки
#define SENS1C_PORT PORTB4
#define SENS1C_PIN PINB4
#define SENS1C_DDR DDRB4
#define SENS1C_CR1 CR1B4
#define SENS1C_CR2 CR2B4
И ещё. Для ножек, у которых альтернативной функцией определен вход АЦП, можно отключить входной триггер Шмидта для устранения его влияния на заряд конденсатора.
Конструкция ёмкостного датчика может быть различной. Главное учитывать то обстоятельство, что измеряется ёмкость между сенсорным датчиком и общим проводом. Это значит, что на передней панели недалеко от датчика должен быть полигон земли, чтобы касание пальца вносило дополнительную ёмкость между полигоном датчика и полигоном общего провода. А то некоторые жалуются на плохую работу сенсоров при питании от батарей, а про общий провод забыли. Если у самого устройства ёмкость общего провода маленькая относительно земли, то откуда взяться большой ёмкости при касанию к датчику?
Как настраивать такую систему? Ёмкость накопительного конденсатора выбирается такой, чтобы не происходило переполнения счётчика при наименьшей ёмкости датчика, но была достаточна для распознавания даже лёгких касаний. Не забудьте, что число циклов обратно пропорционально ёмкости датчика – при касании число циклов уменьшается.
Таким нехитрым способом возможно уловить изменение ёмкости на 0,05пФ, что недостижимо для метода RC цепочки. Также, этот метод устойчив к помехам и наводкам. Объединение трёх датчиков позволило сделать линейный и кольцевой слайдер с очень хорошей чувствительностью и разрешающей способностью.









Спасибо Илья, интересная статья о сенсорных кнопках. Вы не пробовали делать это на stm32f303 с встроенным контроллером TCS? В сети толковых примеров нет, все ссылки на ихнюю либу. Интересно было запустить сенсорные кнопки на встроенном контроллере.
ОтветитьУдалитьЯ запускал контроллер сенсорных кнопок на STM32F051. В принципе, там всё нужно делать по даташиту и по описаниям регистров. Значения ёмкости кнопок получались сравнимые (600-500 циклов). А вот обработка этих показаний - это хороший кусок работы. Пока я не хочу выкладывать свою библиотеку в открытый доступ.
ОтветитьУдалитьХорошо, возьметесь за это дело за вознаграждение?
ОтветитьУдалитьЭтот комментарий был удален автором.
ОтветитьУдалитьок. завтра напишу.
ОтветитьУдалитьотписал.
ОтветитьУдалить