Diferenças entre interrupções e amostragem para botão de hardware?


8

Eu tenho um botão de hardware conectado a uma interrupção, mas meu problema é que ele salta um pouco, fazendo com que o botão não seja confiável. Eu acho que muitas dessas questões seriam resolvidas com a amostragem no loop principal, mas isso parece tecnicamente errado.

As interrupções são mais apropriadas para comunicação intra-circuito ou as interrupções também são apropriadas para comutadores de hardware? Em caso afirmativo, quais técnicas de devolução posso usar?

Tentei manter uma variável de timer e compará-la com a hora atual, atrasos e outras técnicas. Parece que os saltos são tão rápidos que não importa.



Não há nada errado com a amostragem no loop principal, se você reagir no loop principal. As interrupções são mais apropriadas se você deseja reagir de forma assíncrona. Às vezes você faz, e às vezes você não.
Eugene Ryabtsev 18/11/19

A melhor maneira de rebater é um simples filtro passa-baixo.
Lucas92

Respostas:


8

A devolução é um FAQ. Você deve ser capaz de encontrar ... quase um número ilimitado de páginas da web sobre o assunto. Smith também comentou sobre o PDF amplamente lido de Jack Ganssle sobre o assunto. E com todas essas respostas, você tem métodos de hardware e software.

Vou acrescentar a essa "literatura" apenas um pouco falando principalmente sobre idéias que ainda não foram abordadas bem. Mas antes disso, um ou dois pontos:

  1. A rejeição no hardware analógico pode obter resultados que você não pode obter com um switch "observado" apenas digitalmente periodicamente, pesquisando ou mesmo eventos de alteração de pinos do hardware. Mas você pode fazer "bem o suficiente" para todos os efeitos, digitalmente. Atualmente, quase ninguém usa soluções de depuração analógica externa. Mas eu usei tudo, desde alongamento de pulso usando one-shots (74121) até técnicas mencionadas por Jack Ganssle aqui .
  2. Para aqueles que fazem apenas programação incorporada e não estão interessados ​​em aprender sobre eletrônica, a rejeição de switches é provavelmente um dos dois conjuntos de habilidades básicas necessárias. LEDs de operação é provavelmente o outro. E com isso, não quero dizer ter apenas uma habilidade nisso. Quero dizer, ser capaz de fazê-lo de várias maneiras. Então, você realmente fazer necessidade de totalmente apreender o que Jack Ganssle escreve sobre, e ainda mais, em relação switches.

Como eu mencionei o alongamento de pulso usando um 74121 e como Jack Ganssle não o menciona, e também ninguém aqui, eu também posso fornecer esse link adicional como uma leitura adicional sugerida sobre o uso do 74121 ou 555 como one-shot temporizador para interrupção de interruptores.


Agora, vamos fazer isso através da observação com um microcontrolador.

Normalmente, uso uma máquina de estado para lidar com a devolução. Isso quase sempre é acionado por um timer regular de "pulsação" que eu defino para cerca de , sempre que possível. (Geralmente, NÃO uso eventos de interrupção acionados por borda por vários motivos.)8ms

A máquina de estado fica assim:

esquemático

simular este circuito - esquemático criado usando o CircuitLab

O valor de DEBOUNCED para o comutador pode assumir os valores "inativo", "ativo" e "desconhecido". Dessa forma, você pode garantir que o software aguarde até que o valor do comutador seja estabelecido após a inicialização. Mas geralmente, eu não me importo com isso. Substituo o valor "desconhecido" por algum valor padrão e, em vez disso, apenas uso um sistema de valores binários.

A máquina de estados é inserida definindo primeiro o valor debitado como seu padrão e, em seguida, inserindo o estado "ALTERANDO" da máquina de estados. A cada intervalo de tempo (normalmente se eu puder me safar), lerei o valor atual da chave e executarei uma atualização do estado atual e, possivelmente, do valor debitado. Então eu apenas saio. O código de alto nível acessa apenas o estado de devolução.8ms

