Seções do site
Escolha dos editores:
- O melhor programa de reconhecimento de fala russo Reconhecimento de fala offline como desativar
- Como descobrir o VID, PID de um pendrive e para que servem esses números de identificação?
- Huawei P8Lite - Especificações
- Como desbloquear um telefone Xiaomi se você esqueceu sua senha
- Apptools: como ganhar dinheiro jogando
- Lenovo Vibe K5 Plus - Especificações Especificações de áudio e câmera
- Decidiu mudar do Windows para o Mac?
- Como usar o Google Fotos, visão geral das funções de login do Google Fotos
- Sistema de pagamento Payza (ex-Alertpay) Payza faça login em sua conta pessoal
- Como abrir o APK e como editar?
Anúncio
Interrupções no atmega8. Curso de treinamento |
Uma das vantagens do microcontrolador ATmega8 é sua ampla gama de diferentes interrupções. Interromperé um evento em cuja ocorrência a execução do programa principal é suspensa e é chamada uma função que trata uma interrupção de um determinado tipo. As interrupções são divididas em internas e externas. As fontes de interrupções internas incluem módulos microcontroladores integrados (temporizadores, transceptor USART, etc.). As interrupções externas ocorrem quando sinais externos chegam aos pinos do microcontrolador (por exemplo, sinais nos pinos RESET e INT). A natureza dos sinais que levam à ocorrência de uma interrupção é definida no registrador de controle MCUCR, em especial nos bits - ISC00 (bit 0) e ISC01 (bit 1) para entrada INT 0; ISC10 (bit2) e ISC11 (bit3) para entrada INT1. No microcontrolador ATmega8, cada interrupção possui seu próprio vetor de interrupção(endereço no início da área de memória do programa onde está armazenado o comando para saltar para a rotina de interrupção especificada). No mega8, todas as interrupções têm a mesma prioridade. Se ocorrerem várias interrupções simultaneamente, a interrupção com o número de vetor mais baixo será processada primeiro. Vetores de interrupção em Atmega8
Gerenciamento de interrupções4 registros são responsáveis por gerenciar interrupções no ATmega8: GIMSK(também conhecido como GICR) - proíbe/habilita interrupções com base em sinais nas entradas INT0, INT1 GIFR- gerenciamento de todas as interrupções externas TIMSK, TIFR- gestão de interrupções de temporizadores/contadores Registro GIMSK(GICR) INTFx=1: ocorreu uma interrupção na entrada INTx. Ao entrar na rotina de tratamento de interrupções, o INTFx é automaticamente redefinido para o estado de log. 0 Registro TIMSK
TOIE1=1: Interrupção de overflow T/C1 habilitada OCIE1A=1: interrompe quando o registro de comparação A corresponde ao conteúdo do contador T/C1 habilitado OCIE1B=1: interrompe quando o registro de comparação B corresponde ao conteúdo do contador T/C1 habilitado TICIE=1: interrupção habilitada quando a condição de captura for atendida TOIE0=1: Interrupção de overflow T/C0 habilitada Registro TIFR
TOV1=1: Ocorreu estouro de T/C1 FCO1A=1: registro de comparação A coincidiu com o conteúdo do contador T/C1 permitido FCO1B=1: registro de comparação B corresponde ao conteúdo do contador T/C1 permitido CIF=1: condições de captura atendidas TOV0=1: Ocorreu estouro de T/C0 Ao entrar na sub-rotina de tratamento de interrupções, o sinalizador de registro TIFR correspondente à interrupção é automaticamente redefinido para o estado de log. 0 As interrupções só funcionam quando as interrupções gerais estão habilitadas no registro de status SREG (bit 7 = 1). Quando ocorre uma interrupção, este bit é automaticamente redefinido para 0, desativando interrupções subsequentes. Neste exemplo, o pino INT0 está habilitado no modo de entrada pull-up. Quando o pino é curto-circuitado com o aterramento por meio de um botão, a lógica 0 é colocada nele (a borda do sinal cai da tensão de alimentação para 0) e o manipulador de interrupção é acionado, acendendo a lâmpada conectada ao pino zero da porta B lâmpada vaziaON() interromper vazio ext_int0_isr(void) DDRD.2=0; SREG|= (1 enquanto(1) ( O exemplo acima também mostra como os vetores de interrupção são definidos no Code Vision AVR (interrupt void ext_int0_isr(void)). Os vetores de interrupção são definidos de forma semelhante para outros casos: EXT_INT02 Em primeiro lugar, o que é uma interrupção? Com isso, vamos encerrar a teoria e passar à prática (embora haja mais teoria a seguir). Como você provavelmente já adivinhou, escreveremos uma interrupção (gerada por um botão) que acenderá e desligará o diodo. #incluir Vamos concordar que a interrupção (fisicamente) não ligará ou desligará a alimentação da perna do controlador (como já considerei que isso é feito), mas apenas alterará o sinalizador. Em certos valores dos quais o diodo ligará e desligará. Número interno = 1; Agora vamos declarar uma interrupção: ISR(SIG_INTERRUPT1)( if (num == 1) num = 0; senão num = 1; ) Como você pode ver, o chamado vetor de interrupção é indicado entre colchetes da macro. Este vetor informa ao compilador para qual entrada a interrupção será gerada. Para INT1 é SIG_INTERRUPT1. Para um ADC, por exemplo, é SIG_ADC. (a lista completa está perfeitamente descrita no livro “Shpak Yu.A. Programação em C para microcontroladores AVR e PIC.”) Sei(); // em geral GIMSK |= (1< Feito isso, você precisa configurar o comportamento da interrupção. Ele pode ser gerado de diferentes maneiras. Acho que você entenderá como traduzir isso. MCUCR = (0< Agora vamos definir toda a porta C como saída: DDRC = 0xff; //porta C - saída Bem, isso já deve estar claro: While (1)( if (num == 1) PORTC |= 1; // liga a primeira saída C else PORTC &= ~1; // desliga a primeira saída C _delay_ms(100); // espera 100ms ) Não há necessidade de esperar. Além disso, isso reduz o desempenho. Mas eu quero assim. #define F_CPU 8000000UL // 8MHz #include Compilamos hexadecimal e montamos o circuito no Proteus. Gostamos da função de interrupção ao alterar a posição do botão. Muitas vezes acontece que um microcircuito deve funcionar, funcionar silenciosamente e, para algum evento, largar tudo e fazer outra coisa. E então - retorne à tarefa original novamente... É como um relógio, por exemplo - mostra a hora até chegar a hora do despertador. E parece que não há influências externas - apertar um botão, reiniciar - e o microcircuito muda sozinho. Isso pode ser implementado por meio de interrupções - sinais que informam o processador sobre a ocorrência de um evento. Aqui está um exemplo da vida cotidiana - você está sentado na cozinha, tomando chá com geléia de framboesa e guloseimas deliciosas, esperando pelos convidados. Como você sabe que alguém chegou? As opções são duas: ou fazemos uma pausa na geléia, quero dizer, no chá, a cada cinco minutos e corremos para ver se tem alguém na porta, ou compramos uma campainha e esperamos calmamente em local aquecido até que alguém toque. Então, quando um convidado liga, isso é um evento. Conseqüentemente, paramos e corremos para a porta. Então, o microcircuito tem interrupções. E não apenas um. As interrupções são divididas em externas - são acionadas em uma determinada tensão em alguns pinos do microcircuito (INT0, INT1 e às vezes também em toda a porta PCINT) - e internas - quando o contador transborda, o temporizador watchdog é acionado, ao usar USART, quando um comparador analógico, ADC e outros periféricos são interrompidos. Assim, surge um problema prioritário. É como se ainda estivéssemos sentados tomando chá, mas eles não só tocassem a campainha, mas também ao telefone... E você não pode ficar despedaçado, algo precisa ser feito primeiro. Portanto, a folha de dados contém uma tabela de vetores de interrupção. Quanto menor o número de interrupção, maior prioridade ela terá. Existem várias sutilezas aqui... Ocorreu um evento - foi enviada uma solicitação de interrupção, ou seja, o chamado “sinalizador de solicitação de interrupção” está definido. Se tudo estiver bem, a interrupção for resolvida, então a vida é maravilhosa e se processa. Mas se uma interrupção for desabilitada - por exemplo, uma interrupção de prioridade mais alta já está sendo processada - então esse sinalizador de solicitação permanecerá suspenso até que as interrupções sejam habilitadas. Depois disso, o chip verifica o registro da solicitação em ordem de prioridade e, se houver flag, processa-a. MAS! Acontece que mesmo que a interrupção seja processada, não é fato que o evento que a causou ainda esteja vivo... É como se a campainha e o telefone tocassem ao mesmo tempo, você atendesse o telefone, e os convidados já decidiu que não havia ninguém em casa e foi embora. E parecia que havia um acontecimento - a campainha tocou, mas não havia ninguém atrás da porta. Outro problema é que enquanto outra interrupção está sendo processada e o sinalizador de solicitação já foi levantado, o evento pode ocorrer várias vezes. Atendemos o telefonema, abrimos a porta - e já tinha um monte de convidados lá! Apavorante? Apavorante... Outra característica do uso de interrupções – e não apenas interrupções: a reentrância (ou reentrada). Um programa reentrante é aquele que pode ser chamado por vários usuários (ou processos) sem, no mínimo, causar um erro e, no máximo, sem desperdiçar computação – por exemplo, outro usuário não precisa executar o código novamente. Ou seja, se na cozinha enquanto você cumprimenta os convidados ninguém rouba algumas guloseimas, então tudo é reentrante) Em geral, é uma coisa séria - se você não levar isso em consideração, você pode sofrer por muito tempo com “por que não funciona?!” Precisa ser levado em consideração, por exemplo, se diversas interrupções forem processadas, e cada uma delas alterar alguma variável global... As interrupções geralmente NÃO são reentrantes. Ou seja, no momento em que a interrupção está em execução, você não pode chamar a mesma interrupção novamente. É para proteger contra entradas repetidas no manipulador que as interrupções são automaticamente proibidas no momento de seu processamento (caso você queira habilitar interrupções na interrupção). procedimento de manuseio, você precisa pensar dez, vinte vezes antes de dar um passo tão precipitado). Consideremos trabalhar com interrupções externas: precisamos, em primeiro lugar, configurar qual evento irá desencadear a interrupção e, em segundo lugar, permitir que o chip processe essa mesma interrupção. O registro MCUCR é responsável pelos primeiros bits do chip ATmega8 - ISC11-ISC10, responsável por INT1, e ISC01-ISC00, responsável por INT0. Tabela 1. Definição de eventos para geração de interrupção via INT1 Assim, o mesmo com INT0. Agora só falta habilitar as interrupções no pino que precisamos - o registrador GIGR possui os bits INT0 e INT1; definido como “1” desejado - e a interrupção externa está habilitada! Mas é muito cedo para se alegrar - além das interrupções externas, as interrupções em geral devem ser habilitadas - defina o bit I mais à esquerda do registro SREG para “1”. O mesmo pode ser feito com o comando assembler: asm sei; Vejamos um exemplo simples: um botão é anexado ao pino INT0 (D.2) do chip ATmega8 (ao pino e ao zero); pressione - ocorre uma interrupção e o LED no pino B.0 acende. O LED, respectivamente, está conectado à perna e à unidade: //programa para ATmega8 ao pressionar um botão no pino INT0 (D.2) - conectado em 0 - //acende o LED no pino B.0 por interrupção externa - conectado em 1 //redefine tipos typedef unsigned char byte; sbit ddrButton em ddD2_bit; //botão de geração sbit pinButton at pinD2_bit; sbit portButton em portD2_bit; sbit ddrLight em ddB0_bit; //saída para o LED, para a saída pull-up, ligue 0 no botão sbit portLight em portB0_bit; byte flagButton = 0; //sinalizador de clique no botão; pressionado - 1 void INT0_interrupt() org IVT_ADDR_INT0 //você precisa escrever pelo menos uma função vazia - //porque o compilador não a cria sozinho. Caso contrário não funciona ( flagButton = 1; ) //processando o pressionamento do botão - levando em consideração o salto void buttonLight() ( if(flagButton) //se o botão for pressionado ( portLight = 0; //liga o LED delay_ms(500); portLight = 1; //desliga o LED flagButton = 0; ) ) void main() ( //inicializando todas as portas usadas ddrB = 0; portB = 0; ddrD = 0; portD = 0; // inicializando o botão - para a entrada com pull-up portButton = 1; //inicializa o LED, que é ligado por 0 e pressionando o botão - para saída e para 1 portLight = 1; interrupções externas MCUCR.ISC00 = 0; //a interrupção é gerada pelo 0 lógico em INT0 MCUCR.ISC01 = 0; //habilita a interrupção externa INT0 asm sei;//SREG.B7 = 1; interrompe em princípio (bit I os comandos são iguais a while(1) ( buttonLight() ; ) ) Um pouco sobre a sintaxe. A função de interrupção é escrita assim: void function_name() org IVT_ADDR_INT0. A palavra-chave org indica que o endereço de interrupção da folha de dados virá em seguida. Temos o nome da interrupção da biblioteca: digitamos IVT e depois pressionamos Ctrl + Espaço (adoro essas coisas ˆˆ). Você também pode usar iv em vez da palavra org, a julgar pela ajuda do compilador. Outra pequena observação: em primeiro lugar, a interrupção nunca deve ser incômoda - algumas linhas e pronto. Isso se deve ao fato de que durante o processamento de uma interrupção, o microcircuito não pode se distrair com mais nada, o que significa que se tivermos várias interrupções, podemos perder a ocorrência de algum evento. Também pode acontecer que não precisemos de nenhum processamento de interrupção - por exemplo, basta que o circuito tenha saído do modo de hibernação. Mas neste caso, você ainda precisa escrever uma função de interrupção, mesmo que vazia - o chamado “stub”. Em princípio, alguns compiladores escrevem automaticamente funções vazias para cada interrupção, mas este não é o nosso caso - temos que fazer isso manualmente. Hoje veremos o conceito de interrupção e como utilizá-lo. Naturalmente não dispensaremos um programa de treinamento, mas desta vez não piscaremos os LEDs. Já está bom. Vamos fazer algo parecido com uma campainha. Tarefa: faça o microcontrolador emitir um bipe ao pressionar um botão. Criamos um projeto de anel no antigo espaço de trabalho. Selecione o tipo de microcontrolador. Permitir o uso de nomes de bits definidos no arquivo de cabeçalho Altere o tipo de arquivo de saída. Salve o projeto e o espaço de trabalho. Imagine a situação. Você está sentado no trabalho e debruçado sobre outro programa de microcontrolador. O chefe chega até você e diz: “Escute, Pash, compramos osciloscópios para o nosso departamento - Tektronix, quatro canais. Ajude Vasya a arrastá-los.” Você pensa: “Bem, que coisa, só o pensamento atrapalhou... e em você.” E o chefe olha para você assim, e seus olhos são tão gentis, tão gentis. Como você pode recusá-lo? Bom, você larga tudo e vai com um amigo comprar osciloscópios. Eles trouxeram isso. Nós relatamos. E eles voltaram ao programa. Esta é aproximadamente a aparência do mecanismo de interrupção. Bastante simples, mas há vários pontos fundamentais. Em segundo lugar: Tudo isso é muito semelhante ao que acontece em um microcontrolador. Os microcontroladores AVR incluem vários dispositivos periféricos (temporizadores/contadores, conversor analógico-digital, comparador analógico, transceptor assíncrono... etc.). O poder do microcontrolador é que todos esses dispositivos podem funcionar em paralelo e independentemente uns dos outros, bem como em paralelo ao programa que está sendo executado. Cada dispositivo periférico pode disparar uma interrupção quando ocorre um evento específico. A interrupção só ocorrerá se estiver habilitada. A ativação de interrupção é definida para cada dispositivo separadamente. Além disso, há um sinalizador global de ativação/desativação para todas as interrupções - este é o sinalizador I no registro SREG. Quando ocorre uma interrupção, o microcontrolador armazena o conteúdo do contador do programa do PC na pilha, ou seja, lembra o local onde foi interrompido. Carrega o endereço do vetor de interrupção correspondente no contador do programa e salta para esse endereço. Ele atinge um comando de salto incondicional, que vai para a sub-rotina de processamento de interrupção. Desativa interrupções redefinindo o sinalizador I e executa a sub-rotina. Executada a rotina de tratamento de interrupções, o microcontrolador habilita as interrupções configurando o flag I e restaura o conteúdo do contador do programa, ou seja, retorna ao mesmo local do programa em que foi interrompido. Em teoria, o manipulador de interrupções não deveria danificar o conteúdo dos registradores do microcontrolador, pois eles podem conter dados do programa que está sendo executado naquele momento. Para isso, no início da sub-rotina de tratamento de interrupções, o conteúdo dos registradores do microcontrolador é armazenado na pilha e, no final da sub-rotina, eles são restaurados. Assim, após sair da interrupção, o microcontrolador poderá continuar executando o programa como se nada tivesse acontecido. Ao programar em assembler, o próprio programador prescreve o salvamento de registros em C, isso é feito pelo compilador. Agora vamos falar sobre o cronômetro. O ATmega8535 possui três temporizadores/contadores integrados - dois de oito bits (T0, T2) e um de dezesseis bits (T1). Usaremos um temporizador/contador T0 de oito bits. Este temporizador consiste em três registros - o registro de controle TCCR0, o registro de contagem TCNT0 e o registro de comparação OCR0. Quando o temporizador é iniciado, o registrador contador TCNT0 aumenta seu valor em um para cada transição de clock. A frequência do clock é selecionada entre vários valores possíveis no registro de controle TCCR0. Além disso, usando este registro, o modo de operação do temporizador é definido. O temporizador T0 pode disparar uma interrupção na ocorrência de um evento de “overflow” - é quando o registrador de contagem TCNT0 transborda, e na ocorrência de um evento de “coincidência” - é quando o valor do registrador de contagem TCNT0 se torna igual ao valor do registro de comparação OCR0. Os flags que habilitam essas interrupções estão localizados no registrador TIMSK. Agora sobre o programa. Não é mais possível escrever o programa linha por linha, então darei imediatamente o seu texto. A seguir analisaremos todas as suas linhas uma por uma, e tudo ficará claro. Eu deliberadamente não usei macros; o programa é pequeno e não quero desordená-lo. interno principal( vazio
) //configura o temporizador T0 //habilita interrupções //loop do programa principal – pesquisando o botão //manipulador de interrupção para o temporizador T0 Configurações de porta Em nosso circuito, um botão e um alto-falante piezoelétrico estão conectados à porta D. O pino ao qual o botão está conectado deve ser configurado como entrada e o resistor pull-up deve estar ligado. O pino ao qual o alto-falante piezoelétrico está conectado deve ser definido como saída. DDD = (0< Configurando o cronômetro O modo de operação do temporizador T0 é CTC (reset em coincidência), o sinal do relógio é clk/8. Refletimos isso no registro TCCR0 TCCR0 = (1< Por precaução, zeramos o registro de contagem TCNT0 Escreva 0xc8 no registro de comparação OCR0. Por que? Porque eu contei com isso calculadora. Bem, no papel esse cálculo é assim. Para uma descrição detalhada do temporizador T0, consulte a documentação do ATMega8535. Configuramos o temporizador e habilitamos uma interrupção geral usando a função integrada. __enable_interrupt();
Botão de enquete Quando o botão não é pressionado, a saída do microcontrolador é conectada à alimentação através de um resistor pull-up interno, ou seja, há um na saída quando o botão é pressionado, a saída está em curto com o terra, ou seja, existe; é um zero na saída. Para determinar se um botão está pressionado, você precisa ler o conteúdo do registro PIND e verificar o valor do bit zero (um botão está conectado ao PD0). Pesquisaremos o botão em um loop infinito. enquanto
(1) Não esqueça que == não é um operador de atribuição =. Manuseio de pressionar/soltar o botão Ao pressionar o botão habilitaremos a interrupção do temporizador T0, e ao soltá-lo iremos desabilitá-lo. Para fazer isso, manipularemos o bit OCIE0 do registrador TIMSK TIMSK = (1< TIMSK = 0; //desativa a interrupção Como estamos usando apenas um temporizador, não há necessidade de definir ou redefinir bits individuais. A função de interrupção é especificada usando a diretiva #pragma vector= e uma palavra de função __interromper. A função deve ser do tipo void e não deve aceitar nenhum parâmetro. #pragma vetor = Endereço Nome– nome da função, escolha a nosso critério Para nossa tarefa, a função do manipulador de interrupção se parece com isto #pragma vetor = TIMER0_COMP_vect Bom, isso é tudo. Espero que tudo esteja claro. Interrupção - um evento que requer uma resposta imediata do processador. A resposta é que o processador interrompe o processamento do programa atual ( programa interrompido) e prossegue para executar algum outro programa ( interrompendo programa), especialmente desenhado para este evento. Após a conclusão deste programa, o processador volta a executar o programa interrompido. Cada evento que requer uma interrupção é acompanhado por sinal de interrupção, notificando o computador sobre isso e ligou solicitação de interrupção. Status do programa representa um conjunto de estados de todos os elementos de armazenamento no momento correspondente (por exemplo, após a execução do último comando). Quando ocorre uma interrupção, o microcontrolador armazena o conteúdo do contador do programa na pilha e carrega nele o endereço do vetor de interrupção correspondente. O último comando da rotina de serviço de interrupção deve ser um comando que retorne ao programa principal e restaure o contador do programa armazenado anteriormente. Enquanto o manipulador de interrupção está em execução, algumas informações podem mudar. Portanto, ao passar para o manipulador de interrupções, é necessário salvar os elementos que estão sendo alterados. O conjunto de tais elementos é vetor de estado do programa. Nesse caso, outras informações sobre o estado das células de memória não são significativas ou podem ser restauradas programaticamente. Vetor de estado inicial contém todas as informações necessárias para o lançamento inicial do programa. Em muitos casos, o vetor de estado inicial contém apenas um elemento - o endereço inicial do programa que está sendo iniciado. Vetor de interrupçãoé o vetor do estado inicial do programa que interrompe (manipulador) e contém todas as informações necessárias para passar para o manipulador, incluindo seu endereço inicial. Cada tipo de interrupção possui seu próprio vetor de interrupção, que inicia a execução do manipulador correspondente. Normalmente, os vetores de interrupção são armazenados em locais de memória fixa especialmente alocados com endereços curtos, que representam tabela de vetores de interrupção. Para saltar para o programa de interrupção apropriado, o processador deve ter um vetor de interrupção e o endereço desse vetor. Neste endereço, via de regra, existe um comando de salto incondicional para a sub-rotina de tratamento de interrupções. Via de regra, o controle de armazenamento e retorno é atribuído ao manipulador de interrupções. Neste caso, o manipulador é composto por três partes - preparatória (prólogo) e final (epílogo), que garantem a troca do programa, e o próprio programa de interrupção, que realiza as operações solicitadas pela solicitação. O tempo de resposta é definido como o intervalo de tempo desde o momento em que uma solicitação de interrupção é recebida até o início da execução do programa que está interrompendo. Se houver diversas fontes de solicitações, uma determinada ordem de atendimento das solicitações recebidas deve ser estabelecida, chamada relacionamentos prioritários ou disciplina de serviço. O conjunto de todos os tipos possíveis de interrupção do processador é sistema de interrupção microcontrolador. A disciplina de serviço determina qual das várias solicitações recebidas simultaneamente é processada primeiro e se este ou aquele manipulador de interrupção tem o direito de interromper essa solicitação. Se a solicitação de interrupção não for atendida no momento em que uma nova solicitação chegar da mesma fonte (mesma prioridade), então interromper a saturação do sistema. Neste caso, algumas solicitações de interrupção serão perdidas, o que é inaceitável para o funcionamento normal do microcontrolador. Características do sistema de interrupção são: Mascaramento de interrupção usado para dizer ao microcontrolador para responder a cada tipo de interrupção ou ignorá-la. A máscara de interrupção representa um código binário cujos bits são atribuídos às fontes da solicitação de interrupção. O único bit no código binário informa ao microcontrolador para lidar com esse tipo de interrupção. Um bit zero, ao contrário, não permite que o microcontrolador proceda ao processamento de interrupções do tipo especificado. |
Ler: |
---|
Popular:
Novo
- Como descobrir o VID, PID de um pendrive e para que servem esses números de identificação?
- Huawei P8Lite - Especificações
- Como desbloquear um telefone Xiaomi se você esqueceu sua senha
- Apptools: como ganhar dinheiro jogando
- Lenovo Vibe K5 Plus - Especificações Especificações de áudio e câmera
- Decidiu mudar do Windows para o Mac?
- Como usar o Google Fotos, visão geral das funções de login do Google Fotos
- Sistema de pagamento Payza (ex-Alertpay) Payza faça login em sua conta pessoal
- Como abrir o APK e como editar?
- Análise do smartphone Alpha GT da Highscreen Embalagem e entrega