Эмуляция нажатия кнопок энкодером на Attiny24

Обсуждаем контроллеры компании Atmel.
Ответить
ploshka
Родился
Сообщения: 10
Зарегистрирован: Чт мар 29, 2018 08:15:23

Эмуляция нажатия кнопок энкодером на Attiny24

Сообщение ploshka »

Здравствуйте,
Есть андроид-автомагнитола умеющая обучаться кнопкам на мультируле (резистивная матрица).
Есть энкодер (модель EC28A), который хочу к этой магнитоле подключить. Он должен регулировать громкость.
Сделать это хочу через эмуляцию нажатия кнопок. Т.е. кручу влево - на входе магнитолы сопротивление равное сопротивлению при нажатии кнопки Volume-, кручу вправо - Volume+.
Сама логика зашита в Attiny24. Кроме этого, в ней же логика кнопки вкл/откл с памятью.
Аналогичную схему уже делал недавно на PIC10. Там только Volume-, Volume+ делал. И магнитола была андроид, но другой модели. И в принципе работало. Были небольшие проблемы с отлавливанием сигнала магнитолой изначально - не могла она понять, что кнопка нажата. Отрегулировал длину импульса нажатия кнопки (50мс) и стало нормально.
Алгоритм в pic и attiny примерно схожи, реализация разная только.
На Attiny что-то не заводится. Эмуляция в протеусе работает нормально вроде. Но магнитола считает, что поворот влево и вправо - это одно и тоже. Соответственно регулирует громкость только в одну сторону независимо от того куда крутить.
scheme.png
Схему
(69.38 КБ) 104 скачивания
На схеме резистор R2 имитирует общее сопротивление мультируля в состоянии покоя.
При вращении ручки энкодера влево на ноге 7 появляется импульс, который на время замыкает резистор R6 на массу, подключаясь тем самым параллельно к сопротивлению мультируля. Их общее сопротивление равно сопротивлению при нажатой кнопке Volume-.
Аналогично и при вращении вправо - с ноги 6 идет импульс и замыкает R7, Volume+.
Так это должно работать.
Пробовал оптроны заменить на транзисторы, ситуация не изменилась.
Возможно я что-то с инициализацией входов-выходов намудрил в МК. Т.к. в этом деле еще новичок.
Исходники программы и проект в proteus прилагаю.
Спойлер

Код: Выделить всё

#include <tiny24a.h>

#define BUTTON1       PINA.0              // вход первой кнопки
#define OUT1          PORTB.0             // выход состояния первой кнопки
#define VOLUME_UP     PORTA.6             // Выходной импульс "Volume +" (по часовой стрелке)
#define VOLUME_DOWN   PORTA.7             // Выходной импульс "Volume -" (против часовой стрелки)
#define CHATTER_WAIT  10                  // задержка после отжатия кнопки для антидребезга - 100 мсек
#define POWEROFF_WAIT 5000                // время удержания кнопки для отключения
#define IMPULSE_DELAY 200                 // Длительность импульса нажатой кнопки на выходе:
                                          //  указанное кол-во * (1000 мсек / частоту срабатывания прерывания в секунду) = ~ 50 мсек

eeprom unsigned char button1Memory; 
unsigned char triggerChatter1;
unsigned char pressed1;
unsigned int offCounter1;
unsigned int encoderState, encoderStatePrev, 
             volumeUpStateCounter, volumeDownStateCounter, 
             delayUpCounter, delayDownCounter; 
             
void main(void)
{
  DDRA  = (1<<DDA7)   | (1<<DDA6)   | (0<<DDA5)   | (0<<DDA4)   | (0<<DDA3)   | (0<<DDA2)   | (0<<DDA1)   | (0<<DDA0);
  PORTA = (0<<PORTA7) | (0<<PORTA6) | (1<<PORTA5) | (1<<PORTA4) | (0<<PORTA3) | (1<<PORTA2) | (1<<PORTA1) | (1<<PORTA0);

  DDRB  =   (0<<DDB3) | (1<<DDB2)   | (1<<DDB1)   | (1<<DDB0);
  PORTB = (0<<PORTB3) | (0<<PORTB2) | (0<<PORTB1) | (0<<PORTB0);

  // --- Настройка работы прерывания таймера по переполнению ---
  /*
  Предделитель 8, Частота МК 1,200,000 Гц, "тик" таймера 1200000/8 = 150000 раз в секунду. 
  В счетный регистр таймера вносится 0хDA = 216. 
  Cчетчик таймера однобайтовый (max значение = 255), значит до переполнения он инкрементируется 255-218 = 37 раз
  Тогда счетчик таймера переполнится и прерывание будет срабатывать:
  150000 / 37 = 4054 раз в секунду
  */
  TCCR0B = (0<<WGM02)  | (0<<CS02)   | (1<<CS01)   | (0<<CS00);  // инициализация работы таймера предделитель 8 (150кГц)
  TIMSK0 = (0<<OCIE0B) | (0<<OCIE0A) | (1<<TOIE0);               // включение прерывания нулевого таймера по переполнению
  ACSR   =    (1<<ACD) | (0<<ACBG)   | (0<<ACO)    | (0<<ACI)   | (0<<ACIE)   | (0<<ACIS1) | (0<<ACIS0); // Отключить аналаговый компаратор
  TCNT0  = 0xDA;  // счетчик таймера
 
  if (button1Memory)      
   OUT1 = button1Memory;  //  последнее запомненное состояние кнопки 1 на выход
     
  #asm("sei")             // разрешить прерывания
   
  while (1)
  {      
      if (volumeUpStateCounter >= 4) 
      {                            
        VOLUME_UP = 1;
        volumeUpStateCounter = 0;
        delayUpCounter = 0;
      } 
      
      if (volumeDownStateCounter >= 4) 
      {                              
        VOLUME_DOWN = 1;       
        volumeDownStateCounter = 0;
        delayDownCounter = 0;
      }             
      
      
      if (delayUpCounter == IMPULSE_DELAY & VOLUME_UP == 1)
      {
         VOLUME_UP = 0;
      }
      
      if (delayDownCounter == IMPULSE_DELAY & VOLUME_DOWN == 1)
      {
         VOLUME_DOWN = 0;
      } 
                    
  };
}

