CONFIGURANDO CORRETAMENTE OS REGISTRADORES DOS MICROCONTROLADORES AVR – parte 1 USO DOS REGISTROS PORTX, DDRX E PINX CONTROLE DE I/O NOS AVR Os microcontroladores AVR possuem registradores dedicados para a configuração dos seus pinos de I/O. A descrição destes três registradores pode ser vista a seguir: -
Registrador PORTxn (dados – leitura/escrita)
Observando atentamente a figura acima você notará a presença dos três registradores descritos, representados na figura com apenas 1 bit (o controle dos pinos de I/O no AVR é feito sempre de maneira independente). WDx e RDx representam um bit do registrador DDRxn, enquanto WPx e RRx um bit do registrador PORTxn e RPx um bit do registrador PINxn. É preciso compreender que WDx, RDx, WPx, RRx, e RPx são comuns a uma mesma porta, enquanto clkI/O, SLEEP e PUD são comuns a todas as portas. Na momenclatura usada, o “x” presente na descrição dos registradores indicará a porta que se deseja escrever, ler ou configurar e este pode ser: “A”, “B”, “C”, “D”, etc, dependendo do número de portas presentes na versão do microcontrolador utilizado. Já o “n” representa o número do bit da porta e este pode estar dentro da faixa de “0” a “7”. Se, por exemplo, você estiver utilizando o microcontrolador AVR AT90S8535, ou seu sucessor ATMEGA8535, terá ao seu dispor quatro portas de oito bits cada (“A”, “B”, “C” e “D”) num total de 32 pinos de I/O. Em uma versão com menos pinos de I/O como o ATtiny2313 temos disponível apenas as portas “A”, “B” e “D”, sendo que a porta “A” possui apenas três bits (PA0 a PA2), a porta “B” tem oito bits (PB0 a PB7) e a porta “D” com sete bits (PD0 a PD6), perfazendo um total de 18 pinos de I/O. Em um caso mais extremo, como quando se usa o ATMEGA128 dispomos de 53 pinos de I/O divididos entre as portas “A”, “B”, “C”, “D”, “E”, “F” e “G” (lembrando que nem todas as portas podem se apresentar de maneira completa, ou seja, com oito bits). De qualquer maneira, seja qual for o microcontrolador AVR selecionado (com um número pequeno ou grande de I/Os) a maneira como a porta será utilizado é sempre a mesma, tanto para o ATtiny2313 quanto para o ATMEGA128. A seguir falarei de cada um destes registradores e também passarei alguns exemplos de uso, usando para tal a Linguagem de Programação C. Obs.: A escolha da Linguagem C deve-se a alguns pontos positivos como: o fato de podermos contar com compiladores gratuitos, tanto para Linux (GCC-AVR) quanto para Windows (WinAVR); devido a muitas empresas buscarem por profissionais com conhecimentos nesta Linguagem; e o mais relevante de todos que é esta a minha Linguagem de Programação preferida! ;-) .
REGISTRADOR PORTX O registrador PORTx permite o controle dos dados que são enviados ou recebidos (escrita e leitura) através de uma determinada porta de I/O do microcontrolador AVR. É através deste registrador que o microcontrolador “acessa o mundo exterior”. A tabela abaixo mostra o registrador PORTB usado no controle de dados para a porta “B”. Tabela – Registro PORTB
W
– Write (escrita) Obs.: Este registrador pode ser lido ou escrito Se um pino de I/O for configurado como “saída”, podemos alterar o estado de tal pino (nível lógico “1” ou “0”) controlando cargas externas conectadas ao mesmo. A figura abaixo demonstra dois pequenos exemplos para controle de cargas com consumo de corrente de até 400 mA, feito através de transistores.
Mas se um pino de
I/O for configurado como “entrada” será
possível ler o estado deste pino. É desta forma que poderemos “ler”,
por exemplo, o estado de uma chave (ligada
ou desligada) quando a mesma estiver conectada a um pino de I/O do
microcontrolador. Isso
está demonstrado na figura abaixo.
REGISTRADOR DDRX O registrador DDRx permite configurar a direção (entrada ou saída) dos I/Os de uma porta. A tabela abaixo apresenta o registro DDRB usado para configurar a direção dos pinos de I/O da porta B. Tabela – Registro DDRB
W
– Write (escrita) Obs.: Este registrador pode ser lido ou escrito Ao tornar um bit igual a “1” (nível lógico HI) no registrador DDRxn, configuramos o respectivo pino de I/O como saída. Se fazemos um bit igual a “0” (nível lógico LOW) configuramos o pino de I/O como entrada. Um detalhe muito interessante sobre os pinos de I/O dos microcontroladores AVR é a presença de resistores de pull-up internos para cada um dos pinos. Estes resistores podem ser conectados e desconectados de maneira independente, diferente de muitos outros microcontroladores que só permitem a conexão de todos os resistores presentes em uma mesma porta.
Para conectar um resistor de pull-up interno é necessário que o pino de I/O seja configurado como “entrada” através do registrador DDRxn e o valor lógico presente no pino no momento desta configuração, inserido através do registrador PORTxn, seja igual a “1”. Ou seja, usando os registradores PORTxn e DDRxn em conjunto é possível conectar ou desconectar os resistores de pull-up internos do microcontrolador. Veja a tabela abaixo. Tabela – Seleção dos resistores de pull-up
Usando os resistores de pull-up internos é possível reduzir os custos de um projeto, não somente pela eliminação direta de resistores externos mas também pela provável redução no tamanho da placa de circuito impresso e conseqüentemente de seus custos de confecção.
REGISTRADOR PINX O registrador PINx é um registrador que permite acessar de forma direta o valor inserido na porta (pinos de I/O), independentemente da configuração selecionada para esta (entrada ou saída). A tabela abaixo mostra o registrador PINB, como exemplo. Tabela – Registro PINB
R – Read (leitura) Obs.: Este registrador pode apenas ser lido EXEMPLOS DE USO Agora que você já foi devidamente apresentado aos registradores de controle para os pinos de I/O de um AVR, nada melhor que alguns pequenos exemplos de configuração e uso. Vamos começar com a configuração. Veja o segmento de um código fonte hipotético à seguir:
Em seguida, os bits 6 e 7 do registrador PORTB são levados ao nível lógico “1”, mantendo os demais (0, 1, 2, 3, 4 e 5) em zero. Para quem tem pouco conhecimento na Linguagem C a lógica (y << x) pode parecer a primeira vista estranha. Trata-se de um deslocamento à esquerda (shift) de “x” bits do valor “y”. Se você pesquisar nos arquivos de diretrizes de pré-compilação no seu compilador (arquivos de inclusão) irá descobrir que PB0 está definido como “0”, PB1 como “1” e assim por diante até PB7 com o valor “7”. Então, se estamos fazendo um deslocamento de “7” e “6” bits em dois valores distintos e iguais a “1” temos:
Então será inserido no registrador PORTB o resultado da operação lógica “OR” (OU) entre os dois valores obtidos nos deslocamentos e este valor será igual a b11000000. Claro que você pode inserir diretamente o valor desejado no registrador, tanto em binário quanto em hexadecimal ou mesmo decimal. A notação não importa, desde que o valor inserido reflita a configuração desejada. O uso de deslocamentos e operações lógicas deixa mais elegante e inteligível o programa. Na linha seguinte do segmento de código, o registrador DDRB é preenchido de maneira a configurar os pinos de I/O 0, 1, 2, 3, 4 e 5 como saídas, deixando o restante (6 e 7) como entrada. O valor inserido no registrador DDRB é b00111111. Se você voltar a teoria, perceberá que os pinos de I/O 6 e 7 (PB6 e PB7) terão seus resistores de pull-up conectados, mantendo os resistores dos outros pinos da porta desconectados. Uma instrução NOP (não operando) foi inserida para permitir a sincronização do estado atual da porta com o clock interno do microcontrolador. Esta instrução não faz nada, apenas perde um ciclo de máquina no AVR. Na última linha do segmento de código uma representação dos valores presentes nos pinos de I/O da porta “B” é lida através do registrador PINB para a variável “i”. Se você leu atentamente tudo o que foi explicado até aqui, conseguirá chegar ao valor b11000000 para a variável. Os bits seis e sete da porta, apesar de configurados como entrada, possuem resistores de pull-up internos conectados aos mesmos garantindo nível lógico alto nestes pinos. Já o restante dos bits foram configurados como saída e receberam nível lógico “0” (valor inicial dos bits), e assim, durante a leitura apresentarão este mesmo nível lógico. No segundo exemplo demonstrarei como alterar o estado de um pino usando o registrador PORTxn. Vamos supor que você deseje “ligar” (nível lógico “1”) o pino PB0 da porta “B”, que foi configurado como saída. Uma sugestão de como fazê-lo pode ser vista no segmento de código a seguir.
A variável “i” tem seu valor alterado e é levada até a porta “B” através do registrador PORTB com o auxílio da lógica “OR” (OU) entre o registrador e a variável. É preciso compreender que durante uma operação lógica deste tipo todos os bits do registrador são afetados. Tanto o registrador quanto a variável possuem oito bits de tamanho (um byte). Agora, se você deseja “desligar” o pino PB0, basta então fazer com que o pino receba o nível lógico “0” através do registrador PORTB. Veja uma sugestão no segmento de código a seguir.
Este segmento de código usa a lógica “AND” (E) para obter o resultado desejado. Observe que em ambos os casos nenhum pino, a não ser o desejado, será alterado. Você deve lembrar-se do valor obtido através do primeiro segmento de código demonstrado. Vamos supor que os dois segmentos de código apresentados a seguir fazem parte do mesmo programa. Durante a leitura da porta através do registrador PINB, obtivemos o valor b11000000. Se realizarmos uma lógica “OU” entre o valor b11000000 e b00000001 (segundo segmento de código) o resultado obtido será b11000001. Agora o bit “0” do registrador PORTB possui nível lógico “1”. Este nível lógico será refletido no pino de I/O PB0, levando o mesmo também ao nível lógico “1”. Em seguida é feita uma lógica “E” entre o valor atual do registrador PORTB, b11000001 (obtido com o segundo segmento de código) e o novo valor da variável “i”, b11111110. Como resultado obtemos b11000000. Como você pode perceber apenas o bit “0” do registrador PORTB e, conseqüentemente, o pino PB0 foram alterados. Agora vamos supor que você deseje verificar se uma determinada chave, ligada ao pino PB7, foi acionada. E vamos supor ainda que este pino está com seu resistor de pull-up interno ligado. O segmento de código a seguir demonstra como esta leitura pode ser feita:
Muitas outras lógicas poderiam ser utilizadas para se obter os resultados aqui demonstrados. Tudo depende do estilo do programador. Tenho certeza que você saberá encontrar o seu (se é que já não o fez). Você também não deve se preocupar se as instruções, operadores ou outra coisa qualquer nos segmentos de códigos apresentados não lhe parece familiar. Procure apenas se ater a lógica apresentada e aos resultados obtidos. O resto virá com o tempo.
CONCLUSÃO Lidar corretamente com microcontroladores não é uma tarefa difícil como muitos pensam. Após um certo tempo você se acostumará com os mesmos e passará a utilizá-los em seus projetos sem maiores problemas. O importante é dedicar-se ao estudo dos mesmos, buscando conhecer não apenas uma Linguagem de Programação com seus "comandinhos mágicos", mas principalmente a estrutura do microcontrolador em questão!
Este artigo foi publicado, com minha autorização, na revista Eletrônica Total nrº 122 de Março/Abril 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. |