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:
- Defina quais PINs você deseja controlar no registro PCMSK
- Habilite o registro de PINs para controle de interrupção de alteração de pinos (PCICR)
- Ativar interrupções
- 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)
fonte
Respostas:
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.
fonte
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.
fonte
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.
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.
fonte