Se isso for importante para mim, também posso manter um estado debitado prévio. Nesses casos, ao atualizar o próprio estado debitado, primeiro copio esse estado para um 'estado debitado anterior'. Em seguida, posso usar o par de valores para determinar se houve uma transição rebocada. Às vezes, não me importo com transições. Ás vezes eu faço. Então depende. Mas em todos os casos, eu só quero saber sobre as transições que foram rebatidas. Eu nunca se preocupam com runt transições. Portanto, o código de alto nível nunca usa nenhum estado interno que a máquina de estado usa para seu próprio trabalho.

Uma das coisas boas sobre esse método é que posso renunciar a uma porta inteira de switches de uma só vez. E eu posso fazer isso sem uma única ramificação no código de interrupção também. Isso significa um código de depuração muito rápido e curto para a largura da porta do microcontrolador (geralmente com 8 bits de largura). Um exemplo do Atmel AT90 mostra como isso é alcançado usando um evento de interrupção Timer0:

.equ    SWPORTPINS  =   PINB
.def    SwRawCurr   =   r4
.def    SwRawPrev   =   r5
.def    SwState     =   r6
.def    SwDebCurr   =   r7
.def    SwDebPrev   =   r8

            ; Debounce the input switches.

                mov     SwRawPrev, SwRawCurr
                in      SwRawCurr, SWPORTPINS
                mov     Timer0Tmp1, SwRawCurr
                eor     Timer0Tmp1, SwRawPrev
                mov     Timer0Tmp0, Timer0Tmp1
                or      Timer0Tmp1, SwState
                mov     SwState, Timer0Tmp0
                mov     Timer0Tmp0, Timer0Tmp1
                com     Timer0Tmp0
                and     Timer0Tmp1, SwDebCurr
                and     Timer0Tmp0, SwRawCurr
                or      Timer0Tmp1, Timer0Tmp0
                mov     SwDebPrev, SwDebCurr
                mov     SwDebCurr, Timer0Tmp1

Agora, este exemplo mostra a oferta completa, incluindo os valores anteriores e atuais da opção debitada. E ele executa todas as transições de estado necessárias também. Não mostro a inicialização deste código. Mas o exposto acima mostra o quão fácil é a máquina de estado e como é necessário pouco código para fazê-lo. É bastante rápido e simples e não requer ramificação (o que às vezes envolve ciclos adicionais e espaço de código adicional).


Prefiro usar o tempo porque testes longos e longos com várias pessoas diferentes usando equipamentos nos quais trabalhei no passado me levaram até lá. Eu tentei períodos mais longos e, quando faço isso, começo a fazer as pessoas me dizerem que a "capacidade de resposta" não é "rápida" o suficiente. (Hoje em dia, com crianças crescendo em jogos em tempo real "atirar neles", posso até reduzi-lo ainda mais. Eles vão reclamar amargamente dos atrasos leves causados ​​pelas modernas TVs digitais na configuração e exibição de um quadro.)8ms

Algumas pessoas têm sentimentos muito claros sobre o quão preciso e responsivo um sistema deve ser. Nítido e responsivo significa amostrar com mais frequência, não menos. Mas, pessoalmente, considero aceitáveis períodos de observação. ( Porém, não acho que os tempos mais longos sejam bons o suficiente para mim.)20ms

Observe que a máquina de estado que mencionei deve primeiro entrar no estado SETTLED e, em seguida, permanecer lá por mais um tempo de amostra antes que o valor de DEBOUNCED seja atualizado. Então, apertar um botão e segurá-lo, mesmo nas melhores circunstâncias, exigirá as seguintes transições:

  1. mude de SETTLED para CHANGING
  2. mude de ALTERAR para CONFIGURADO
  3. permanecer em SETTLED, atualizando DEBOUNCED

Portanto, um novo estado debocado requer um período mínimo de 3 períodos de amostra para ser alcançado.

Um botão de pressão exigirá pelo menos 6 tempos de amostra para passar de inativo para ativo e depois voltar para inativo.


Mencionei os detalhes acima para que fique absolutamente claro que um tempo de amostragem de significa que está entre passar de inativo a um resultado reconhecido ativo debitado. E serão necessários outros antes que o estado possa retornar para inativo. Isso é no mínimo para passar por um ciclo inteiro de botões.8ms16ms<t24ms24ms40ms<t48ms

