RSS
people

Player Remote — Keyboard

Ну переезд закончен (все, кажется, работает как раньше), а посему можно продолжить.

Я в прошлой статье обещал рассказать про сенсорную клавиатуру моего пульта для плеера, что я сейчас и сделаю.

Для начала — сделал все таки фотку, как  выглядит пульт в машине:

Пульт для плеера в машину

В машине

Для начала — схемка клавиатуры: mmp3premote_kbd_schematic.pdf.

Схема и принцип взяты отсюда.

В принципе, там все описано, но можно и повторить еще разок.

И так, подключение кнопки выглядит вот так:

Схема подключения кнопки

Схема подключения кнопки

P1 — это контактная площадка. Линия BUTTONS0 подключается напрямую к пину микроконтроллера.

В исходном положении пин находится в положении 0, выход, таким образом ток идет от VCC, через сопротивление R1 в пин, а там в землю.

Чтобы опросить состояние сенсора, мы переключаем пин на вход, т.е. просто меняем регистр направления. Сопротивление подтяжки отключено. Теперь ток заряжает конденсатор, если он был разряжен. А разряженным он окажется, если к нему поднести палец очень близко или прикоснуться.

И чем сильнее, плотнее мы будем прикасаться к сенсору, тем дольше конденсатор будет заряжаться.

Когда конденсатор зарядится до уровня логической единицы, она, единица, появится на пине контроллера.

Измерив время, можно определить, был ли сенсор «нажат» или нет.

Т.е. при старте системы надо всегда делать калибровку, что-бы определить время заряжаемости конденсатора в «не нажатом» состоянии. Естественно, если в этот момент поднести руку к сенсору, то потом он просто не будет определяться как «нажатый».

В исходниках можно поглядеть процесс опроса кнопки поближе:

KbdScan()   
  1. void KbdScan( void )
  2. {
  3.   register u08 butIdx = 0;
  4.   register u16 tempVal;
  5.   register u16 newKbdKeys = kbdKeys;
  6.   for( butIdx = 0; butIdx < KEY_MAX_KEYS; butIdx++ )
  7.   {
  8.     #ifdef TEST_PINS_BUTTONS
  9.     if( butIdx < 3 )
  10.     {
  11.       continue;
  12.     }
  13.     #endif
  14.     TCNT1  = 0x0000;  // Reset Timer counter
  15.         TCCR1B = KBD_START_TIMER;    // Start time
  16.     kbdButtons[butIdx].portAddr[ PDIR ] &= ~kbdButtons[butIdx].pinMask;
  17.     while( !(kbdButtons[butIdx].portAddr[ PINS ] & kbdButtons[butIdx].pinMask) );
  18.         TCCR1B  = KBD_STOP_TIMER;      // Stop timer
  19.     if( !(TIFR & (1 << TOV1)) )
  20.     {
  21.       tempVal = TCNT1;
  22.       if ( tempVal > kbdButtons[ butIdx ].sensHi )
  23.       {
  24.         newKbdKeys |= (1 << butIdx);
  25.       }
  26.       else if ( tempVal < kbdButtons[ butIdx ].sensLo )
  27.       {
  28.         newKbdKeys &= ~(1 << butIdx);
  29.       }
  30.     }
  31.     else
  32.     {
  33.       TIFR |= (1 << TOV1);
  34.     }
  35.     KbdSetButton( butIdx );
  36.   }
  37.   #ifdef TEST_PINS_BUTTONS
  38.   newKbdKeys &= 0xFFF8;
  39.   #endif
  40.   if( (newKbdKeys != kbdKeys) ) // & (gFlags.kbdEvent == 0)
  41.   {
  42.     gFlags.kbdEvent = 1;
  43.     gKeys = newKbdKeys;
  44.   }
  45.   kbdKeys = newKbdKeys;
  46. }

В строках 150-155 и 185-187 идет «отсечение» пинов, которые используются для отладки, если определен TEST_PINS_BUTONS. Поэтому на них особо внимания не обращайте.

Самое основное происходит в строках 156-163:

  • 156,157 — заводим таймер,
  • 159 — переключаем пин кнопки на вход,
  • 161 — ждем, когда пин переключится в 1,
  • 163 — выключаем таймер.

