arduino interrup coes
TRANSCRIPT
Interrupções no Arduino: uma breve análise
Escrito por Gabriel Yoshiaki Hotta em 2013/12/17, sugestões, correções, comentários, etc,
podem ser enviadas para [email protected]
Requisitos Este artigo supõe que o leitor já possui o conhecimento básico sobre interrupções: o que é
uma interrupção, como trata-las (attachInterrupt() e detachInterrupt()).
Também é recomendado o conhecimento básico da linguagem Assembly para melhor
compreensão, embora não seja estritamente necessário.
Introdução Interrupções no Arduino são simples de usar e na maioria das vezes não precisamos nos
preocupar com muitos detalhes.
Entretanto, quando passam a ser utilizadas várias interrupções, características importantes e
que não são tão evidentes passam a possuir grande relevância. Uma delas é a prioridade das
interrupções, bem como a necessidade de interrupções aninhadas (nested interruptions).
Pouco material sobre o assunto está disponível, e de forma dispersa e fragmentada. Este artigo
tenta reunir informações sobre o tema, boa parte do material se baseia na
tradução/interpretação do Datasheet.
Terminologia Capturar interrupção: tratamento de um evento associado a uma interrupção.
Condição de interrupção: circunstancias na qual uma interrupção deve ser efetuada.
Habilitação de interrupção: circunstancias na qual o processador é capaz de efetuar a captura
de uma interrupção.
Rotina de interrupção: conjunto de comandos/instruções que serão executados quando
houver captura de interrupção.
Os tipos de interrupção Existem basicamente dois tipos de interrupção. O primeiro tipo é capturado por um evento
que seta o respectivo bit na "Flag Interrupt". Quando isso acontece a rotina de interrupção
associada é executada e o bit na "Flag Interrupt" é limpo (zerado). Se durante a execução da
rotina de interrupção houver uma nova condição de interrupção, o bit será novamente setado
e manterá seu valor até a interrupção ser novamente habilitada (também é possível limpar o
bit explicitamente dentro da rotina, impedindo a execução posterior da rotina de interrupção).
O segundo tipo de interrupção é capturado tão logo a condição de interrupção esteja
presente, elas não utilizam necessariamente a "Flag Interrupt". Se a condição de interrupção
surgir e desaparecer antes da interrupção estar habilitada a rotina de interrupção associada
não será executada.
Se duas ou mais condições de interrupção ocorrerem simultaneamente haverá uma prioridade
de execução entre as interrupções.
Ao sair de uma rotina de interrupção o AVR irá retornar ao programa principal e executar uma
instrução antes de executar a próxima rotina de interrupção.
Ou seja, se houver duas condições de interrupção, a rotina de interrupção da primeira (com
maior prioridade) será executada, em seguida uma, e somente uma, instrução do programa
principal será executado e apenas depois disso a segunda rotina de interrupção será
executada.
O "Status Register" não é automaticamente armazenado ao entrar em uma rotina de
interrupção, nem é restaurado ao sair da Rotina de interrupção. Cabe ao programador faze-lo
caso seja do seu interesse.
Prioridade de interrupções A prioridade de execuções é dada pela posição no "Interruption Vector", quanto menor a
posição ocupada, maior a prioridade. Para o ATmega2560 (usado no Arduino Mega 2560, em
outros modelos deve-se consultar o respectivo Datasheet) temos a seguinte tabela:
Interrupções aninhadas (nested interruptions) Quando uma interrupção é capturada a flag de interrupção global "SREG" é zerada,
desabilitando todas interrupções.
Ao sair de uma rotina de interrupção é executada a instrução "RETI" que reabilita as
interrupções.
Este é o comportamento desejado na maioria das vezes, pois espera o termino de uma rotina
de interrupção para, em seguida, executar uma outra rotina de interrupção.
Entretanto, se o programador desejar pode-se reabilitar a captura de interrupção dentro de
uma rotina de interrupção. A isso dá-se o nome de interrupções aninhadas (nested
interruptions).
Para habilitar interrupções aninhadas o programador deve explicitamente utilizar a instrução
responsável por habilitar interrupções: sei().
Note ainda que o uso incorreto pode acarretar em infinitas interrupções recursivas.
Ainda há outro porém, mesmo utilizado a instrução "sei()" no início de uma rotina de
interrupção ainda permanecem algumas instruções geradas pelo compilador antes da
instrução Assembly "SEI", ou seja, algumas poucas instruções ainda são executadas com a
interrupção desativada.
O código em C
ISR(TIM0_OVF_vect) { sei(); //do nothing }
Gera o seguinte código assembly:
PUSH R1 ; \ PUSH R0 ; | IN R0, 0x3F ; | interrupcao ainda desativada PUSH R0 ; | CLR R1 ; / SEI ; <<-- reabilita interrupcoes globais POP R0 OUT 0x3F, R0 POP R0 POP R1 RETI ; retorna ao programa principal
Melhor forma de utilizar a instrução SEI A avr-libc permite a inserção da instrução "SEI" antes de qualquer instrução na rotina de
interrupção através da seguinte declaração:
ISR(XXX_vect, ISR_NOBLOCK) {
}
onde XXX_vect é uma interrupção valida no vetor de interrupções.
Por exemplo, o seguinte código C
ISR(TIM0_OVF_vect, ISR_NOBLOCK) { //do nothing }
Gera o código Assembly
SEI ; <<-- habilita interrupcoes globais PUSH R1 PUSH R0 IN R0, 0x3F PUSH R0 CLR R1 POP R0 OUT 0x3F, R0 POP R0 POP R1
RETI ; retorna ao programa principal
Fontes Datasheet do ATmega2560
http://www.carlosdelfino.eti.br/eletronica/item/89-prioridade-das-
interrup%C3%A7%C3%B5es-em-microcontroladores-atmega
http://stackoverflow.com/questions/5111393/do-interrupts-interrupt-other-interrupts-on-
arduino
http://www.nongnu.org/avr-libc/user-manual/group__avr__interrupts.html
http://ucexperiment.wordpress.com/2013/05/20/nested-interrupts/