O uso de tempos de amostragem mais longos terá períodos correspondentemente mais longos. O uso de eu mencionei como "aceitável" para mim já significa algo em torno de por um ciclo completo de botões . E isso está ficando diretamente para dentro da área onde as pessoas não tendem a aviso prévio. Eu certamente não gosto da "sensação" se ficar mais tempo do que isso.20ms100ms<t120ms

Se você seguir esse caminho, não seja descuidado ao usar tempos de amostragem mais longos. Se você precisar, acho que também deve fazer muitos testes com usuários / consumidores.

E se você estiver desenvolvendo código para um teclado de digitação, use tempos mais curtos. O recorde para um datilógrafo foi estabelecido décadas atrás em 217 wpm. Isso resulta em cerca de uma chave a cada . Datilógrafos como esse estão pressionando várias teclas em uma ordem controlada. Para obter um bom desempenho para datilógrafos muito rápidos que usam um sistema de comutação de relé com palheta úmida de mercúrio, descobri que funcionava bem.45ms2ms


os tempos de rejeição variam de 0 para comutadores de mercúrio a "poucos" tipos de ms para comutadores micro-táteis e 30ms para comutadores desajeitados, portanto 8ms é um bom número, considerando o aumento do tempo de rejeição com o envelhecimento.
Tony Stewart Sunnyskyguy EE75

@ TonyStewart.EEsince'75 Eu escolhi fazer testes extensivos com usuários usando equipamentos com uma variedade de tipos diferentes de comutadores, e o valor de 8 ms vem de uma destilação de todo esse trabalho. (Não me preocupei muito com a "teoria", pois a prática de criar e fazer comutadores e sua grande variedade fazia com que a coleta e a análise desses dados parecessem assustadoras.) Sempre uso 8 ms, quando possível, pois parece para ser o ponto doce determinado software de gravação de longa experiência que apenas obras e onde pós-venda reclamações vão para um exato zero (nesse ponto, de qualquer maneira.)
jonk

@ TonyStewart.EEsince'75 A propósito, esse teste INCLUI o uso de relés de palheta molhados de mercúrio como parte de chaves usadas em teclados (que, eu acho, não parecem mais ser feitas.) Nesses casos, no entanto, Eu vou para 1-2 ms de amostragem (depende da unidade.) #
1116

Aquela luz laser de jardim que mencionei uma vez atrás ... possui interruptores de controle remoto por membrana tátil com baixo tempo de rejeição, mas o programador os fez alternar a uma taxa de 10Hz, para que seja necessário liberá-los em <100ms, caso contrário a energia será desligada. .. em outra nota .. O teclado de piano Yamaha é extremamente rápido e suporta a substituição de 10 teclas, enquanto apenas o teclado IBM PC original suporta a verdadeira substituição de ponta. Desde então, todos os teclados são os primeiros toques a serem de ponta e, em seguida, a rolagem de borda posterior, que é uma PITA para habilidades de digitação ruins como a minha #
Tony Stewart Sunnyskyguy EE75 18/16/16

@ TonyStewart.EEsince'75 Essa área de amostragem de switch é um ponto delicado. O advento de micros baratos, com zero rejeição externa e opção de quem sabe o que foi aplicado, adicionado à ignorância do programador incorporado, significou que realmente encontro problemas com quase todos os instrumentos incorporados com teclado ou botão de pressão. Todos eles funcionam muito bem, na minha opção. E acho que é principalmente porque os programadores têm pouca ou nenhuma experiência, apenas 'pesquise no Google e aplique' sem pensar. Às vezes, salgando seu código com pontos de pesquisa aleatórios, até. É lixo. Perturbador. É fácil acertar.
Jonk

5

O debounce pode ser feito no software, mascarando os IRQs para o tempo de ressalto ou no hardware, adicionando um capacitor de retenção com seu tempo de ressalto RC = T> variando de 1 a 15ms, dependendo do tamanho do comutador.

  • por exemplo, 100k pullup e 0,1μF através do switch = 10ms @ 63% ou ~ 8ms a 50% Vdd ou se estiver usando a porta Schmitt Trigger @ 1,33V = Vil a partir de 5V ou ~ 73% V + ~ 12ms

4

