STM32F TSC или ёмкостный датчик касаний.
Ссылка на видео в YouTube В некоторых STM32F( смотрите datasheet) имеется на борту TSC-сенсорный контроллер касаний, что сильно упрощает создание той же клавиатуры или кнопок для управления каким нибудь устройством. Из за того что во многих моделях контроллеров отсутствует TSC в SPL( стандартная периферийная библиотека) не были добавлены функции для работы с ним, но разработчики создали отдельную библиотеку которая очень объёмная и не понятная, поэтому будем использовать CMSIS.
Начнем изучение, с начало ознакомимся с принципиальной схемой модуля TSC потом расскажу как происходит сканирование сенсорных кнопок им.
Схема довольно простая состоит из следующих компонентов:
Clock prescler-делитель частоты тактирования модуля TSC;
Pulse generator-блок управления зарядом ёмкостного сенсора касания, разряда его в конденсатор накопитель и deadtime( закрыт верхний и нижний транзистор одновременно).
I/O control logic-блок управления непосредственно выводами TSC а также выбором рабочей группы;
Group counters-счетчики количества переданных зарядов из сенсора касания в конденсатор накопитель, для каждой группы свой.
И не много про группы, группы это не большие модули внутри TSC которые имеют свой счётчик и по четыре своих вывода из которых к одному( любому из четырех) должен обязательно подключен конденсатор накопитель а к остальным трём по желанию сенсоры касаний.
Процесс сканирования и порядок которого описан ниже.
Сам процесс довольно прост, сенсорный датчик касания Cx накапливает заряд до напряжения VDD контроллера, потом передаёт в конденсатор накопитель Cs и так будет делать циклически пока в Cs не зарядится до напряжения 1.3 вольт, после TSC отключается. Когда процесс сканирования закончен вы должны проверит счётчик циклических повторений заряда, если было касание то счётчик будет с меньшим результатом чем без касания.
Схема подключения и описание компонентов.
Схема подключения к STM32F051C8T6, к выводам группы 1.
Описание компонентов:
C1-конденсатор накопитель, должен быть в пределах ~47nF;
R1-R3-ограничители токов заряда сенсора касания, по документации должны быть от 100Om до 10kOm у меня 500Om, сопротивление должно обеспечить полный заряд и разряд сенсорного датчика касания, если будет слишком большим то циклы заряда будут увеличены что приведет к уменьшению точности сканирования;
C2-C4-сенсоры касания, площадь их должна соответствовать пятну касания пальца и с обратной стороны платы необходимо сделать под ними экран в виде сетки с 10% заполнением, так же подводящие проводники желательно делать максимально тонкими.
Описание регистров для настройки и работы с TSC.
Название регистров взято из файла stm32f0xx.h в скобках название соответствуют справочному руководству RM0091.
TSC управляющий регистр TSC->CR( TSC_CR).
CTPH[3:0]-время заряда ёмкостного датчика касания.
0000: 1x t PGCLK
0001: 2x t PGCLK
...
1111: 16x t PGCLK
CTPL[3:0]-время передачи заряда из ёмкостного датчика касания в конденсатор накопитель.
0000: 1x t PGCLK
0001: 2x t PGCLK
...
1111: 16x t PGCLK
SSD[6:0]-биты для установки компенсации времени заряда ёмкостного датчика касания, попросту к времени заряда
CTPH прибавляется SSD.
0000000: 1x t SSCLK
0000001: 2x t SSCLK
...
1111111: 128x t SSCLK
SSE-включение или отключение компенсации заряда ёмкостного датчика касания.
0 отключает
1 включает
SSPSC-предделитель компенсации заряда ёмкостного датчика касания.
0 fHCLK
1 fHCLK /2
PGPSC[2:0]-предделитель тактирования TSC.
000: f HCLK
001: f HCLK /2
010: f HCLK /4
011: f HCLK /8
100: f HCLK /16
101: f HCLK /32
110: f HCLK /64
111: f HCLK /128
MCV[2:0]-максимальное количество передач зарядов из ёмкостного датчика касания в конденсатор накопитель, после переполнения установленного значения сканирование останавливается и срабатывает флаг переполнения.
000: 255
001: 511
010: 1023
011: 2047
100: 4095
101: 8191
110: 16383
111: не используется
IODEF-состояния выводов когда не используются которые определены как ёмкостные датчики или конденсатор накопитель.
0: I/O вывод push-pull открыт на землю
1: I/O вывод input floating
SYNCPOL-условия при котором должен срабатывать вывод синхронизации.
0: при спаде с высокого уровня на низкий
1: при подъёме с низкого на высокий уровень и от высокого уровня на выводе
AM-бит определяет от чего будет осуществлять старт сканирования, от внешнего вывода или установкой бита в START.
0: установка бита в START
1: от внешнего вывода
START-запись в этот бит 1 запустит сканирование, если только в AM установлено 0.
TSCE-включает и отключает TSC модуль.
0: TSC не включен
1: TSC включен
TSC регистр TSC->IER( TSC_IER) включения прерываний.
MCEIE-включение прерывания от переполнения счетчика любой группы, если результат превысит значение установленное в MCV регистра TSC_CR.
0: выключает прерывание
1: включает прерывание
EOAIE-включает прерывание от окончания сканирования любой из групп, так как переполнение счетчика является окончанием сканирования тоже вызовет данное прерывание, исключить можно проверив флаг.
0: выключает прерывание
1: включает прерывание
TSC регистр TSC->ICR( TSC_ICR) сброса флагов прерываний.
MCEIC-сброс флага прерывания от переполнения счетчика.
0: запись не имеет эффекта
1: запись очищает флаг MCEF в регистре TSC_ISR
EOAIC-сброс флага прерывания от окончания сканирования.
0: запись не имеет эффекта
1: запись очищает флаг EOAF в регистре TSC_ISR
TSC регистр TSC->ISR( TSC_ISR) статуса прерываний.
MCEF-флаг переполнения счетчика.
0: переполнение счётчика не произошло
1: было переполнение счётчика
EOAF-флаг окончания сканирования.
0: сканирование не закончилось
1: сканирование закончилось или было переполнение счетчика
TSC I/O регистр TSC->IOHCR( TSC_IOHCR) управления триггером шмидта вывода, по умолчания в нём значение 0xFFFF FFFF( включены).
Gx_IOy-включение и отключение триггера шмидта, x-группа, y-номер вывода в группе.
0: Gx_IOy шмидт триггер отключён
1: Gx_IOy шмидт триггер включён
TSC I/O регистр TSC->IOASCR( TSC_IOASCR) подключение вывода к аналоговому модулю.
Gx_IOy-регистр подключает и отключает вывод( используется также для ADC) от аналогового модуля, x-группа, y-номер вывода в группе.
0: Gx_IOy не подключён к аналоговому модулю
1: Gx_IOy подключён к аналоговому модулю
TSC I/O регистр TSC->IOSCR( TSC_IOSCR) выбора вывода для конденсатора накопителя, у каждой группы может быть только один такой вывод.
Gx_IOy-включение и отключение конденсатора накопителя, x-группа, y-номер вывода в группе.
0: Gx_IOy вывод не используется
1: Gx_IOy вывод используется как конденсатор накопитель
TSC I/O регистр TSC->IOCCR( TSC_IOCCR) выбора вывода ёмкостного датчика для сканирования.
Gx_IOy-включение и отключение ёмкостного датчика касания, x-группа, y-номер вывода в группе.
0: Gx_IOy вывод не используется
1: Gx_IOy вывод используется как сенсорный датчик
TSC I/O регистр TSC->IOGCSR( TSC_IOGCSR) статуса группы и выбора в которой будет происходить сканирование.
GxS-статус группы, x-номер.
0: сканирование не запущено или не закончено
1: сканирование в этой группе закончилось
GxE-включение группы для сканирования, x-номер группы.
0: группа не выбрана
1: группа выбрана
TSC I/O регистр TSC->IOGXCR[0]( TSC_IOG1CR) - TSC->IOGXCR[7]( TSC_IOG8CR) счётчики групп.
CNT[13:0]-шестнадцати битный счётчик сканирования, максимальное значение 16383.
Номер массива соответствует счётчику
TSC->IOGXCR[0] - TSC_IOG1CR
TSC->IOGXCR[1] - TSC_IOG2CR
. . . . .
TSC->IOGXCR[7] - TSC_IOG8CR
Примеры програм.
Сам код как я уже упоминал будем писать в CMSIS и для примера я буду использовать контроллер STM32F051C8T6 с тактированием от внешнего кварца 8МГц через умножитель со значением 6 в итоге получилось частота 48МГц.
В первом примере мы будем проверять каждый из трёх сенсоров касания по очереди сохраняя результат в переменные sensor_result_1, sensor_result_2, sensor_result_3 для проверки и выполнения дальнейшего вашего действия, у меня в коде если было касание одному из трёх сенсоров то зажжётся светодиод подключённый к GPIOC выводу 13. В пример так же я добавил дополнительный код отправки результатов по UART через переходник USB-UART в программу терминал для визуального контроля, что бы использовать эту функцию просто раскомментируйте код настройки UART и отправки данных. Подключение контроллера к UART, порт GPIOA вывод 30( PA9) USART1_TX и вывод 31 ( PA10) USART1_RX.
#include "stm32f0xx.h"
//Переменная для результата сканирования 1.
uint16_t sensor_result_1;
//Переменная для результата сканирования 2.
uint16_t sensor_result_2;
//Переменная для результата сканирования 3.
uint16_t sensor_result_3;
int main(){
//Настройка тактирования контроллера, подключение к внешнему кварцу 8МГц через множитель 6, получилось 48МГц.
RCC_DeInit();
RCC_HSEConfig( RCC_HSE_ON);
while(RCC_GetFlagStatus( RCC_FLAG_HSERDY)== RESET) ;
FLASH_PrefetchBufferCmd( ENABLE);
FLASH_SetLatency( FLASH_Latency_1);
RCC_PREDIV1Config( RCC_PREDIV1_Div1);
RCC_PLLConfig( RCC_PLLSource_PREDIV1, RCC_PLLMul_6);
RCC_PLLCmd( ENABLE);
while( RCC_GetFlagStatus( RCC_FLAG_PLLRDY)== RESET);
RCC_SYSCLKConfig( RCC_SYSCLKSource_PLLCLK);
RCC_HSICmd(DISABLE);
RCC_AHBPeriphClockCmd( RCC_AHBPeriph_GPIOA| RCC_AHBPeriph_GPIOC, ENABLE);
//Настройка вывода для светодиода.
GPIO_InitTypeDef led_pc13;
led_pc13.GPIO_Mode= GPIO_Mode_OUT;
led_pc13.GPIO_OType= GPIO_OType_OD;
led_pc13.GPIO_Pin= GPIO_Pin_13;
led_pc13.GPIO_PuPd= GPIO_PuPd_NOPULL;
led_pc13.GPIO_Speed= GPIO_Speed_Level_1;
GPIO_Init( GPIOC, &led_pc13);
//Настройка UART, раскомментируйте если будете использовать.
/*
GPIO_PinAFConfig( GPIOA, GPIO_PinSource9, GPIO_AF_1);
GPIO_PinAFConfig( GPIOA, GPIO_PinSource10, GPIO_AF_1);
GPIO_InitTypeDef uart_gpio;
uart_gpio.GPIO_Pin = GPIO_Pin_9| GPIO_Pin_10;
uart_gpio.GPIO_Speed = GPIO_Speed_50MHz;
uart_gpio.GPIO_Mode = GPIO_Mode_AF;
uart_gpio.GPIO_OType = GPIO_OType_PP;
uart_gpio.GPIO_PuPd = GPIO_PuPd_UP;
GPIO_Init( GPIOA, &uart_gpio);
RCC_APB2PeriphClockCmd( RCC_APB2Periph_USART1, ENABLE);
USART_InitTypeDef uart;
uart.USART_BaudRate= 115200;
uart.USART_HardwareFlowControl= USART_HardwareFlowControl_None;
uart.USART_Mode= USART_Mode_Rx| USART_Mode_Tx;
uart.USART_Parity= USART_Parity_No;
uart.USART_StopBits= USART_StopBits_1;
uart.USART_WordLength= USART_WordLength_8b;
USART_Init( USART1, &uart);
USART_Cmd( USART1, ENABLE);
*/
//Настройка TSC и его выводов.
GPIO_PinAFConfig( GPIOA, GPIO_PinSource0, GPIO_AF_3);
GPIO_PinAFConfig( GPIOA, GPIO_PinSource1, GPIO_AF_3);
GPIO_PinAFConfig( GPIOA, GPIO_PinSource2, GPIO_AF_3);
GPIO_PinAFConfig( GPIOA, GPIO_PinSource3, GPIO_AF_3);
GPIO_InitTypeDef TSC_gpio;
//Настройка вывода для конденсатора захвата TSC_G1_IO1.
TSC_gpio.GPIO_Pin= GPIO_Pin_0;
TSC_gpio.GPIO_Speed= GPIO_Speed_50MHz;
TSC_gpio.GPIO_Mode= GPIO_Mode_AF;
TSC_gpio.GPIO_OType= GPIO_OType_OD;
TSC_gpio.GPIO_PuPd= GPIO_PuPd_NOPULL;
GPIO_Init( GPIOA, &TSC_gpio);
//Настройка выводов для сенсоров TSC_G1_IO2, TSC_G1_IO3, TSC_G1_IO4.
TSC_gpio.GPIO_Pin= GPIO_Pin_1| GPIO_Pin_2| GPIO_Pin_3;
TSC_gpio.GPIO_OType= GPIO_OType_PP;
GPIO_Init( GPIOA, &TSC_gpio);
//Настройка параметров TSC.
RCC_AHBPeriphClockCmd( RCC_AHBPeriph_TS, ENABLE);
TSC->CR= 0xFF0000C1;//bin=1111 1111 0000 0000 0000 0000 1100 0001.
TSC->ICR= 0x3;//Очищение флагов прерываний.
TSC->IOSCR= 0x1;//G1_IO1 подключен конденсатор захвата.
TSC->IOGCSR= 0x1;//Включаем первую группу.
TSC->IOHCR= 0xfffffff0;//Отключение триггера шмидта у первой группы, по умолчанию 0xffffffff у всех груп включен.
//Основной цикл программы.
while( 1){
TSC->IOCCR= 0x2;//На выводе G1_IO2 включен сенсор.
TSC->CR|=0x2;//Старт сканирования.
while( TSC->ISR==0);//Ждем окончания сканирования.
sensor_result_1= TSC->IOGXCR[0];
TSC->IOCCR= 0x0;//Отключены все сенсоры.
TSC->ICR|= 0x3;//Сброс флагов прерывания.
for( uint32_t i= 50; i> 0; i--);//Пауза для стабилизации.
TSC->IOCCR= 0x4;//На выводе G1_IO3 включен сенсор.
TSC->CR|=0x2;//Старт сканирования.
while( TSC->ISR==0);//Ждем окончания сканирования.
sensor_result_2= TSC->IOGXCR[0];
TSC->IOCCR= 0x0;//Отключены все сенсоры.
TSC->ICR|= 0x3;//Сброс флагов прерывания.
for( uint32_t i= 50; i> 0; i--);//Пауза для стабилизации.
TSC->IOCCR= 0x8;//На выводе G1_IO4 включен сенсор.
TSC->CR|=0x2;//Старт сканирования.
while( TSC->ISR==0);//Ждем окончания сканирования.
sensor_result_3= TSC->IOGXCR[0];
TSC->IOCCR= 0x0;//Отключены все сенсоры.
TSC->ICR|= 0x3;//Сброс флагов прерывания.
for( uint32_t i= 50; i> 0; i--);//Пауза для стабилизации.
//Проверка нажатий.
GPIO_SetBits( GPIOC, GPIO_Pin_13);
if( sensor_result_1< 0x350){ //Если было прикосновение включаем светодиод.
GPIO_ResetBits( GPIOC, GPIO_Pin_13);
}else{ }
if( sensor_result_2< 0x350){ //Если было прикосновение включаем светодиод.
GPIO_ResetBits( GPIOC, GPIO_Pin_13);
}else{ }
if( sensor_result_3< 0x350){ //Если было прикосновение включаем светодиод.
GPIO_ResetBits( GPIOC, GPIO_Pin_13);
}else{ }
//Передача по URAT, раскомментируйте если будете использовать.
/*
USART_SendData( USART1, (uint8_t )( sensor_result_1));
while( !USART_GetFlagStatus( USART1, USART_FLAG_TXE));
USART_SendData( USART1, (uint8_t )( sensor_result_1>>8));
while( !USART_GetFlagStatus( USART1, USART_FLAG_TXE));
USART_SendData( USART1, (uint8_t )( sensor_result_2));
while( !USART_GetFlagStatus( USART1, USART_FLAG_TXE));
USART_SendData( USART1, (uint8_t )( sensor_result_2>>8));
while( !USART_GetFlagStatus( USART1, USART_FLAG_TXE));
USART_SendData( USART1, (uint8_t )( sensor_result_3));
while( !USART_GetFlagStatus( USART1, USART_FLAG_TXE));
USART_SendData( USART1, (uint8_t )( sensor_result_3>>8));
while( !USART_GetFlagStatus( USART1, USART_FLAG_TXE));
for( uint32_t i=15000000; i> 0; i--);
*/
}
return 0;
}
И ещё один пример тут будем использовать прерывание для обнаружения нажатия и я покажу как реализовать кнопку у которой будет два постоянных состояния включить или выключить, что будет показано на светодиоде.
#include "stm32f0xx.h"
void TS_IRQHandler( void);
uint8_t On_Off_Button=0;//Переменная для состояния кнопки 0-включено 1-не включено.
uint8_t Trigger=0;//Переменная исключающая ошибочного переключения 0-изменилось состояния сенсора 1-не менялось.
int main(){
//Настройка тактирования контроллера, подключение к внешнему кварцу 8МГц через множитель 6, получилось 48МГц.
RCC_DeInit( );
RCC_HSEConfig( RCC_HSE_ON);
while(RCC_GetFlagStatus( RCC_FLAG_HSERDY)== RESET);
FLASH_PrefetchBufferCmd( ENABLE);
FLASH_SetLatency( FLASH_Latency_1);
RCC_PREDIV1Config( RCC_PREDIV1_Div1);
RCC_PLLConfig( RCC_PLLSource_PREDIV1, RCC_PLLMul_6);
RCC_PLLCmd( ENABLE);
while( RCC_GetFlagStatus( RCC_FLAG_PLLRDY)== RESET);
RCC_SYSCLKConfig( RCC_SYSCLKSource_PLLCLK);
RCC_HSICmd( DISABLE);
RCC_AHBPeriphClockCmd( RCC_AHBPeriph_GPIOA| RCC_AHBPeriph_GPIOC, ENABLE);
//Настройка вывода для светодиода.
GPIO_InitTypeDef led_pc13;
led_pc13.GPIO_Mode= GPIO_Mode_OUT;
led_pc13.GPIO_OType= GPIO_OType_OD;
led_pc13.GPIO_Pin= GPIO_Pin_13;
led_pc13.GPIO_PuPd= GPIO_PuPd_NOPULL;
led_pc13.GPIO_Speed= GPIO_Speed_Level_1;
GPIO_Init( GPIOC, &led_pc13);
//Настройка TSC и его выводов.
GPIO_PinAFConfig( GPIOA, GPIO_PinSource0, GPIO_AF_3);
GPIO_PinAFConfig( GPIOA, GPIO_PinSource1, GPIO_AF_3);
//Настройка порт GPIOA выводы 0( конденсатор) и 1( сенсор) для TSC.
GPIO_InitTypeDef TSC_gpio;
//Настройка вывода для конденсатора захвата TSC_G1_IO1.
TSC_gpio.GPIO_Pin= GPIO_Pin_0;
TSC_gpio.GPIO_Speed= GPIO_Speed_50MHz;
TSC_gpio.GPIO_Mode= GPIO_Mode_AF;
TSC_gpio.GPIO_OType= GPIO_OType_OD;
TSC_gpio.GPIO_PuPd= GPIO_PuPd_NOPULL;
GPIO_Init( GPIOA, &TSC_gpio);
//Настройка вывода для сенсора TSC_G1_IO2.
TSC_gpio.GPIO_Pin= GPIO_Pin_1;
TSC_gpio.GPIO_OType= GPIO_OType_PP;
GPIO_Init( GPIOA, &TSC_gpio);
//Настройка параметров TSC для работы с одним сенсором.
RCC_AHBPeriphClockCmd( RCC_AHBPeriph_TS, ENABLE);
TSC->CR= 0xFF0000C1;//bin=1111 1111 0000 0000 0000 0000 1100 0001.
TSC->ICR= 0x3;//Очищение флагов прерываний.
TSC->IER= 0x3;//Включаем прерывание от переполнения и окончания преобразования.
TSC->IOSCR= 0x1;//G1_IO1 подключен конденсатор захвата.
TSC->IOCCR= 0x2;//На выводе G1_IO2 включен сенсор.
TSC->IOGCSR= 0x1;//Включаем первую группу.
TSC->IOHCR= 0xfffffffc;//включение отключение триггер шмидта, по умолчанию 0xffffffff.
//Настройка прерывания от TSC.
NVIC_InitTypeDef TSC_interrupt;
TSC_interrupt.NVIC_IRQChannel= TS_IRQn;
TSC_interrupt.NVIC_IRQChannelCmd= ENABLE;
TSC_interrupt.NVIC_IRQChannelPriority= 0;
NVIC_Init( &TSC_interrupt);
while( 1){
while( TSC->CR& 2);//Не запускаем сканирование пока не закончится предыдущее.
TSC->CR|=0x2;
for( uint32_t i=50; i>0; i--);
}
return 0;
}
//Обработчик прерываний.
void TS_IRQHandler( void){
if( TSC->ISR& 2){//Ошибка, счётчик переполнен.
TSC->ICR|= 0x3;
}else{//Удачное окончания преобразования.
TSC->ICR|= 0x1;
if( ( TSC->IOGXCR[0]< 0x300)&& ( Trigger== 0)){//Есть нажатие, больше не обрабатывать пока не отожмёт кнопку.
//Меняем состояние светодиода в зависимости от On_Off_Button.
if( On_Off_Button== 0){
On_Off_Button= 1;
GPIO_SetBits( GPIOC, GPIO_Pin_13);
}else{
On_Off_Button= 0;
GPIO_ResetBits( GPIOC, GPIO_Pin_13);}
Trigger= 1;//Больше не обрабатывать нажатие пока нажата кнопка.
}
if( ( TSC->IOGXCR[0]> 0x900)&& ( Trigger== 1)){ Trigger= 0;}//Нет касания, можно обрабатывать нажатие.
}
}