SFRs

A utilização das funções incluídas na biblioteca Arduino permite a criação de programa de uma forma fácil mas também apresenta algumas desvantagens:

  • a ocupação da memória de programa (flash) é em geral maior
  • o desempenho é normalmente pior, i.e. os tempos de execução são maiores
  • certas funcionalidades do PIC32 podem ainda não estar incluídas na biblioteca

Embora o tamanho da memória flash do PIC32 seja suficientemente grande para o primeiro ponto não ser muito crítico, e o tipo de programa que iremos desenvolver não apresentarem em geral grandes apertos temporais, a não existência de funções na biblioteca Arduino que cubram algumas das funcionalidades do PIC32 poderá ser um forte incentivo ao acesso direto ao recursos de hardware do microcontrolador. Esse acesso faz-se através de registos especiais, os chamados Special Function Registers (SFR).

Esta nova abordagem é mais complexa pois exige o conhecimento desses registos assim como da função dos bits que os compõem, algo que pode ser obtido pela consulta da datasheet do PIC32. Em certos casos é ainda preciso perceber a forma como as funcionalidades estão implementadas no hardware, algo que está também descrito nessa datasheet. Nesse documento as diferentes funcionalidades estão divididas pelas várias secções, indo nós aqui (a título de exemplo) apenas focar a secção referente ao uso das portas de entrada/saída (GPIO).

A necessidade que muitas das vezes temos em (des)ativar certos bits dentro dos registos SFR obriga à utilização de funções lógicas bitwise da linguagem C.

Projeto Blink com SFRs

Os micro controladores PIC32 permitem que a grande maioria dos seus pinos possam ser usados como pontos de entrada/saída digital. Dado o seu elevado número estes pinos encontram-se agrupados em portas designadas por uma letra de A a G. Cada pino será pois designado pela letra da respetiva porta e um número (que poderá ir de 0 a 15). Assim, RG6 será o pino 6 da porta G. Como mostra o excerto do esquemático da placa chipKIT Uno32, este é o pino responsável pelo controlo do LED LD4 usado no tutorial.

De realçar ainda o facto dos LEDs não estarem ligados diretamente aos pinos, mas sim através dum transístor Q2. Desta forma o nível de corrente pedido ao PIC32 é menor. Contudo, e segundo a datasheet do componente (ver figura seguinte), cada pino I/O pode fornecer/receber até um máximo de 25mA (desde que os pinos no seu conjunto não ultrapassem os 200mA).

Note-se que este micro requer uma tensão de alimentação de 3,3V (no máximo 4V) pelos que os seus pinos I/O funcionam com esse nível. Contudo quando funcionam como entradas são em geral tolerantes a tensões de 5V (até 5,5V mais concretamente).

A exemplo do que foi feito no projeto blink, uma das primeiras preocupações do nosso programa consiste em definir quais os pinos que pretendemos como entradas e quais os que serão saídas. Para isso qualquer porta x tem a si associada um registo SFR designado de TRISx, onde os pinos de saída são definidos por um bit igual a 0 (0utput), e os de entrada por um 1 (1nput). Após um reset do microcontrolador, este registo possui todos os seus bits a 1, estando pois por omissão todos os pinos definidos como entradas.

No nosso exemplo pretendemos definir o pino RG6 (LED LD4) como saída pelo que bastará escrever: TRISG = 0xFFBF (colocando um zero no bit 6 desse registo).

De notar que este como qualquer outro SFR possui três outros registos a ele associados: TRISGCLR, TRISGSET e TRISGINV., i.e. designados igualmente de TRISG mas com as terminações CLR, SET e INV. Estes registos permitem simplificar respetivamente o processo de desativação, ativação e troca do valor lógico dos bits desse SFR.