Para fazer um retorno do SW, registre o registro de data e hora do evento atual e verifique se há atraso no último evento válido:

#define DELAY_DEBOUNCE       150

uint32_t    __ts_lastpress = 0;

ISR(some_vector)
{
    uint32_t    now = millis(); // some timer tick counter

    if ( now - __ts_lastpress < DELAY_DEBOUNCE )
        return; // ignore it

    __ts_lastpress = now;
    // do the job here
}

UPD: com poucas modificações, você pode registrar dois cliques:

#define DELAY_DEBOUNCE       150
#define DELAY_DOUBLE_CLICK   600

uint32_t    __ts_lastpress = 0;

ISR(some_vector)
{
    uint32_t    now = millis(); // some timer tick counter

    if ( now - __ts_lastpress < DELAY_DEBOUNCE )
        return; // ignore it

    // do the job here
    if ( now - __ts_lastpress < DELAY_DOUBLE_CLICK )
    {
        // it is double click
    }
    else
    {
        // it is single click
    }

    __ts_lastpress = now;
}

2

As interrupções são definitivamente ótimas também para switches de hardware. Ao usar interrupções, você evita um grande desperdício de recursos e possivelmente energia, principalmente se estiver lidando com dispositivos alimentados por bateria.

Além disso, à medida que seu código aumenta, você verá que é ainda mais fácil implementar as interrupções para os botões do que pesquisá-los no loop principal.

Quanto à sua devolução, provavelmente é um problema de codificação. Eu geralmente uso um timer de ~ 10ms para rebater, enquanto checo a liberação do botão. Também desative temporariamente a interrupção do botão enquanto você a renuncia, para que a rotina de interrupção não seja executada várias vezes.

Se você ainda estiver com problemas, poste o código aqui, para que possamos ajudá-lo.


1

Isso é bem parecido com a resposta de Tony Stewart, mas acho que poderia ser expandido.

O esquema superior é para uma interrupção na borda baixa ou na borda descendente. O esquema inferior é para uma interrupção na borda alta ou ascendente.

esquemático

simular este circuito - esquemático criado usando o CircuitLab

pessoalmente, dado o custo de um capacitor, vale a pena usá-lo simplesmente, em vez de me preocupar se o debounce do meu software é incorreto.

Observe que, como Tony Stewart disse, a constante de tempo neste circuito é de 10ms ou . Vai levar de três a cinco constantes de tempo (dependendo da sensibilidade do seu microcontrolador para o botão se redefinir, portanto, se o seu microcontrolador tiver problemas com a repetição da função de interrupção, isso pode ser a causa, e talvez você precise ajuste a tampa / resistor para que a interrupção não ocorra várias vezes (ou seja, apenas se a interrupção estiver configurada para funcionar com um sinal alto ou baixo, e não com a borda ascendente ou descendente).(RC10kΩ1μF)

Relacionadas à depuração de hardware


1
Qualquer uma das versões funciona para as bordas + ve ou -ve, especialmente se o pino de interrupção tiver uma característica de entrada no estilo schmitt (muitos fazem). Ambos os SW1 e SW2 experimentam uma oscilação atual ao fechar. Alguns botões de pressão de botão de carbono podem fornecer resultados diferentes dos botões de pressão de domo metálico.
glen_geek

1

Os seres humanos são lentos, não precisamos da atenção imediata de um micro que esteja na faixa de microssegundos.

Naturalmente, essa não é a única nem a maneira correta de fazê-lo, mas geralmente acho mais sensato configurar um temporizador (muitos micros possuem ticks de sistema) para acionar uma interrupção em intervalos fixos e mudar o estado do pino para um variável para o código examinar posteriormente. Você acaba com um var cheio de cinzas durante o salto

10010110 ash

mas em determinados momentos, você obtém esses 4 valores:
01111111 borda ascendente recém-lançada
11111111 botão em estado estacionário acima
10000000 borda descendente recém-lançada
botão 00000000 em estado estacionário desativado

Na maioria das vezes, porém, eu apenas uso um contador que é redefinido durante o salto. É rápido, testado e fácil de fazer.
Se falhar, tento algo mais inteligente no documento de Ganssle que outros sugeriram!

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.