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:
- 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 .
- 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.)8em
A máquina de estado fica assim:
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.8em
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.)8em
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.)20em
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:
- mude de SETTLED para CHANGING
- mude de ALTERAR para CONFIGURADO
- 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.8em16ms <t≤24em24em40.ms <t≤48em
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.20em100ms <t≤120em
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.45em2em