Делаю я тут клавиатуру для прибора. И грустно мне стало использовать мембранную клавиатуру, когда в моём микропроцессоре есть нативный контроллер сенсорных кнопок.
Информации по этому вопросу довольно мало. С одной стороны, есть библиотека от ST. Но она громоздкая и для моих задач излишняя.
С другой стороны, есть возможность прямо работать с регистрами контроллера. Нашёл в сети маленький примерчик, и на его основе делаю свой интерфейс.
Выкладываю наработки.
Условия: работа во FreeRTOS, процессор STM32F051.
Код пока не проверен, так как плата с сенсорными кнопками в процессе изготовления.
Инициализация:
Код:
void __inline vInit_TSC (void)
{
RCC->AHBENR |= RCC_AHBENR_TSEN | RCC_AHBENR_GPIOAEN;
GPIOA->MODER |= GPIO_MODER_MODER4_1;
GPIOA->OTYPER &= ~GPIO_OTYPER_OT_4;
GPIOA->OSPEEDR |= GPIO_OSPEEDER_OSPEEDR4;
GPIOA->PUPDR &= ~GPIO_PUPDR_PUPDR4;
GPIOA->MODER |= GPIO_MODER_MODER5_1;
GPIOA->OTYPER &= ~GPIO_OTYPER_OT_5;
GPIOA->OSPEEDR |= GPIO_OSPEEDER_OSPEEDR5;
GPIOA->PUPDR &= ~GPIO_PUPDR_PUPDR5;
GPIOA->MODER |= GPIO_MODER_MODER6_1;
GPIOA->OTYPER &= ~GPIO_OTYPER_OT_6;
GPIOA->OSPEEDR |= GPIO_OSPEEDER_OSPEEDR6;
GPIOA->PUPDR &= ~GPIO_PUPDR_PUPDR6;
GPIOA->MODER |= GPIO_MODER_MODER7_1;
GPIOA->OTYPER |= GPIO_OTYPER_OT_7;
GPIOA->OSPEEDR |= GPIO_OSPEEDER_OSPEEDR7;
GPIOA->PUPDR &= ~GPIO_PUPDR_PUPDR7;
GPIOB->MODER |= GPIO_MODER_MODER11_1;
GPIOA->OTYPER &= ~GPIO_OTYPER_OT_11;
GPIOA->OSPEEDR |= GPIO_OSPEEDER_OSPEEDR11;
GPIOA->PUPDR &= ~GPIO_PUPDR_PUPDR11;
GPIOB->MODER |= GPIO_MODER_MODER12_1;
GPIOA->OTYPER &= ~GPIO_OTYPER_OT_12;
GPIOA->OSPEEDR |= GPIO_OSPEEDER_OSPEEDR12;
GPIOA->PUPDR &= ~GPIO_PUPDR_PUPDR12;
GPIOB->MODER |= GPIO_MODER_MODER13_1;
GPIOA->OTYPER &= ~GPIO_OTYPER_OT_13;
GPIOA->OSPEEDR |= GPIO_OSPEEDER_OSPEEDR13;
GPIOA->PUPDR &= ~GPIO_PUPDR_PUPDR13;
GPIOB->MODER |= GPIO_MODER_MODER14_1;
GPIOA->OTYPER |= GPIO_OTYPER_OT_14;
GPIOA->OSPEEDR |= GPIO_OSPEEDER_OSPEEDR14;
GPIOA->PUPDR &= ~GPIO_PUPDR_PUPDR14;
GPIOA->AFR[0] |= GPIO_AFRL_AFRL4 & (GPIO_AF_AF3 << 16);
GPIOA->AFR[0] |= GPIO_AFRL_AFRL5 & (GPIO_AF_AF3 << 20);
GPIOA->AFR[0] |= GPIO_AFRL_AFRL6 & (GPIO_AF_AF3 << 24);
GPIOA->AFR[0] |= GPIO_AFRL_AFRL7 & (GPIO_AF_AF3 << 28);
GPIOB->AFR[1] |= GPIO_AFRH_AFRH3 & (GPIO_AF_AF3 << 12);
GPIOB->AFR[1] |= GPIO_AFRH_AFRH4 & (GPIO_AF_AF3 << 16);
GPIOB->AFR[1] |= GPIO_AFRH_AFRH5 & (GPIO_AF_AF3 << 20);
GPIOB->AFR[1] |= GPIO_AFRH_AFRH6 & (GPIO_AF_AF3 << 24);
TSC->CR = (1 << TSC_CR_TSCE);
TSC->CR =
(2 << TSC_CR_CTPH)
| (6 << TSC_CR_CTPL)
| (9 << TSC_CR_SSD)
| (0 << TSC_CR_SSE)
| (1 << TSC_CR_SSPSC)
| (0 << TSC_CR_PGPSC)
| (TSC_CR_MCV_16383 << TSC_CR_MCV)
| (0 << TSC_CR_IODEF)
| (0 << TSC_CR_SYNCPOL)
| (0 << TSC_CR_AM)
| (0 << TSC_CR_START)
| (1 << TSC_CR_TSCE);
TSC->IER =
(1 << TSC_IER_EOAIE)
| (1 << TSC_IER_MCEIE);
TSC->IOSCR = TSC_IOSCR_G2_IO4
| TSC_IOSCR_G6_IO4;
TSC->IOCCR = TSC_IOCCR_G2_IO1
| TSC_IOCCR_G6_IO1;
TSC->IOGCSR = TSC_IOGCSR_G2E
| TSC_IOGCSR_G6E;
}
Я использовал 6 кнопок в двух группах. Видно по комментариям.
В обработчике прерывании я измеряю емкость всех кнопок, а затем передаю массив на обработку:
Код:
void TS_IRQHandler (void)
{
portBASE_TYPE xWoken = pdFALSE;
Tsense *point_t = &t;
if ((TSC->ISR & TSC_ISR_MCEF)
{
t.Cap[0][t.Position] = 0;
t.Cap[1][t.Position] = 0;
TSC->ICR = TSC_ICR_MCEIC | TSC_ICR_EOAIC;
}
else
{
t.Cap[0][t.Position] = *TS_Group[0];
t.Cap[1][t.Position] = *TS_Group[1];
TSC->ICR = TSC_ICR_EOAIC;
}
if (++t.Position < TS_MAX_NUM_PIN)
{
TSC->IOCCR = TS_Mask_Channel[0][t.Position]
| TS_Mask_Channel[0][t.Position];
TSC->CR |= (1 << TSC_CR_START);
t.EndMeasure = 0;
}
else
{
t.Position = 0;
t.EndMeasure = 1;
xQueueSendToBackFromISR (g.xTS_Queue, &oint_t, &xWoken);
portYIELD_FROM_ISR (xWoken);
}
}
Структура данных кнопок:
Код:
#define TS_MAX_NUM_GROUP 2
#define TS_MAX_NUM_PIN 3
#pragma pack(4)
typedef struct Tsense
{
int Cap [TS_MAX_NUM_GROUP][TS_MAX_NUM_PIN];
int Position;
int EndMeasure;
}Tsense;
#pragma pack()
extern Tsense t;
const __IO uint32_t *TS_Group [TS_MAX_NUM_GROUP] =
{
&TSC->IOGXCR[1],
&TSC->IOGXCR[5]
};
const uint32_t TS_Mask_Channel [TS_MAX_NUM_GROUP] [TS_MAX_NUM_PIN] =
{
TSC_IOCCR_G2_IO1, TSC_IOCCR_G2_IO2, TSC_IOCCR_G2_IO3,
TSC_IOCCR_G6_IO1, TSC_IOCCR_G6_IO2, TSC_IOCCR_G6_IO1
};
И, наконец, главный цикл программы. Здесь пока рыба, а будет обработка
Код:
void vTsense (void *pvParameters)
{
int i, j;
Tsense *Sense;
int TSC_min [TS_MAX_NUM_GROUP][TS_MAX_NUM_PIN];
int TSC_max [TS_MAX_NUM_GROUP][TS_MAX_NUM_PIN];
vInit_TSC ();
for (i = 0; i < TS_MAX_NUM_GROUP; i++)
{
for (j = 0; j < TS_MAX_NUM_PIN; j++)
{
TSC_max[i][j] = t.Cap[i][j] = 0;
TSC_min[i][j] = 16384;
}
}
t.Position = t.EndMeasure = 0;
for ( ; ; )
{
vTaskDelay (200);
TSC->CR |= (1 << TSC_CR_START);
xQueueReceive (g.xTS_Queue, &Sense, portMAX_DELAY);
if (t.EndMeasure)
{
t.EndMeasure = 0;
vTSCompareminmax (&Sense->Cap[0][0], TSC_min, TSC_max);
}
}
}
Я выкладываю этот код скорее для того, чтобы самому лучше разобраться в процессе, но если кому интересно, пишите свои соображения.
Если кто заметит явные косяки, откликнитесь.
Также интересны способы постобработки для получения статуса нажатых кнопок и использования слайдера.