Uso correto de uma interrupção de troca de pinos

10

Estou tentando usar interrupções de troca de pinos para detectar botões pressionados. Até agora, nunca trabalhei com esse tipo de interrupção e existem alguns problemas, por isso quero ter certeza se esse é o uso correto.

Se obtive a folha de dados correta, as seguintes ações devem ser feitas para usar uma interrupção de alteração de pinos:

  1. Defina quais PINs você deseja controlar no registro PCMSK
  2. Habilite o registro de PINs para controle de interrupção de alteração de pinos (PCICR)
  3. Ativar interrupções
  4. Use o vetor de interrupção correspondente

Projeto: Moodlamp simples, cores controladas através de 4 botões.

Configuração:

  • Atmega168A-PU
  • 4 mini botões
  • MOSFETS para controlar meu LED RGB de 3 watts

Aqui está o código que estou usando, que não está funcionando conforme o esperado:

#include <avr/io.h>
#include <stdint.h>
#include <avr/interrupt.h>
#include <util/delay.h>

#define BUTTON1 (1<<PC5) 
#define BUTTON2 (1<<PC4) 
#define BUTTON3 (1<<PC3) 
#define BUTTON4 (1<<PC2) 

#define GREEN   (1<<PB1) 
#define BLUE    (1<<PB2) 
#define RED     (1<<PB3) 

void init() {

        // enable LED
        DDRB |= GREEN;
        DDRB |= BLUE;
        DDRB |= RED;

        // button pullups
        PORTC |= BUTTON1;
        PORTC |= BUTTON2;
        PORTC |= BUTTON3;
        PORTC |= BUTTON4;

        // pin change interrupts for buttons
        PCMSK1 |= PCINT13;
        PCMSK1 |= PCINT12;
        PCMSK1 |= PCINT11;
        PCMSK1 |= PCINT10;

        // enable pin change for buttons
        PCICR |= PCIE2;

        sei();

}

ISR(PCINT2_vect) {

                PORTB = BLUE;
}


void ledTest() {

                PORTB ^= RED;
                _delay_ms(250);
                PORTB ^= RED;
                _delay_ms(250);
                PORTB ^= RED;
                _delay_ms(250);
                PORTB ^= RED;


                PORTB ^= BLUE;
                _delay_ms(250);
                PORTB ^= BLUE;
                _delay_ms(250);
                PORTB ^= BLUE;
                _delay_ms(250);
                PORTB ^= BLUE;

                PORTB ^= GREEN;
                _delay_ms(250);
                PORTB ^= GREEN;
                _delay_ms(250);
                PORTB ^= GREEN;
                _delay_ms(250);
                PORTB ^= GREEN;
}

int main() {

        init();
        ledTest();

        _delay_ms(500);
        PORTB |= GREEN;

        while(1) {
                _delay_ms(100);
        }
}

Nota: Os botões devem ser devolvidos. Como estou tentando fazer isso passo a passo e não deve interessar em ligar o LED, eu o ignorei aqui.

Pergunta: A maneira como estou tentando usar as interrupções está correta?

Problemas com minha configuração:

  • Os botões 1-3 são totalmente ignorados.
  • Button4 está acionando uma redefinição do atmega

Coisas que eu verifiquei:

  • Os botões não estão de forma alguma conectados ao PIN de redefinição
  • Os botões estão conectados corretamente ao GND se pressionados
  • Os botões não estão conectados ao GND se não forem pressionados
  • Os botões funcionam bem se eu os utilizar sem interrupção, por exemplo:

    if (! (PINC & BOTÃO4)) {PORTB ^ = AZUL; }

  • Cristal externo 16MHZ / cristal interno
  • Quaisquer erros no roteamento
  • Estou usando um capacitor de 100nF entre PWR e GND no atmega
  • VCC (7), GND (8), GND (22), AVCC (20) estão conectados (já que não preciso do AREF, ele não está conectado)
echox
fonte
Você precisa dos sinalizadores PCIE1 (não PCIE2) e PCINT1_vect (não PCINT2)
microtherion
Porquê PCIE1? Estou usando o registro C, então, se eu contar, seria A (PCIE0), B (PCIE1), C (PCIE2)? Enfim, eu tentei com PCIE1 nad PCINT1_vect e não há reação se eu pressionar os botões.
Echox 11/09/13
11
Pode ser um pouco arriscado assumir a ortogonalidade em tais tarefas. Nesse caso em particular, você estaria quase correto, exceto que o ATmega168 não possui uma porta A. Em qualquer caso, consultei a folha de dados e a pinagem. Outra dica foi que você estava usando o PCIE2, mas configurando bits no PCMSK1; isso não pode estar certo (infelizmente, não sei por que seu esboço revisado ainda não está funcionando).
microtherion
Obrigado, eu também entendo que a combinação de depuração software que depende de hardware de construção de auto que não é fácil ;-)
echox

Respostas:

14

As interrupções de troca de pinos geralmente não são uma boa maneira de detectar as ações dos botões. Isso ocorre porque os botões mecânicos pulam e você recebe muitas interrupções sem sentido e, em seguida, ainda precisa fazer a devolução.

Uma maneira melhor é ter uma interrupção periódica, como a cada 1 ms (taxa de 1 kHz). Como a maioria dos processadores demora muito tempo, a fração do tempo gasto na interrupção será pequena. Simplesmente experimente o estado do botão a cada interrupção. Declare um novo estado do botão se você viu o novo estado 50 ms seguidos. 50 ms é mais longo que a maioria dos botões, mas ainda é curto o suficiente para que os humanos não notem ou se importem com o atraso.

