CONFIGURANDO CORRETAMENTE OS REGISTRADORES DOS MICROCONTROLADORES AVR - parte 2 USO DOS REGISTRADORES TCCR0, TCNT0 e TIMSK – CONTROLE DO TIMER0
O uso correto de um temporizador (timer) em um projeto microcontrolado pode garantir o sucesso deste. Muitas linguagens de programação oferecem funções prontas para temporização, mas estas funcionam ,em sua maioria, através de algoritmos compreendidos como “espera ocupada”. “Mas afinal o que é espera ocupada?”. A espera ocupada é um algoritmo desenvolvido para fazer o programa aguardar um determinado período necessário a algum processo. Este algoritmo é inserido no programa e quando executado passa a “ocupar” o microcontrolador e este só é “liberado” após o término da rotina. Porém o grande problema é que enquanto o microcontrolador se "ocupa" da tal rotina de temporização, nenhuma outra pode ser executada. Em alguns casos isso é muito ruim e pode prejudicar todo o projeto. Veja o código fonte abaixo1.
O programa demonstrado não esta de todo errado se a intenção for apenas manter o pino PB0 ligando e desligando continuamente. Agora em um programa maior, onde mais processos devem ser cuidados, a idéia não seria assim tão boa e, portanto, nada adequada, pois o grande problema é que o microcontrolador ficaria ocupado enquanto aguarda a passagem do tempo. Imagine se o tempo tivesse de ser igual a um minuto!!! Agora imagine que uma válvula de segurança de um equipamento qualquer tivesse de ser monitorada ao mesmo tempo pelo programa em questão. Enquanto o programa esta preso na temporização, o pino ligado a entrada de monitoramento da válvula poderia alterar seu estado, informando uma situação de emergência e nenhum algoritmo de correção seria executado até que a temporização anterior tivesse fim e o programa retornasse ao ponto após a chamada da função para espera ocupada. É ou não é algo que pode por em cheque-mate todo um projeto? Um outro detalhe importante é a precisão deste tipo de temporização. Ela não é nada segura e o desenvolvedor não teria como configurar a rotina para temporizar com precisão. Muito ruim não é mesmo? Desta forma, o desenvolvedor só deve utilizar algoritmos de espera ocupada em situações onde:
Agora, se qualquer uma destas situações for positiva então você só tem um caminho: fazer uso de um dos temporizadores internos no microcontrolador. Assim fica garantido que o programa não ira se ocupar com qualquer temporização e as mesmas também serão mais precisas (precisão esta depende apenas do tipo do clock utilizado).
O PERIFÉRICO TIMER0 DO AVR O periférico TIMER0 nos microcontroladores AVR é o mais simples de se usar, entre os demais timers presentes, e consequentemente o mais utilizado pelos desenvolvedores. Neste artigo demonstrarei a sua configuração e uso, deixando os demais para uma outra oportunidade. O TIMER0 do microcontrolador possui oito bits, com um prescale que permite a divisão do clock por até 1024. Os microcontroladores AVR possuem registradores dedicados para a configuração dos seus timers. Para utilizar o TIMER0 é necessário compreender o uso de três registradores básicos. A descrição destes três registradores pode ser vista a seguir: -
Registrador TCCR0 (Controle do timer
e contador) Na figura abaixo apresento a estrutura do timer/counter de 8 bits dos microcontroladores AVR. Na figura é possível observar a presença dos registradores TCCRx e TCNTx. O registrador TIMSK não é mostrado, pois o mesmo é compartilhado com outros periféricos.
REGISTRO TCCR0 A seguir, na tabela é dada a descrição do registro TCCR0 – Timer/Counter Control, a posição de cada um dos bits de controle, o valor inicial dos mesmos durante o RESET do microcontrolador e a descrição de suas funções.
Tabela – Registro TCCR0
FOC0 – Force Output Compare Bit de escrita que permite configurar de acordo com seu valor lógico (0 ou 1):
0
– modo PWM não selecionado; Obs.: A seleção acima citada depende da configuração adotada através dos bits WGM00 e WGM01.
WGM00:01 – Wave Forme Generation Mode Bits de leitura e escrita que permitem configurar a forma de onda gerada no PWM. Esta configuração pode ser vista na tabela abaixo. Tabela – Configuração dos bits WGM00 e WGM01
COM00:01 – Compare Match Output Mode Bits de leitura e escrita que permitem configurar o pino OC0 (Output/Compare 0). Esta configuração pode ser vista na tabela a seguir.
Tabela – Configuração dos bits COM00 e COM01
CS02:00 – Clock Select Bits de leitura e escrita que permitem configurar o clock para o Timer/Counter. Esta configuração pode ser vista na tabela abaixo.
Tabela – Configuração dos bits CS02, CS01 e CS01
REGISTRO TIMSK A seguir, na tabela, é dada a descrição do registro TIMSK – Timer/Counter Interrupt Mask, a posição de cada um dos bits de controle, o valor inicial dos mesmos durante o RESET do microcontrolador e a descrição de suas funções. Este registro permite controlar as interrupções para todos os timers e comparadores presentes no microcontrolador. Os bits 1 e 0 controlam o TIMER0, e sendo assim, apenas estes bits serão analisados, pois são de total interesse aqui. Os outros bits são irrelevantes neste momento e não serão discutidos.
OCIE0 – Timer/Counter 0 Output Comp Math INT Enable Bit de escrita e leitura que permite configurar de acordo com seu valor lógico (0 ou 1):
0
– desabilita INT para modo comparador;
TOIE0 – Timer/Counter 0 Overflow INT Enable Bit de escrita e leitura que permite configurar de acordo com seu valor lógico (0 ou 1):
0
– desabilita INT por Overflow
do Timer;
REGISTRO TCNT0 A seguir, na tabela, é dada a descrição do registro TCNT0 – Timer/Counter Register. Este registro não é utilizado para o controle do TIMER0 propriamente dito. Sua função principal é conter o valor a ser “contado”.
Tabela – Registro TCNT0
UM PEQUENO EXEMPLO A prática permite que a teoria seja aplica e sendo assim, sem teoria não há prática! Portanto, antes de se dedicar ao teste aqui sugerido, é interessante que você tenha compreendido o uso dos registradores de controle para o TIMER0. Caso algo não tenha ficado muito claro, leia novamente tudo o explicado acima até que fique claro. Meu exemplo prático sugere a aplicação do timer na contagem de tempo (dãrh) que será usado na troca do estado de um pino de I/O do microcontrolador. Para verificar a troca de estado nada melhor que um LED. Ou seja, vamos preparar um pisca LED microcontrolado! (uhuaua). Para muitos este tipo de aplicação é algo tão simples que não deveria nem mais ser utilizado. Mas é justamente pela simplicidade da aplicação que a mesma foi aqui valorizada. “Porque?”. Simples. Porque com base nesta “simples” aplicação você poderá implementar outras que julgar, digamos, “mais complexas”. A figura abaixo demonstra o circuito proposto. Vamos utilizar para tal o microcontrolador AVR ATMEGA8535, porém um outro AVR qualquer também poderia ser utilizado. O código fonte fornecido mais adiante foi preparado para o microcontrolador em questão. O compilador utilizado foi o GCC-AVR (Linux), sendo que seu compatível para Windows® é o WinAVR. O uso de um outro compilador qualquer, como ICC AVR ou CodeVision AVR, requer as devidas adaptações e não é meu intuito discutí-las aqui. Se você escolheu usar um outro compilador, altere o código fonte caso constate alguma incompatibilidade. O circuito proposto é bem simples e minha sugestão é a sua montagem em uma matriz de contatos ou então em uma placa padrão, pois este servirá apenas para alguns testes. Para os mais "moderninhos" o uso de simuladores também é possível. O código fonte a ser utilizado em conjunto com o circuito apresentado pode ser visto logo abaixo. Código Fonte para testes
Antes de detalhar como os registradores de configuração do TIMER0 foram utilizados, vou me ater ao ponto principal deste programa que é a temporização. Esta equivale a 1000 ms (1 segundo). O TIMER0 não tem condições de realizar uma temporização deste tamanho, com o clock selecionado. Neste caso o que deve-se fazer é encontrar o melhor tempo (com a máxima precisão) que pode ser obtido, de acordo com o clock utilizado, para que este seja posteriormente “multiplicado” via software para se obter o tempo desejado. Para isso é necessário configurar o TIMER0, através de seus registradores, de acordo com o clock externo do microcontrolador. Estou considerando um clock externo de 7.3728MHz para o microcontrolador. Se o TIMER0 for configurado para que a divisão (prescale) seja a máxima, ou seja 1024, e o mesmo for carregardo com o valor a ser contado igual a 180 teremos: Tempo contado = [(1/7.3728 MHz) x 1024] x 180 = 25 ms
Este tempo de 25 ms não é o desejado, mas com um simples tratamento é possível conseguí-lo. Para isso usei uma variável auxiliar com o único intuito de contar 40 vezes a chamada da interrupção do timer. Quando a variável for igual a 40, tem-se exatos um segundo passado (Tempo Total = 25 ms x 40 = 1 segundo). Com base nestes dados é possível carregar os registradores de acordo com o desejado. O registrador TCCR0 é carregado então, durante a inicialização do microcontrolador, com o valor 5 decimal, que convertido para binário é igual a b00000101. Isso configura os bits CS02, CS01 e CS00 do referido registrador para que o prescale seja igual a 1024. No registrador TCNT0 é inserido o valor 4CH (hexadecimal) que equivale ao valor 76 decimal. Você deve estar se perguntando: “Porque 76 e não 180 conforme disposto na equação?”. Se queremos contar 180 vezes através do TIMER0, o valor a ser inserido no registrador TCNT0 deve ser igual ao valor que desejamos contar, subtraído do valor máximo admitido no registrador que é 255. Veja: TCNT0 = 255 – valor desejado = 256 – 180 = 76 No registrador TIMSK é inserido o valor um decimal (b00000001) com o único intuito de habilitar a interrupção por overflow do TIMER0. Você deve ter notado a presença de duas funções: cli() e sei(). Estas funções tem como objetivo desabilitar e habilitar, respectivamente, o bit de controle para interrupção GLOBAL. Assim, enquanto tudo está sendo configurado não é permitido que nenhuma interrupção ocorra. A troca de estado do pino PB0 é feita dentro da função de tratamento da interrupção do TIMER0, sempre que a variável timercount for igual a 40. Quando esta situação for verdadeira, o pino PB0 tem seu estado invertido através de uma operação lógica XOR (Ou exclusivo) entre o valor contido na porta e o literário “1” (PORTB ^= 1 que corresponde a PORTB = PORTB ^ 1). Um outro detalhe importante no programa é a forma como a função principal é mantida “rodando”. A função while(1) seguida do “;” fica tratando “nada” constantemente. Qualquer outro processamento desejado deverá ser inserido no lugar do ponto e virgula (dentro do laço while). Assim, quando necessário o timer solicitará ao microcontrolador que interrompa o processamento normal, desvie para a subrotina de tratamento da interrupção do timer, execute o processamento nela contida e em seguida retorne para o ponto onde o programa foi interrompido. No programa isso é feito a cada 25ms.
CONCLUSÃO Nesta parte demonstrei o uso do TIMER0 do microcontrolador AVR através dos seus registradores.Você perceberá que a medida avançamos, algumas novidades serão introduzidas. Nesta, além do uso dos registradores de controle você viu também como é possível usar uma interrupção em conjunto com o TIMER0 e novas formas de “uso” na linguagem escolhida (Linguagem C) também foram introduzidas. Abaixo alguns documentos que eu recomendo a leitura: ATMEGA8535
- http://www.atmel.com/dyn/resources/prod_documents/doc2502.pdf Este artigo foi publicado, com minha autorização, na revista Eletrônica Total nrº 124 de Julho de 2007. |
Copyright deste conteúdo reservado para Márcio José Soares e protegido pela Lei de Direitos Autorais LEI N° 9.610, de 19 de Fevereiro de 1998. É estritamente proibida a reprodução total ou parcial do conteúdo desta página em outros pontos da internet, livros ou outros tipos de publicações comerciais ou não, sem a prévia autorização por escrito do autor. |