Помощь - Поиск - Пользователи - Календарь
Полная версия этой страницы: TSC (Touch sensing controller) на STM32F051
Форум разработчиков электроники ELECTRONIX.ru > Микроконтроллеры (MCs) > ARM
silovi4
Выполняю проект по разработке устройства. Нужно реализовать управление сенсорными кнопками. Всего кнопок 14. Контроллер использую только STM32F051.
Вот схема данного устройства: http://www.picshare.ru/view/2993892/
Кнопки расположены вот так: http://www.picshare.ru/view/2993980/
Скачал библиотеку "STM32F0xx_STMTouch_Lib_V1.0.1" на сайте ST, но как с ней работать ума не приложу. Непонятен сам алгоритм работы, что зачем вызывать. Информации по данному контроллеру очень мало.
Прошу помощи по работе с TSC под STM32F051. Кто работал с данной периферией? Есть ли рабочие примеры или документация (желательно на русском, а то с английским не очень дружу)
adnega
Делал сенсорные кнопки на TSC в STM32F3xx. Думаю, это одно и то же.
С UserManual без каких-либо библиотек запускается с пол оборота, но осциллограф желателен.
silovi4
Можете поделиться исходным кодом? rolleyes.gif
adnega
Могу. Но вопросов будет еще больше.
Библиотек не использую, работаю напрямую с регистрами.
Приду домой - выложу.
silovi4
Я бы и не создавал эту тему если бы не было вопросов sm.gif Мне хотя бы понять алгоритм работы, как и что инициализировать, что запускать. Я кстати сам предпочитаю работать напрямую с регистрами, но в последнее время пристрастился к библиотекам и функциям.
Буду ждать... wink.gif
adnega
Цитата(silovi4 @ Sep 20 2013, 15:01) *
Буду ждать... wink.gif

CODE

//-----------------------------------------------------------------------------
// void __inline init_TSC(void)
//-----------------------------------------------------------------------------
void __inline init_TSC(void)
{
//TSC_CR_TSCE_bit = 1;

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)
| (6 << 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 = (1 << TSC_G1_IO4); // Cs
TSC->IOCCR = (1 << TSC_G1_IO3);
TSC->IOGCSR = (1 << 0);

}

Кроме этого:

NVIC->ISER[0] = (1 << NVIC_ISER0_TSC) | (1 << NVIC_ISER0_TIM2);

RCC->AHBENR =
(1 << RCC_AHB_IOPA)
| (1 << RCC_AHB_IOPB)
| (1 << RCC_AHB_IOPC)
| (1 << RCC_AHB_IOPD)
| (1 << RCC_AHB_IOPE)
| (1 << RCC_AHB_TSC)
| (1 << RCC_AHB_FLITF)
| (1 << RCC_AHB_SRAM)
| (1 << RCC_AHB_DMA1)
| (0 << RCC_AHB_DMA2);

GPIOA->MODER =
(GPIO_MODE_ALTERNATE << GPIO_MODER_PIN1)
| (GPIO_MODE_ALTERNATE << GPIO_MODER_PIN2)
| (GPIO_MODE_ALTERNATE << GPIO_MODER_PIN3)
| (GPIO_MODE_ANALOG << GPIO_MODER_PIN4)
| (GPIO_MODE_OUTPUT << GPIO_MODER_PIN8)
| (GPIO_MODE_ALTERNATE << GPIO_MODER_PIN9)
| (GPIO_MODE_ALTERNATE << GPIO_MODER_PIN10)
| (GPIO_MODE_ALTERNATE << GPIO_MODER_PIN13)
| (GPIO_MODE_ALTERNATE << GPIO_MODER_PIN14)
| (GPIO_MODE_OUTPUT << GPIO_MODER_PIN15);

GPIOA->OTYPER = (1 << 3);

GPIOA->AFR[0] =
(AF_PA1_TSC_G1_IO2 << GPIO_AFR0_PIN1)
| (AF_PA2_TSC_G1_IO3 << GPIO_AFR0_PIN2)
| (AF_PA3_TSC_G1_IO4 << GPIO_AFR0_PIN3);