Observe que dessa maneira você também pode lidar com vários botões na mesma interrupção periódica de 1 ms. Tudo que você precisa é de um contador para cada botão.

Mais sobre o tempo de debounce:

Ocasionalmente, como neste caso, alguém diz que 50 ms é muito demorado. Isso não é verdade para botões comuns pressionados por seres humanos. Pode ser um problema talvez em aplicativos muito críticos em termos de tempo, como um cronômetro, mas até agora não encontrei nenhum. Eu testei isso no início dos anos 80, e muitas outras pessoas também.

É verdade que o tempo típico de retorno do botão é de cerca de 10 ms, com praticamente todos os ajustes de 25 ms. O fator limitante no tempo de debounce é a percepção humana. 50 ms é um pouco menor do que quando as pessoas começam a perceber um atraso quando não o procuram. Mesmo assim, leva muito mais tempo para ser irritante. Em alguns casos, é possível que um ser humano detecte uma diferença entre um atraso de 50 e 0 ms, se estiver procurando especificamente , mas isso é bem diferente de apertar um botão e ver algo acontecer e não pensar no atraso.

Portanto, 50 ms é um bom tempo de retorno, porque o atraso está abaixo do limite de percepção em aplicações comuns, muito abaixo do limite de aborrecimento e bem acima do tempo de retorno da maioria dos comutadores. Eu encontrei interruptores que saltaram por quase tanto tempo, então é melhor ir até o limite de percepção, já que não há nada a perder.

Eu fiz muitos produtos com botões de firmware desativado, usando um tempo de recuperação de 50 ms. Nem uma vez um cliente mencionou sequer notar um atraso. Todos eles aceitaram os botões como funcionando sem problemas.

Olin Lathrop
fonte
11
50ms pode ser muito longo para alguns casos (geralmente, 10-20ms é o limite da percepção humana, e isso deve ser suficiente para rebater), mas o método descrito aqui é o caminho a percorrer.
Laszlo Valko 9/09/13
11
@ Laszlo: Não, 50 ms não são muito longos para o caso comum. Veja a adição à minha resposta.
precisa saber é o seguinte
Eu tentei 50ms, o que funciona bem para mim :-) Ainda estou curioso para saber por que a interrupção da troca de pinos não está funcionando (além das coisas que saltam), mas funciona :-) Obrigado.
Echox 11/09/13
1

As interrupções de alteração de pinos são a melhor maneira de debounce do que a pesquisa. A interrupção geralmente passa por alguma lógica, como um D-Flip Flop ou D-Latch. Embora isso seja verdade, é mais difícil implementar essa rotina de debounce com compiladores de nível superior. Depois que a interrupção ocorre, o sinalizador de interrupção não é limpo e a ativação de interrupção é limpa até que ocorra um atraso. Depois que o atraso ocorre, o estado do pino é verificado e, se ainda estiver no estado especificado que acionou a interrupção, o estado do botão é alterado, o sinalizador de interrupção é limpo e a ativação de interrupção é definida. Se não estiver no estado que causou o início, a habilitação de interrupção é definida e o estado permanece o mesmo. Isso libera o processador para outras tarefas. Periódico interrompe o tempo perdido no programa.

Jim Vernay
fonte
-1

"As interrupções de troca de pinos geralmente não são uma boa maneira de detectar as ações dos botões."

Errado. PC INT é a melhor opção. Se você usar a pesquisa para verificar o estado de um botão, nada será executado na maioria das vezes. Você perde muito tempo precioso na CPU. O PC INT permite que as ações sejam executadas somente mediante solicitação.

"Isso ocorre porque os botões mecânicos pulam e você recebe muitas interrupções sem sentido, e então ainda precisa fazer as devoluções."

Correto sobre quicar. No entanto, você NUNCA devolve um botão / chave dentro de uma rotina de interrupção (mesmo motivo: perda de tempo da CPU). Os ISRs devem ser realmente curtos e eficientes, em termos de código. Basta usar a depuração de hardware. Mantenha seu software limpo!

A depuração de hardware é mais conveniente, consulte aqui / RC debouncing + Schmitt trigger para referência. Eu o usei inúmeras vezes com o PC INT, ele nunca falhou.

Então, sim, você pode (e deve) usar o PC INT para obter um estado de botão. Mas você também precisa usar a devolução adequada de hardware.

Nelson
fonte
2
A rejeição de software é uma abordagem válida e, na maioria das vezes, a pequena sobrecarga extra da CPU é irrelevante. Dizer que você normalmente devolve o hardware é questionável, na melhor das hipóteses. Dizer que você precisa usar a depuração de hardware em todos os casos é totalmente errado.
precisa saber é o seguinte
Na maioria das aplicações, o controlador está ocioso na maior parte do tempo, executando o loop principal. Além disso, o tempo de CPU necessário para executar uma verificação do estado de E / S e aumentar potencialmente uma variável é mínimo. A implementação de uma simples conversão de software é quase gratuita, o hardware custa dinheiro. E não ria de alguns centavos, a montagem também custa dinheiro e se você administra volumes médios a altos de um produto, isso não é desprezível. É verdade que o tempo de ISR deve ser curto, mas isso dificilmente é um argumento neste caso. Provavelmente é mais crítico se o PC INT ISR disparar 50 vezes seguidas devido a quedas.
Rev1.0
@ Nelson, 'perda de tempo da CPU' é importante em alguns aplicativos e não em muitos outros. Você deve qualificar sua resposta para situações em que o tempo da CPU é crítico.
usar o seguinte comando