Обсуждаем контроллеры компании Atmel.
Ответить

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

Пн ноя 06, 2023 00:11:19

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

На схеме резистор 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 KiB) Скачиваний: 19

Подскажите что не так

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

Пн ноя 06, 2023 00:39:59

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

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

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

Пн ноя 06, 2023 11:17:39

Используемый алгоритм обработки событий енкодера предельно прост. Механические энкодеры дают много паразитных сигналов, много глюков получается уже в первую неделю/месяц эксплуатации. Измените алгоритм. Вынесите обработку из таймера. Лучше с помощью внешнего прерывания или прерывания при изменении состояния (PCINTx). В вашем случае даже в основном цикле (с флагами) тоже не будет мешать другой обработке.

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

Пн ноя 06, 2023 18:03:10

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