И наконец:
//-----------------------------------------------------------------------------
// void EXTI2_TSC_IRQHandler(void)
//-----------------------------------------------------------------------------
void EXTI2_TSC_IRQHandler(void) __attribute__((interrupt("IRQ")));
void EXTI2_TSC_IRQHandler(void)
{

int i;

static int ff = 0;
#define FF_SIZE (5)
static int point = 0;
static int delta = 0;
static int updown = 0;

if(TSC->ISR & (1 << TSC_ISR_MCEF))
{
// max
TSC->ICR = (1 << TSC_ICR_MCEIC);
//TSC_CR_START_bit = 1;
}
if(TSC->ISR & (1 << TSC_ISR_EOAF))
{
// done
TSC->ICR = (1 << TSC_ICR_EOAIC);
tsc_val = TSC->IOGXCR[0];
val[state][count] = tsc_val;

cap_sensor_data(item, tsc_val);

value[item] = tsc_val;
switch(item)
{
case 0:
pos[item] = (cap[item].val - cap[item].t_min) * 100 / (cap[item].t_max - cap[item].t_min);
break;

case 1:
pos[item] = (cap[item].val - cap[item].t_min) * 100 / (cap[item].t_max - cap[item].t_min);
break;
}

delta = delta + pos[0] + pos[1] - (delta / DELTA_N);

if(((delta / DELTA_N) - (pos[0] + pos[1])) > 20)
{
if(updown == 0)
{
//con_str("{UPDOWN=1}\n\r");
//con_start();
}
updown = 1;
}
else if(((delta / DELTA_N) - (pos[0] + pos[1])) < -20)
{
if(updown == 1)
{
//con_str("{UPDOWN=0}\n\r");
//con_start();
}
updown = 0;
}

if(updown)
{
i = (pos[0] - pos[1]) / 2;
if(i < p_min) p_min = i;
if(i > p_max) p_max = i;
}
else
{
if((p_min != 1000) && (p_max != -1000))
{
i = (p_min + p_max) / 2;

if(i < -5) v = 0;
else if(i > 5) v = 900;
else if((-5 <= p_min) && (p_max <= 5)) v = 450;

}
p_min = 1000;
p_max = -1000;
i = -1000;
}

if(i != -1000)
{
if(l == -1000) l = i;
v += (i - l) * 10;
l = i;

v += i / 5;

if(v < 0) v = 0;
else if(v > 900) v = 900;

}
else l = -1000;

fv = fv + v - (fv / F_MUL);
i = (1 << ((fv / F_MUL) / 100)) - 1;
GPIOE->BSRR = (i << 8) | (((i ^ 0xFF) << 8) << 16);
}
}

И вызываем с частотой 100Гц:

//count++;
//if(count >= 100) count = 0;

count = 27;

switch(item)
{
case 0:
item = 1;
TSC->IOCCR = (1 << TSC_G1_IO3);
break;

case 1:
default:
item = 0;
TSC->IOCCR = (1 << TSC_G1_IO2);
break;
}

TSC->CR =
((1 + (count / 10)) << TSC_CR_CTPH)
| ((1 + (count % 10)) << TSC_CR_CTPL)
| (6 << TSC_CR_MCV)
| (1 << TSC_CR_START)
| (1 << TSC_CR_TSCE);



Код в большей части отладочный, но рабочий.
silovi4
Да уж... тут без пол литра не разобраться smile3046.gif А зачем осциллограф нужен?
adnega
Цитата(silovi4 @ Oct 7 2013, 13:49) *
Да уж... тут без пол литра не разобраться smile3046.gif А зачем осциллограф нужен?

Если выкинуть матан из обработчика прерывания, то инициализация простейшая.
С осциллографом работа ускоряется в разы.
silovi4
Какой кварц использовали внешний или внутренний, на какой частоте тактировали шину AHB?

Какие библиотеки нужно подключить для работы с TSC? А то у меня при компиляции миллион ошибок. Работаю в Coocox IDE
Подключил как основную
#include "stm32f0xx.h"

а также для удобства
#include "stm32f0xx_rcc.h"
#include "stm32f0xx_gpio.h"
#include "stm32f0xx_exti.h"

Я так понимаю нужен файл с расписанной адресацией регистров:
TSC_CR_CTPH
TSC_CR_CTPL
TSC_CR_SSD
TSC_CR_SSE
TSC_CR_SSPSC
TSC_CR_PGPSC
TSC_CR_MCV
и т.д.

Как у вас с этим дело обстоит?
silovi4
Я так понимаю CMSIS файл вы сами доделывали?
adnega
Цитата(silovi4 @ Oct 9 2013, 12:06) *
Я так понимаю CMSIS файл вы сами доделывали?

Я ж говорю: "библиотек чужих не использую".
Описывал номера битов самостоятельно на основе UM.
Вы не пытайтесь скомпиллировать пример - лучше попробуйте написать свой, взяв пример для демонстрации ключевых моментов.
silovi4
Цитата(adnega @ Oct 9 2013, 12:25) *
Я ж говорю: "библиотек чужих не использую".
Описывал номера битов самостоятельно на основе UM.
Вы не пытайтесь скомпиллировать пример - лучше попробуйте написать свой, взяв пример для демонстрации ключевых моментов.