А дальше идет анализ таймера.

В строке 165 мы определяем, было ли переполнение. Если таковое произошло, то либо у нас не откалиброваны  значения таймеров для кнопок, либо к кнопкам прикоснулись напрямую, без бумаги между пальцем и сенсором, тогда время заряда очень большое, нежели, если прикоснуться к нему через какой либо диэлектрик тонкий.

Если такое произошло, мы просто не обрабатываем эту кнопку а переходим к следующей.

Если же переполнения не было, то мы в строках 167-175 проверяем временные гистерезисы, и решаем, была ли «нажата» кнопка или нет, и выставляем или удаляем соответствующий бит в локальной маске newKbdBytes.

В строке 182 мы возвращяем бит в исходное положение.

В строках  189 — 193 мы смотрим, если произошли какие либо изменения! то мы выставляем глобальный флаг этого, и сохраняем локальную маску кнопок в глобальной маске gKeys.

Под изменениями я имею ввиду не только «нажатие», но и «отпускание» клавиши. Таким образом, если клавиша «нажата», то мы отправляем одно сообщение, и все. Если она останется «нажатой», то хост не получит больше никаких сообщений, и только когда клавиша будет «отжата», тогда хост получит извещение об этом.

Вот собственно и вся процедура.

Отдельно можно глянуть на функции установки пинов:

  1. typedef struct
  2. {
  3.   volatile u08 * portAddr;
  4.   u08 pinMask;
  5.   u16 sensHi;
  6.   u16 sensLo;
  7. } TKbdButton;

  1. TKbdButton kbdButtons[ KEY_MAX_KEYS ] =
  2. {
  3.   {&PINA, 0x01, 0, 0}, //  0
  4.   {&PINA, 0x02, 0, 0}, //  1
  5.   {&PINA, 0x04, 0, 0}, //  2
  6.   {&PINA, 0x08, 0, 0}, //  3
  7.   {&PINA, 0x10, 0, 0}, //  4
  8.   {&PINA, 0x20, 0, 0}, //  5
  9.   {&PINA, 0x40, 0, 0}, //  6
  10.   {&PINA, 0x80, 0, 0}, //  7
  11.   {&PINB, 0x08, 0, 0}, //  3
  12.   {&PINB, 0x20, 0, 0}, //  5
  13.   {&PINB, 0x40, 0, 0}  //  6
  14. };
  15. void KbdSetButton( u08 idx )
  16. {
  17.   kbdButtons[idx].portAddr[ PORT ] &= ~kbdButtons[idx].pinMask;
  18.   kbdButtons[idx].portAddr[ PDIR ] |= kbdButtons[idx].pinMask;
  19. }

В структуре  TKbdButton мы храним адреса портов кнопок, масок пинов кнопок и значения калибровки.

Такая система хороша тем, что кнопки могут быть разбросаны по пинам всего контроллера как угодно и от этого сам цикл установки пинов и их сброса не страдает.

Правда я в цикле сканирования использовал только функцию установки пина в исходное положение, потому как такие функции обычно делаются универсальными, т.е. нам бы пришлось выполнить две команды — установить регистр направления и сам пин потом, но в данном конкретном случае нам надо быстро проделать всю процедуру и только установить регистр направления, поэтому я отказался от вызова функции, а поменял бит напрямую.

На сегодня пока что все.

Позже  расскажу, как эта клавиатура общается с хостом посредством TWI интерфейса, с простонародье именуемом I2C.

До скорого!

2 комментария к “Player Remote — Keyboard”

  1. schemu пишет:

    Очень интересная разработка, хочу себе в машину такой-же плеер. Только у меня Peugeot 307 думаю что сложно будет подружить штатный дисплей с ним.

  2. MasterAlexei пишет:

    Даже не в курсе про пежо. Я про мазды то узнал только по стольку, по скольку себе мазду приобрел, и начал искать инфу в интернете.

    Наверняка там же (в интернете) есть и про пежо и его протоколы Головного Устройства.

Оставить комментарий или два

Пожалуйста, зарегистрируйтесь для комментирования.