Por exemplo, a operação atrás realizada (colocar a zero o bit 6 de TRISG) pode ser também realizada por: TRISGCLR = 0x0040, ou duma forma mais intuitiva: TRISGCLR = (1<<6). A diferença é que desta forma apenas estamos a alterar o valor do bit 6 mantendo os restantes inalterados! É certo que isto também podia ter sido realizado assim: TRISG = TRISG & ~(1<<6), mas desta desta forma para além da escrita no registo era necessário realizar também uma leitura prévia do mesmo o que tornaria o processo mais demorado.

Definido que está o pino RG6 como saída resta agora dentro do ciclo infinito alternar o seu valor lógico. Para isso cada porta possui um registo LATx que contem os valor lógicos pretendidos para todos os seus bits. Assim, LATG = (1<<6) acenderia o LED, e LATG = 0x0000 apagaria-o.

Caso mais uma vez nos interessasse atuar só sobre o pino RG6, teríamos que fazer respetivamente LATG = LATG | (1<<6) seguido de LATG = LATG & ~(1<<6), ou simplesmente LATG = LATG ^ (1<<6) (recorrendo à função lógica XOR). Como já foi referido a existência dos registos LATGSET e LATGCLR e LATGINV facilitam o processo permitindo a escrita respetivamente de: LATGSET = (1<<6), LATGCLR = (1<<6) e LATGINV = (1<<6).

O seguinte código implementa um projeto alternativo para fazer piscar o LED LD4 da placa chipKIT Uno32, mas desta feita dispensando as funções Arduino pinMode e digitalWrite e acedendo diretamente antes aos SFRs do PIC32:

#include <Arduino.h>

#define RG6 (1<<6)

void setup() {
  TRISGCLR = RG6;
}

void loop() {
  LATGINV = RG6;
  delay(1000);
}

Como se pode ver na imagem seguinte, da compilação resultou um programa que embora continue a fazer o pretendido piscar do LED, necessita de menos recursos de memória, já que em lugar dos 5464 bytes de memória flash ocupados pela versão que usa as funções pinMode e digitalWrite, foram agora ocupados apenas 4140 bytes:

Para completar a apresentação dos principais SFRs associados aos pinos GPIO do PIC32, diga-se que caso um pino seja definido como entrada, a determinação posterior do seu valor lógico far-se-á através dum um outro registo SFR associado à porta onde esse pino se encontra, trata-se do registo PORTx. Em cada bit desse SFR encontraremos o valor lógico do pino respetivo dessa porta.

Em conclusão, os SFRs usados para controlo das funcionalidades dos pinos GPIO duma porta x (onde x pode ir de A a G) são os seguintes:

  • TRISx – onde se especifica se os pinos das porta x são entradas ou saídas
  • LATx – onde se especifica o valor lógico dos pinos de saída da porta x
  • PORTx – onde se lê o valor lógico dos pinos de entrada da porta x

Não esquecer que todos estes registos têm 3 outros associados quando terminados pelo sufixo CLR, SET e INV.

Desempenho das funções Arduino vs SFRs

Vejamos o seguinte exemplo que gera um impulso no pino 5 (RD1) através da função Arduino digitalWrite, ladeado por dois outros impulsos desta vez gerados através do acesso direto ao SFR LATD:

#include <Arduino.h>

#define RD1 (1<<1)

void setup() {
  pinMode(5, OUTPUT);
}

void loop() {
  LATDSET = RD1;
  LATDCLR = RD1;

  digitalWrite(5, HIGH);
  digitalWrite(5, LOW);

  LATDSET = RD1;
  LATDCLR = RD1;
}

Como se pode constatar no osciloscópio o sinal periódico gerado no pino 5 apresenta de facto um sinal cíclico com 3 impulsos, mas de larguras bem diferentes, sendo o gerado pela função digitalWrite bastante mais largo. Tal justifica-se pelo uso duma função, com todo o peso daí inerente (invocação e retorno da rotina, passagem e descodificação dos parâmetros, etc.). Já os impulsos gerados via SFR requerem poucas instruções do PIC32 sendo por isso mais breves.

Isto prova que para certas aplicações mais exigentes do ponto de vista temporal, o acesso direto aos SFR em detrimento das funções da biblioteca Arduino pode ser incontornável.