ОК попробую rolleyes.gif
silovi4
Что-то я не могу запустить TSC, тактирование подаю:
RCC->AHBENR |= RCC_AHBENR_TSEN;
Смотрю на состояние регистров RCC - тактирование TSC идёт.
Но когда пытаюсь запустить TSC:
TSC->CR = 0x01;
Состояние бита не меняется wacko.gif Тактирую чип от внутреннего кварца HSI с умножителем на 48 МГц, шину AHB тоже на 48 МГц.
Может что-то не то делаю? help.gif
adnega
Цитата(silovi4 @ Oct 9 2013, 18:05) *
Что-то я не могу запустить TSC, тактирование подаю:
RCC->AHBENR |= RCC_AHBENR_TSEN;
Смотрю на состояние регистров RCC - тактирование TSC идёт.
Но когда пытаюсь запустить TSC:
TSC->CR = 0x01;
Состояние бита не меняется wacko.gif Тактирую чип от внутреннего кварца HSI с умножителем на 48 МГц, шину AHB тоже на 48 МГц.
Может что-то не то делаю? help.gif

Попробуйте подождать перед чтением TSC->CR.
А лучше запишите TSC->CR два раза.
silovi4
Цитата(adnega @ Oct 9 2013, 20:56) *
Попробуйте подождать перед чтением TSC->CR.
А лучше запишите TSC->CR два раза.