// Прерыванию по переполнению счетчика таймера. ~ 4000 раз в секунду
interrupt [TIM0_OVF] void timer0_ovf_isr(void) 
{
  TCNT0 = 0xDA;                                // выставить счетчик таймера  
  encoderState = PINA & 0b00110000;            // на pin 4 и 5 - входы фаз A и B энкодера
  delayUpCounter++;
  delayDownCounter++; 
  
  // --- обработка сигнала энкодера ---
  
  if (encoderState != encoderStatePrev)
  {  
    switch(encoderStatePrev)
	{
	  case 32:
	  {
	    if(encoderState == 48) volumeUpStateCounter++;
	    if(encoderState == 0) volumeDownStateCounter++; 
	    break;
	  }
	  case 0:
	  {
	    if(encoderState == 32) volumeUpStateCounter++;
	    if(encoderState == 16) volumeDownStateCounter++; 
	    break;
	  }
	  case 16:
	  {  
	    if(encoderState == 0) volumeUpStateCounter++;
   	    if(encoderState == 48) volumeDownStateCounter++; 
	    break;
	  }
	  case 48:
	  {
	    if(encoderState == 16) volumeUpStateCounter++;
	    if(encoderState == 32) volumeDownStateCounter++; 
	    break;
	  }
    }
    encoderStatePrev = encoderState;            
  } 
  
  // --- обработка нажатия кнопки 1 ---
  if (BUTTON1 == 0)                  
  {
    if (triggerChatter1 == 0 && OUT1 == 0)     // если кнопка нажата только что и до этого выход был отключен 
    { 
        OUT1 = 1;                              // включить выход 
        button1Memory = 1;                     // запоминить состояние кнопки в памяти  
        pressed1 = 1;                          // запоминить, что при нажатии кнопки выход включился
    } 
    else if (pressed1 == 0)                    // если выход не включался
    {
      if (offCounter1 < POWEROFF_WAIT)         // и не истекло время удержания кнопки для выключения выхода
        offCounter1++;                         // увеличивать счетчик удержания отключения
        
      else if (offCounter1 == POWEROFF_WAIT)   // если подошло кнопку удерживали нужное для отключения время
      {
         OUT1 = 0;                             // выключить выход
         button1Memory = 0;                    // запоминить состояние кнопки в памяти  
         offCounter1 = 0xFFFF;                 // счетчик удержания отключения установить в MAX значение, чтобы он был больше времени задержки
      }                     
    }   
    
    triggerChatter1 = 1;                       // включить триггер-защелку    
  }
  else                                         // кнопка1 отжата
  { 
    offCounter1 = 0;                           // сбросить счетчик удержания отключения
    pressed1 = 0;                              // сбросить состояние кнопки1 
    
    if (triggerChatter1)                       // если кнопка1 была нажата
    {
      triggerChatter1++;                       // увеличить счетчик антидребезга
      
      if (triggerChatter1 > CHATTER_WAIT)
        triggerChatter1 = 0;                   // если время антидребезга прошло, то сбросить триггер-защелку
    }
  }   
     
}
EncoderDecoder.zip
исходники
(70.36 КБ) 63 скачивания
Подскажите что не так
Реклама
Martian
Друг Кота
Сообщения: 12867
Зарегистрирован: Сб дек 18, 2021 19:25:32
Контактная информация:

Re: Эмуляция нажатия кнопок энкодером на Attiny24

Сообщение Martian »

на схеме и коллектор и эмиттер оптронов замкнуты на землю, оба оптрона параллельны. Нет никакой нужды в оптронах, если диод и транзистор прям так вот соединены наглухо. В общем, в схеме нет никакого смысла.

Если энкодер механический, то его надо развязывать или удалять, просто параллельно ему не получится, так как один или оба сигнала могут быть замкнуты.
Реклама
veso74
Поставщик валерьянки для Кота
Сообщения: 1916
Зарегистрирован: Сб май 05, 2012 20:24:52
Откуда: KN34PC, Болгария
Контактная информация:

Re: Эмуляция нажатия кнопок энкодером на Attiny24

Сообщение veso74 »

Используемый алгоритм обработки событий енкодера предельно прост. Механические энкодеры дают много паразитных сигналов, много глюков получается уже в первую неделю/месяц эксплуатации. Измените алгоритм. Вынесите обработку из таймера. Лучше с помощью внешнего прерывания или прерывания при изменении состояния (PCINTx). В вашем случае даже в основном цикле (с флагами) тоже не будет мешать другой обработке.
Аватара пользователя
>TEHb<
Друг Кота
Сообщения: 5749
Зарегистрирован: Ср ноя 11, 2009 17:19:30
Откуда: Воронеж
Контактная информация:

Re: Эмуляция нажатия кнопок энкодером на Attiny24

Сообщение >TEHb< »

А ещё у оптронов с биполяром на выходе ненулевое падение напряжения. Стоит заменить их на какой-нибудь 2n7002 или вовсе прямо с порта и управлять.
"Привет!" - соврал он.
Реклама
Эиком - электронные компоненты и радиодетали
Ответить

Вернуться в «AVR»