Попробовал, не помогло. Уже смотрю напрямую в память - там тоже нули ((( Даже в бесконечном цикле не устанавливается. Может я спалил его когда паял, хотя АЦП, порты, таймеры и часы работают.
Даже не знаю что делать, тупик какой-то...
Попробую в Keil-е собрать и отдебажить.
silovi4
И всё таки проблема с CooCox-ом. В Keil-е всё отлично устанавливается и сбрасывается. Так что буду копать дальше. 08.gif
silovi4
Непонятно как это работает:
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)
| (6 << TSC_CR_MCV)
| (0 << TSC_CR_IODEF)
| (0 << TSC_CR_SYNCPOL)
| (0 << TSC_CR_AM)
| (0 << TSC_CR_START)
| (1 << TSC_CR_TSCE);

Можете объяснить как эта запись записывает значения в регистр CR? Или это такая очистка регистра?
silovi4
И тишина...
adnega
Цитата(silovi4 @ Oct 11 2013, 15:00) *
Непонятно как это работает:
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)
| (6 << TSC_CR_MCV)
| (0 << TSC_CR_IODEF)
| (0 << TSC_CR_SYNCPOL)
| (0 << TSC_CR_AM)
| (0 << TSC_CR_START)
| (1 << TSC_CR_TSCE);

Можете объяснить как эта запись записывает значения в регистр CR? Или это такая очистка регистра?

Посмотрите поля регистра TSC->CR. Устанавливаются соответствующие значения в соответствующие поля. Что конкретно не понятно?
silovi4
Цитата(adnega @ Oct 30 2013, 13:13) *
Посмотрите поля регистра TSC->CR. Устанавливаются соответствующие значения в соответствующие поля. Что конкретно не понятно?

С этим уже разобрался. Инициализация у меня проходит, т.е. я инициализировал порты и TSC. Теперь не понятно как определять касание. Я так понимаю это определяется уже в прерывании, но в Вашем коде мне не понятны некоторые переменные, откуда они берутся. Можете в двух словах объяснить как определять касание?
adnega
Цитата(silovi4 @ Oct 30 2013, 19:05) *
С этим уже разобрался. Инициализация у меня проходит, т.е. я инициализировал порты и TSC. Теперь не понятно как определять касание. Я так понимаю это определяется уже в прерывании, но в Вашем коде мне не понятны некоторые переменные, откуда они берутся. Можете в двух словах объяснить как определять касание?

Исключительный матан. В двух словах: это авторский алгоритм с автокалибровкой, раскрывать желания не имею.
Вам сойдет простое сравнение с порогом. Данные от TSC можно забирать так:
Код
tsc_val = TSC->IOGXCR[0];

silovi4
Цитата(adnega @ Oct 30 2013, 19:39) *
Исключительный матан. В двух словах: это авторский алгоритм с автокалибровкой, раскрывать желания не имею.
Вам сойдет простое сравнение с порогом. Данные от TSC можно забирать так:
Код
tsc_val = TSC->IOGXCR[0];

Спасибо хоть за это! Хотел ещё спросить: в моей схеме, та что я приложил в самом начале, я правильно подключил конденсаторы? А то я меряю осциллографом ножку PA0 на неё вообще ничего не приходит, а на PA1 идут импульсы примерно такого вида:
Нажмите для просмотра прикрепленного файла
adnega
Цитата(silovi4 @ Nov 1 2013, 16:33) *
Спасибо хоть за это! Хотел ещё спросить: в моей схеме, та что я приложил в самом начале, я правильно подключил конденсаторы? А то я меряю осциллографом ножку PA0 на неё вообще ничего не приходит, а на PA1 идут импульсы примерно такого вида:
Нажмите для просмотра прикрепленного файла

А данные при этом меняются?
Осциллограмма очень похожа на правду.
silovi4
Цитата(adnega @ Nov 1 2013, 16:52) *
А данные при этом меняются?
Осциллограмма очень похожа на правду.

Вы про счётный регистр? Если да, то он досчитывает до максимально заданного значения при этом устанавливаются два флага (флаг ошибки и флаг завершения опроса), которые вызывают прерывание. При касании пальцем ничего не меняется. sad.gif
silovi4
Цитата(silovi4 @ Nov 1 2013, 18:39) *
Вы про счётный регистр? Если да, то он досчитывает до максимально заданного значения при этом устанавливаются два флага (флаг ошибки и флаг завершения опроса), которые вызывают прерывание. При касании пальцем ничего не меняется. sad.gif

В общем разобрался, то я не правильно сконфигурировал порты. Данные получаются вполне адекватные. Когда не касаюсь сенсора счётный регистр показывает где-то 0x14С0, когда дотрагиваюсь - 0x17С0. Я думаю так и должно быть. Теперь мне не понятно как определять что нажата конкретная кнопка?
adnega
Цитата(silovi4 @ Nov 4 2013, 12:10) *
В общем разобрался, то я не правильно сконфигурировал порты. Данные получаются вполне адекватные. Когда не касаюсь сенсора счётный регистр показывает где-то 0x14С0, когда дотрагиваюсь - 0x17С0. Я думаю так и должно быть.

Когда я делал сенсор, то числа отличались очень сильно (в разы).
Цитата
Теперь мне не понятно как определять что нажата конкретная кнопка?

Переключением опроса кнопок по очереди.
silovi4
Цитата(adnega @ Nov 4 2013, 11:34) *
Когда я делал сенсор, то числа отличались очень сильно (в разы).

Кстати у меня почему-то в group2 значения очень маленькие, от 0x04 до 0x0B sad.gif Во всех остальных (при тех же настройках) вроде нормально.
Цитата(adnega @ Nov 4 2013, 11:34) *
Переключением опроса кнопок по очереди.

Всё понял rolleyes.gif

А какой ёмкостью кондёры у вас были?
adnega
Цитата(silovi4 @ Nov 4 2013, 15:43) *
А какой ёмкостью кондёры у вас были?

вроде, 100нФ.
silovi4
Разобрался с group 2, оказалось просто не припаял один конец кондёра. Теперь возник другой вопрос: почему на некоторых тачах (даже если тач из одной группы) значения меняются в противоположную сторону. Допустим на таче G4_IO1, когда не касаешься его, значение 0x1400, если касаешся 0x1600. А на таче G4_IO3 наоборот (при касании 0x1400, а при отпускании 0x1600). Может сталкивались с этой проблемой?
adnega
Цитата(silovi4 @ Nov 4 2013, 22:31) *
Теперь возник другой вопрос: почему на некоторых тачах (даже если тач из одной группы) значения меняются в противоположную сторону. Может сталкивались с этой проблемой?

Такого не должно быть. При касании значение должно уменьшаться (т.к. переносимый заряд увеличивается
и для достижения порога нужно меньше иттераций). Где-то неправильно инициализируете.
silovi4
Цитата(adnega @ Nov 4 2013, 22:48) *
Такого не должно быть. При касании значение должно уменьшаться (т.к. переносимый заряд увеличивается
и для достижения порога нужно меньше иттераций). Где-то неправильно инициализируете.

Действительно. Оказывается для разных групп необходима своя инициализация, я бы даже сказал для каждого сенсора (если сенсоры находятся на разном расстоянии от ножки микроконтроллера). Жаль что я этого не знал когда проектировал плату, теперь придётся при каждом опросе менять инициализацию. Ну как говорится не бывает худа без добра, зато я теперь могу сенсором управлять светодиодами, такой себе hello word biggrin.gif
Для просмотра полной версии этой страницы, пожалуйста, пройдите по ссылке.
Invision Power Board © 2001-2025 Invision Power Services, Inc.