Tema 4

Fazer download em pdf ou txt
Fazer download em pdf ou txt
Você está na página 1de 71

DESCRIÇÃO

Descrição geral e acesso aos periféricos externos mais importantes dos microcontroladores,
Programação para realização de sistemas completos de monitoração e controle com a
plataforma Arduino e com microcontroladores da família PIC.

PROPÓSITO
Acionar e coletar dados de dispositivos externos aos microcontroladores, complementares aos
sistemas embarcados nas aplicações de monitoração e controle, é importante para a formação
do projetista desses sistemas que programa as interfaces de entrada e saída para integrar as
funções não atendidas pelos periféricos internos, mas que são essenciais no desenvolvimento
do projeto.

PREPARAÇÃO
Antes de iniciar o conteúdo deste tema, tenha instalado o compilador CCS, o ambiente MPLAB
e o simulador PICSimLab, além de fazer um cadastro no simulador Tinkercad.
OBJETIVOS

MÓDULO 1

Identificar as funções de programação de protocolos de transmissão de dados em


microcontroladores para a comunicação com dispositivos externos

MÓDULO 2

Descrever sensores e atuadores para a programação de sistemas de controle embarcados

MÓDULO 3

Identificar as funções para a programação de mostradores na criação de interfaces com o


usuário

MÓDULO 4

Reconhecer a importância da programação de interrupções para o recebimento otimizado de


dados de dispositivos externos ao microcontrolador

INTRODUÇÃO
Sempre que uma determinada aplicação precisar de algum periférico diferente dos fornecidos
dentro do microcontrolador, eles podem ser adicionados posteriormente. Esses periféricos são
chamados de periféricos externos, pois, evidentemente, são externos ao chip do
microcontrolador, diferentemente dos que já estão no chip, que são chamados de periféricos
internos.

Dessa forma, quando não é possível encontrar um microcontrolador que tenha todos os
periféricos necessários internamente, pode-se comprar o periférico separadamente, soldá-lo na
mesma placa de circuito impresso e fazê-lo se comunicar com o microcontrolador.

Neste conteúdo, interagiremos com os periféricos externos mais comuns, que são utilizados
para estabelecer interfaces com os usuários e para criar um sistema completo de controle.

Para isso, usaremos protocolos de comunicação disponibilizados em periféricos internos do


microcontrolador, tais como I2C, UART e SPI. Utilizaremos também a técnica de interrupção
para o atendimento eficiente aos periféricos externos. Dessa forma, será possível usar as
instruções em C para se comunicar e atuar em periféricos externos no desenvolvimento de
projetos de sistemas embarcados, com exemplos para as plataformas Arduino e PIC de
microcontroladores.

MÓDULO 1

 Identificar as funções de programação de protocolos de transmissão de dados em


microcontroladores para a comunicação com dispositivos externos

TRANSMISSÃO DE DADOS
A transmissão de dados é um modo de transmitir dados digitais ou analógicos em um meio de
comunicação para um ou mais dispositivos.

Permite a transmissão e a comunicação de dispositivos em diferentes ambientes:

PONTO A PONTO
PONTO A MULTIPONTO

MULTIPONTO A MULTIPONTO

A transmissão de dados pode ser analógica ou digital, mas é principalmente destinada ao


envio e recebimento de dados digitais. Como tal, ela também é conhecida como transmissão
digital ou comunicação digital.

Assim, ela atua quando um dispositivo visa transmitir um conjunto de informações para um ou
vários dispositivos destinatários.

Os dados digitais vêm do dispositivo de origem na forma de fluxos de bits digitais. Esses fluxos
de dados são então colocados em um meio de comunicação, que pode ser paralelo ou serial,
permitindo, portanto, que eles sejam entregues para o dispositivo de destino.

TRANSMISSÃO PARALELA

Dentro de uma placa de circuito impresso com microcontroladores, as distâncias entre as


diferentes subunidades são curtas. Assim, poderia ser considerada uma prática comum a
transferência de dados entre essas subunidades utilizando um fio separado para transportar
cada bit de dados.

Os dados, nesse caso, são trocados por meio de um modo de transferência paralela. Esse
modo de operação resulta em atrasos mínimos na transferência de cada palavra, pois todos os
bits de dados são transmitidos simultaneamente, sendo que, para transmitir n bits, são
utilizados n fios ou linhas. Dessa forma, cada bit tem sua própria linha e todos os n bits de um
grupo são transmitidos em um pulso de clock de um dispositivo para outro.

Uma desvantagem desse tipo de transmissão, no entanto, decorre do fato de mais linhas
paralelas implicarem em chips maiores, com mais portas, ocupando mais espaço. Em função
disso, o uso de periféricos externos com portas paralelas para comunicação tem se tornado
menos usual. Em geral, somente alguns conversores analógicos-digitais de alta velocidade têm
mantido o uso de portas paralelas. Os microcontroladores usam as suas portas de entrada e
saída digitais, normalmente duas ou três de 8 bits cada, para se comunicarem com periféricos
externos de interface paralela.

TRANSMISSÃO SERIAL

A transmissão serial possui a vantagem principal de utilizar poucos fios ou linhas para a
transmissão. Os dados, neste caso, são transmitidos por meio de um único bit por vez, usando
um intervalo de tempo fixo para cada bit. Assim, todos os bits de dados são transmitidos em
uma única linha de maneira serial, com apenas um único bit enviado com cada pulso de clock.

Então, para um byte (8 bits), como 10011010, ser enviado da origem para o destino, ele pode
ser transmitido a partir do bit menos significativo (0), seguido pelos bits seguintes até o mais
significativo (1), tudo através de uma única linha de comunicação.

Existem dois tipos de transmissão serial – assíncrona e síncrona – e ambas usam


sincronização de bits. A sincronização de bits é uma função necessária para se determinar
quando o início e o fim da transmissão de dados ocorrem. Dessa forma, ela ajuda o receptor a
saber quando os dados começam e terminam durante uma transmissão. Portanto, a
sincronização de bits fornece controle de temporização.

Transmissão assíncrona

A transmissão assíncrona envia apenas um caractere (byte) por vez. A sincronização de bits
entre os dois dispositivos é possível usando o bit de início e o bit de parada. O primeiro indica o
início dos dados, isto é, alerta o receptor para a chegada de um novo grupo de bits. Portanto,
um bit de início, geralmente 0, deve ser adicionado no começo de cada byte. Já o bit de parada
indica o fim dos dados, ou seja, permite que o receptor saiba que o byte está concluído. O bit
de parada é, geralmente, de valor 1. A adição de bits ao início e ao final aumenta o número de
bits de dados. Logo, mais largura de banda é consumida na transmissão assíncrona.

Transmissão síncrona

A transmissão síncrona não usa bits de início e parada. Nesse método, o fluxo de bits pode ser
combinado em quadros maiores que podem conter vários bytes, ou seja, não há intervalo entre
os vários bytes no fluxo de dados. Na ausência de bits de início e parada, a sincronização de
bits é estabelecida entre o emissor e o receptor por um bit de temporização, um pino adicional
de clock que sincroniza a transmissão de cada bit. Logo, para receber os dados sem erros, o
receptor e o emissor operam na mesma frequência de clock. Como vários bytes podem ser
colocados na transmissão sem qualquer espaço entre eles, é responsabilidade do receptor
separar o fluxo de bits em bytes para reconstruir as informações originais.

PROTOCOLO SERIAL I2C


O I2C, abreviatura de Inter-Integrated Circuit (Circuito Inter integrado), é um protocolo de
barramento serial de curta distância desenvolvido pela Philips Semiconductor para melhorar a
comunicação entre o núcleo controlador e vários outros chips envolvidos em torno do núcleo.
Ele foi criado para reduzir os custos de fabricação de produtos eletrônicos.

Antes do I2C as comunicações chip a chip utilizavam diversos fios em uma interface paralela,
muitas vezes exigindo chips periféricos com mais de 20 pinos usados para endereçamento,
seleção, controle e transferência de dados entre os chips.

O I2C realiza comunicações chip a chip usando apenas dois fios em uma interface serial,
permitindo que eles se comuniquem com menos pinos.

Os dois fios usados no I2C são chamados de clock (SCL) e dados (SDA). Esses dois fios
carregam endereçamento, seleção, controle e dados, um bit de cada vez.

O fio SDA carrega os dados, enquanto o fio SCL sincroniza o emissor e o receptor durante a
transferência; portanto, o I2C é de transmissão síncrona.

 COMENTÁRIO

Chips que usam o barramento I2C podem realizar a mesma função que suas contrapartes de
interface paralela maiores, mas com muito menos pinos. Isso reduz muito o tamanho e o custo
desses chips.

Os dispositivos I2C são classificados como mestre (master) ou escravo (slave), sendo o
conjunto de dispositivos classificados como barramento I2C. Um dispositivo que inicia uma
mensagem é chamado de mestre, enquanto outro que responde a uma mensagem é chamado
de escravo.

Um dispositivo pode ser somente mestre, somente escravo ou alternar entre mestre e escravo,
conforme a necessidade do aplicativo.
O barramento I2C, como mostrado na Figura 1, pode conectar muitos chips, como conversores
(ADC e DAC), microcontroladores (µC) e outros em apenas dois fios. Cada dispositivo escravo
I2C tem seu próprio endereço exclusivo.

Quando um mestre envia uma mensagem, inclui o endereço do escravo no início da


mensagem.

Todos os dispositivos no barramento ouvem a mensagem, mas apenas o escravo que


reconhece o seu próprio endereço participa da transferência.

Fonte: Wikimedia Commons.


 Figura 1 – Barramento I2C.
Fonte: Wikimedia Commons.

O I2C também oferece suporte a vários dispositivos mestre (master) no barramento ao mesmo
tempo, um recurso poderoso que otimiza o uso deste último, reduzindo ao mínimo o tráfego de
mensagens.

Para oferecer suporte a vários mestres, o I2C deve resolver conflitos de sinal quando dois ou
mais dispositivos mestres tentarem falar no barramento ao mesmo tempo.

Esse feito, chamado de detecção de perda de arbitragem de barramento, permite que um


mestre detecte quando seus sinais de barramento estão em conflito com os de outro. Um
mestre que assim os detecta encerra seu uso do barramento, permitindo que a mensagem
gerada por outro mestre cruze o barramento ileso.

Aplicações iniciais para I2C incluíam controle de volume e contraste em rádios e televisores.


O I2C se expandiu para incluir uma ampla gama de aplicativos.

Atualmente, o I2C pode ser encontrado em uma variedade de sistemas computacionais,
industriais, de entretenimento, médicos e militares, com potencial de crescimento quase
ilimitado.

FUNÇÕES PARA PROTOCOLO I2C NO ARDUINO

O ambiente de desenvolvimento da plataforma Arduino utiliza biblioteca Wire para implementar


comunicação por protocolo I2C. No Arduino Uno, os pinos para I2C são A4 (SDA) e A5 (SCL).
Um exemplo de comunicação entre duas placas Arduino Uno pode ser montado como
mostrado na figura 2, realizada no simulador Tinkercad. Nesta montagem, os pinos A4, A5 e
GND de cada placa são ligados entre si.

Fonte: EnsineMe.
 Figura 2 – Montagem para comunicação I2C entre duas placas Arduino Uno.

Fonte: EnsineMe.

Na placa Arduino da esquerda, na Figura 2, um botão (push button) é ligado entre os pinos 12
e GND. Na placa da direita, um LED é montado entre os pinos 13 e GND. No programa, ao se
pressionar o botão na placa mestre (Arduino Uno da esquerda), o LED acende na placa
escravo (Arduino Uno da direita). Os códigos para a placa mestre e para a placa escravo são
mostrados mais adiante.

Observe bem o código para verificar como a transmissão é configurada com o escravo
definindo seu endereço como 5 no setup(). Quando o botão é pressionado, a placa da
esquerda emite um caractere H. Caso contrário, ela emite um caractere L. Assim, quando
recebe o caractere, a placa escravo apaga ou acende o LED em função do caractere recebido.
CÓDIGO PARA O I2C MESTRE

#include < Wire.h >

void setup() {

Wire.begin();

pinMode(12, INPUT_PULLUP);

void loop() {

// Endereço do Escravo é 5

if (digitalRead(12) == LOW) {

Wire.beginTransmission(5);

Wire.write('H');

Wire.endTransmission();

delay(100);

} else {

Wire.beginTransmission(5);

Wire.write('L');

Wire.endTransmission();

delay(100);

CÓDIGO PARA O I2C ESCRAVO

#include < Wire.h >

void setup() {

// Endereço é 5

Wire.begin(5);

Wire.onReceive(react);

pinMode(13,OUTPUT);

digitalWrite(13,LOW);

void loop() {

void react(int n) {

while (Wire.available()) {

char c = Wire.read();

if (c=='H') {

digitalWrite(13,HIGH);

} else {

digitalWrite(13,LOW);
}

FUNÇÕES PARA PIC

O compilador CCS oferece suporte para o I2C baseado em hardware e um dispositivo I2C
mestre baseado em software.

As funções principais da biblioteca I2C para PIC são:

i2c_start() Emite um comando de partida quando no modo mestre I2C.

i2c_write(data) Envia um único byte pela interface I2C.

i2c_read() Lê um byte da I2C.

i2c_stop() Emite um comando de parada quando no modo mestre I2C.

i2c_poll() Retorna um TRUE se o hardware recebeu um byte no buffer.


Atenção! Para visualização completa da tabela utilize a rolagem horizontal

PROTOCOLO UART
UART (Universal Asynchronous Transmitter Receiver – Transmissor Receptor Universal
Assíncrono), é o protocolo mais comum usado para comunicação serial full duplex, ou seja,
onde o dispositivo pode transmitir e receber dados ao mesmo tempo.
Como seu próprio nome sugere, ele foi projetado para realizar comunicação assíncrona,
enviando e recebendo dados de um sistema para outro. Assim, o transmissor e o receptor
usam os parâmetros bit de início, bit de parada e uma temporização para sincronizar uns com
os outros.

Dessa forma, como o protocolo não tem sinal de sincronismo, um parâmetro muito importante é
o Baud Rate, que especifica a velocidade de recepção e envio, sendo que os dois dispositivos
devem utilizar a mesma taxa.

A unidade do Baud Rate é bits por segundo (bps).

As taxas de comunicação assíncrona mais comumente usadas são 9600, 33600 e 115200 bps.

Em geral, um pino denominado RX é usado para representar o pino receptor de uma


comunicação serial e TX representa o transmissor em microcontroladores. O TX deve ser
ligado no RX, ou seja, transmissor enviando para o receptor.

UART E RS232

O UART é um protocolo para transferir os dados em série de um nó para outro. Logo,


especialmente em comunicação serial, o protocolo é obrigatório, obrigando-nos a informar ao
sistema quando começarmos a transmitir dados, quantos bits existem e como a transferência
deles terminará. Todas essas regras estão incluídas no protocolo.

PROTOCOLO

Um protocolo é conjunto de regras adotadas por todas as partes que participam na


comunicação, a fim de obter uma comunicação livre de erros.

Entretanto, quando queremos exibir nossos dados no computador, devemos transferi-los


através da porta COM. Nesse caso, temos o conceito de padrões de comunicação, que indica
a camada física para transferir os dados.
 COMENTÁRIO

Os antigos computadores possuíam portas que seguiam o padrão RS232 para aceitar os
dados, que empregava um conector de nove pinos (DB9).

De acordo com a norma RS232:

A Lógica 1 (nível alto) é aceita entre -3 V a -25 V.

A Lógica 0 (nível baixo) é aceita entre +3 V a +25 V.

O protocolo UART gera dados de acordo com a lógica TTL, nos quais a lógica 1 é aceita entre
2,4 V a 5 V e lógica 0 entre 0 a 0,4 V.

Portanto, os dados do protocolo UART não podem ser transferidos diretamente na porta RS232
do computador.

Um driver de linha, ou conversor chamado MAX232, é usado para converter níveis TTL em
níveis RS232. Para se comunicar com computadores modernos por UART pode-se usar
conversores conhecidos como USB/Serial UART. Estes criam uma COM virtual que é usada
assim como os computadores antigos usavam a RS232.

Da mesma forma, temos outros padrões que são perfeitamente compatíveis com o protocolo
UART, como os padrões RS485 e RS422, muito usados em ambientes industriais.

FUNÇÕES PARA USO DE PORTA SERIAL EM PIC

No compilador CCS, temos funções para enviar dados pela porta serial. O código a seguir pode
ser usado para acessar a porta serial na placa 4 (PICGenios) do simulador PICSimLab.

Verifique que o programa envia mensagens e depois fica aguardando caracteres pela porta
serial. Quando o programa recebe um caractere, ele o devolve.

#include < 18F4550.h >

#fuses NOMCLR INTRC_IO

#use delay(clock = 8000000)

#use rs232(uart1, baud = 9600) // Inicializa módulo UART

#include < stdio.h >

char message[] = "Microcontrolador PIC18F4550 - exemplo UART" ;

char i, j, textsize;

void main(){

setup_oscillator(OSC_8MHZ);

putc(13); // Vai para a primeira coluna

printf("Ola Mundo!"); // Escreve na UART

delay_ms(5000);

putc(13);

putc(10); // Nova linha

textsize = strlen(message); // Conta No de caracteres

for(j = 0; j < textsize; j++){

putc(message[j]);

delay_ms(100);

putc(13);

putc(10);

while(TRUE){

if(kbhit()){ // Se dado foi recebido

i = getc(); // Lê da UART

putc(i); // Envia de volta

A instrução #use rs232 (UART1, baud = 9600) é usada para configurar o protocolo UART.

As funções usadas no código C são:

printf putc

if(kbhit()) getc()

 Atenção! Para visualizaçãocompleta da tabela utilize a rolagem horizontal

PRINTF

Envia uma string de caracteres para o pino de transmissão RS232 (TX).


PUTC

Envia um caractere através do pino de transmissão RS232 (TX).

IF(KBHIT())

Testa se um caractere foi recebido e está pronto para a função getc().

GETC()

Lê o caractere.

FUNÇÕES PARA USO DE PORTA SERIAL EM


ARDUINO

O IDE do Arduino possui muitas funções para operar a porta serial.

SERIAL.PRINT()
Esta função imprime dados na porta serial como texto ASCII.

Esse comando pode ter várias formas. Os números são impressos usando um caractere ASCII
para cada dígito. Números de ponto flutuante são impressos de maneira similar a dígitos ASCII,
com duas casas decimais. Bytes são enviados como um único caractere. Caracteres e
sequências de caracteres são enviados como estão.
 EXEMPLO

Serial.print (54) escreve "54".

Serial.print (1,23456) escreve "1,23".

Serial.print ('N') escreve "N".

Serial.print ("Ola Mundo") escreve "Ola Mundo".

Um segundo parâmetro opcional especifica a base (formato) a ser usada.

Os valores permitidos são: BIN (binário ou base 2), OCT (octal ou base 8), DEC (decimal ou
base 10), HEX (hexadecimal ou base 16).

Para números de ponto flutuante, esse parâmetro especifica o número de casas decimais a
serem usadas.

 EXEMPLO

Serial.print (78, BIN) escreve "1001110".

Serial.print (78, OCT) escreve "116".

Serial.print (78, DEC) escreve "78".

Serial.print (78, HEX) escreve "4E".

Serial.print (1.23456, 0) escreve "1".

Serial.print (1.23456, 2) escreve "1,23".

Serial.print (1.23456, 4) escreve "1,2346".

SERIAL.AVAILABLE()
Esta função retorna o número de caracteres (ou seja, bytes de dados) que chegaram ao buffer
serial e estão prontos para serem lidos.

SERIAL.READ()
Esta função retorna o primeiro (mais antigo) caractere no buffer e remove esse byte de dados
desse local. Portanto, quando todos os bytes de dados são lidos e nenhum novo dado serial
chega, o buffer fica vazio e Serial.available() retornará 0.
READSTRINGUNTIL()
Esta função permite combinar todos os caracteres da mensagem enviada em uma única string
do Arduino.

Nesse caso, estamos aguardando o caractere \n, que é o caractere de nova linha que vem no
final de uma string enviada no monitor serial do Arduino.

O exemplo a seguir mostra o controle de acender, apagar ou piscar um LED no pino 9 da placa
Arduino Uno. Ele pode ser simulado no Tinkercad.

int LED = 9;

int i;

int n;

char c;

void setup(){

pinMode(LED , OUTPUT);

Serial.begin(9600);

void loop() {

Serial.print("Digite o numero referente a opcao desejada:\n");

Serial.print("0: para apagar o led:\n");

Serial.print("1: para acender o led;\n");

Serial.print("2: para piscar o led.\n");

while (!Serial.available()){
delay(50);

c = Serial.read();

switch(c){

case '0':

digitalWrite(LED , LOW);

break;

case '1':

digitalWrite(LED , HIGH);

break;

case '2':

Serial.print("Por quantos segundos o led piscará? (de 1 a 9:\n");

while (!Serial.available()){
delay(50);

c = Serial.read();

n = c-48;

n = constrain(n, 1, 9);

pisca(LED, n);

break;

default:

Serial.print("Entrada errada\n");

break;

void pisca(int LED, int t){

int r = 0;

while(r< t){

digitalWrite(LED, HIGH);

delay(500);

digitalWrite(LED, LOW);

delay(500);

r+=1;

PROTOCOLO SPI
A SPI (Serial Peripheral Interface ou Interface Serial Periférica) é um protocolo comumente
usado para enviar dados entre microcontroladores e periféricos externos pequenos, tais como
registradores de deslocamento, sensores e cartões SD.

O barramento SPI usa linhas de clock e dados separadas com uma linha de seleção para
escolher o dispositivo com o qual se deseja conversar, conforme apresentado na Figura 3.

Fonte: Cburnett/Wikimedia Commons/Licença GFDL


 Figura 3 – Barramento SPI.

Fonte: Cburnett/Wikimedia Commons/Licença GFDL

Para iniciar a comunicação SPI, o mestre (master) deve enviar o sinal do clock e selecionar o
escravo (slave) ativando o sinal CS.

 COMENTÁRIO

Normalmente, CS é um sinal ativo em nível baixo. Logo, o mestre deve enviar um valor lógico 0
nesse pino para selecionar o escravo. O SPI é uma interface full-duplex; tanto o mestre como o
escravo podem enviar dados ao mesmo tempo através das linhas MOSI e MISO,
respectivamente.

A borda do clock serial sincroniza o deslocamento e a amostragem dos dados. A interface SPI
fornece ao usuário flexibilidade para selecionar a borda de subida ou descida do clock para
amostrar e/ou deslocar os dados.

Muitos microcontroladores têm periféricos SPI integrados que lidam com todos os detalhes de
envio e recebimento de dados e podem fazê-lo em velocidades muito altas. O protocolo SPI é
simples, existindo bibliotecas em compiladores para Arduino e PIC que facilitam a utilização
dos dispositivos.
SIMULANDO A TRANSMISSÃO DE DADOS

VERIFICANDO O APRENDIZADO

1. O QUE PODEMOS DIZER SOBRE A TRANSMISSÃO SERIAL DE


DADOS?

I – PARA TRANSMITIR N BITS DEVEM SER USADAS N LINHAS DE


DADOS.

II – SÓ PODE SER SÍNCRONA, POIS CADA BIT É ENVIADO EM CADA


PULSO DO CLOCK (RELÓGIO).

III – TENDE A SER MAIS LENTA DO QUE A TRANSMISSÃO PARALELA DE


DADOS.

A) Somente a I está correta.

B) Somente a III está correta.

C) I e III estão corretas.

D) I e II estão corretas.

E) Todas estão corretas.

2. A SEQUÊNCIA DE CÓDIGO A SEGUIR FOI USADA NO IDE DO ARDUINO


COMO PARTE DE UM PROGRAMA A SER USADO EM UMA PLACA
ARDUINO UNO.

WIRE.BEGINTRANSMISSION(3);

WIRE.WRITE('A');

WIRE.ENDTRANSMISSION();

O QUE O TRECHO DE CÓDIGO FAZ?

A) Envia a letra A via UART com velocidade de 3000 bps.

B) Envia a letra A via SPI para um dispositivo escravo de endereço 3.

C) Envia a letra A via I2C para um dispositivo escravo de endereço 3.

D) Envia a letra A via I2C com velocidade de 3000 bps.

E) Envia a letra A via SPI com velocidade de 3000 bps.

GABARITO
1. O que podemos dizer sobre a transmissão serial de dados?

I – Para transmitir n bits devem ser usadas n linhas de dados.

II – Só pode ser síncrona, pois cada bit é enviado em cada pulso do clock (relógio).

III – Tende a ser mais lenta do que a transmissão paralela de dados.

A alternativa "B " está correta.

A transmissão serial de dados tende a ser mais lenta do que a transmissão paralela, pois os
bits são enviados um por vez, ao contrário da transmissão paralela. No entanto, ela pode ser
assíncrona, e para transmitir n bits somente uma linha de dados é necessária, linha esta que
transmite os dados de forma serial.

2. A sequência de código a seguir foi usada no IDE do Arduino como parte de um


programa a ser usado em uma placa Arduino Uno.

Wire.beginTransmission(3);

Wire.write('A');

Wire.endTransmission();

O que o trecho de código faz?

A alternativa "C " está correta.

A biblioteca Wire usada é para o protocolo I2C no Arduino. A função beginTransmission(3)


estabelece a comunicação com o escravo de endereço 3, enviando a letra A com a função
Wire.write('A'). A função Wire.endTransmission() encerra a transmissão com o escravo I2C e
libera o barramento para outras transmissões.

MÓDULO 2

 Descrever sensores e atuadores para a programação de sistemas de controle


embarcados
CONCEITO DE SENSORES E ATUADORES
Para que um sistema embarcado baseado em microcontrolador realize um controle, ele precisa
ser capaz de receber dados externos de dispositivos que medem grandezas físicas ou de ler
um sinal digital de entrada, além de poder atuar de alguma forma em dispositivos externos,
para acender uma luz, por exemplo.

Em outras palavras, um sistema embarcado para controle deve ser capaz de ler dados de
sensores e enviar dados para atuadores.

E O QUE SÃO SENSORES E ATUADORES?

Transdutor é o termo coletivo utilizado para os sensores, que podem ser acionados para
detectar uma ampla gama de diferentes formas de energia; e os atuadores, que podem ser
usados para alternar tensões ou correntes, “atuando” em um sistema.

Um sensor monitora as condições ambientais, tais como níveis de fluido, temperaturas,


vibrações ou tensão. À medida que essas condições ambientais mudam, elas são percebidas
por um sensor, que emite um sinal elétrico proporcional à condição. Logo, um sensor é um
dispositivo que detecta e responde a algum tipo de entrada do ambiente físico.

A entrada específica pode ser luz, calor, movimento, umidade, pressão ou vários outros de um
grande número de fenômenos ambientais.


A saída é um sinal elétrico que, quando recebido por um microcontrolador, pode ser
processado e convertido para exibição legível no local do sensor ou transmitido
eletronicamente por uma rede para leitura ou processamento posterior.

O sinal elétrico fornecido pelo sensor também pode ser usado pelo microcontrolador para
estabelecer um controle. Em função do processamento do sinal, o código embarcado pode
enviar um sinal a um atuador de um sistema e este realizar um movimento, ou seja, ele recebe
um sinal elétrico e o combina com uma fonte de energia para criar movimento físico.

Um atuador pode ser:

pneumático
hidráulico

elétrico

térmico

magnético

 EXEMPLO

Um pulso elétrico pode conduzir o funcionamento de um motor dentro de um sistema de


controle.

Os atuadores estão presentes em quase todas as máquinas ao nosso redor, desde sistemas
de controle de acesso eletrônico simples, como o vibrador dos telefones celulares, até
eletrodomésticos, veículos, dispositivos industriais e robôs. Exemplos comuns de atuadores
incluem motores elétricos, motores de passo, macacos hidráulicos, dentre outros.

Fonte: Shutterstock.com
 Fonte: Shutterstock.com
TIPOS DE SENSORES QUANTO AO SINAL
GERADO
Sensores analógicos estão presentes nos mais diversos equipamentos e sistemas. Eles
produzem um sinal de saída contínuo ou uma tensão que é geralmente proporcional à
quantidade sendo medida.

Quantidades físicas como temperatura, velocidade, pressão, deslocamento, deformação etc.


são todas grandezas analógicas, pois tendem a ser de natureza contínua.

 EXEMPLO

A temperatura de um líquido pode ser medida usando um termopar, que responde


continuamente com variações de tensão às alterações de temperatura, à medida que o líquido
é aquecido ou resfriado.

Os sensores digitais produzem uma saída discreta representando um número ou dígito binário,
como um nível lógico “0” ou um nível lógico “1”. Nesse sentido, um simples botão (push button)
de uma placa pode ser considerado um sensor digital, indicando quando o usuário o
pressionou.

Vejamos exemplos de utilização de sensores digitais e analógicos usados com


microcontroladores.

UTILIZAÇÃO DE SENSORES DIGITAIS


Um exemplo de sensor digital é o sensor PIR (passive infrared sensor), que permite perceber
movimento, quase sempre usado para detectar se um humano entrou ou saiu da faixa de
percepção do sensor.

Eles são pequenos, baratos, de baixa potência, fáceis de usar e não se desgastam. Por essa
razão, são comumente encontrados em aparelhos instalados em residências ou empresas.
Os PIRs são basicamente feitos de um sensor piroelétrico que pode detectar níveis de radiação
infravermelha (RI). Todos os objetos emitem alguma radiação de baixo nível, e quanto mais
quente algo está, mais radiação é emitida.

O sensor em um detector de movimento é dividido em duas metades. A razão para isso é que
estamos procurando detectar o movimento (mudança) e não os níveis médios de RI.

As duas metades estão ligadas de modo que se anulam mutuamente. Se uma metade
perceber mais ou menos radiação IR do que a outra, situação esta que é típica de movimento
na área de detecção (detecting area), a saída será alta ou baixa.

A Figura 4 ilustra a atuação do sensor PIR.

Fonte:Swagatam Majumdar/Wikimedia Commons


 Figura 4 – Atuação do sensor PIR.

Fonte:Swagatam Majumdar/Wikimedia Commons.

Sensor PIR com Arduino

A montagem da Figura 5, feita no simulador Tinkercad, mostra um exemplo de teste com o uso
de sensores digitais com microcontroladores. Nesta, um sensor PIR está ligado a uma placa
Arduino Uno.

Fonte: EnsineMe.
 Figura 5 – Montagem de sensor PIR com Arduino.

Fonte: EnsineMe.

Os fios vermelho e preto ligados ao sensor PIR são a alimentação do sensor, retirada do
Arduino.

O fio amarelo é a saída do sensor PIR, que informa nível lógico alto quando percebe
movimento.

O programa a seguir é usado para simular a aquisição de dados do sensor PIR.

void setup() {

pinMode(2, INPUT); // Pino 2 como entrada

pinMode(13, OUTPUT); // Pino 13 como saída

void loop() {

if (digitalRead(2) == HIGH) // Verifica se o sensor PIR está acionado

digitalWrite(13, HIGH); // Liga o LED da placa

delay(100); // Espera 100 milissegundos

digitalWrite(13, LOW); // Desliga o LED da placa

delay(100); // Espera 100 milissegundos

A simulação da percepção de movimento pelo sensor PIR é feita clicando-se no centro do


sensor e movimentando um círculo que aparece em área sombreada na frente deste, como
pode ser visto na Figura 5. Talvez seja necessário diminuir o zoom do campo onde é observada
a montagem para se ter acesso à área sombreada. Pelo código, a percepção de movimento faz
com que o LED da placa pisque.

UTILIZAÇÃO DE SENSORES ANALÓGICOS

SENSOR DE TEMPERATURA COM ARDUINO

Quando usamos sensores analógicos precisamos medir, em geral, uma tensão que é
proporcional à quantidade sendo medida. Esse é o caso dos sensores de temperatura.

 EXEMPLO

O LM35 ou o TMP36 são sensores de temperatura linear analógicos. Isso significa que a
tensão de saída é proporcional à temperatura.

A tensão de saída aumenta em 10 mV para cada aumento de 1 grau Celsius na temperatura. O


Arduino pode ler a entrada de 0 a 5 V com suas entradas analógicas ligadas aos conversores
analógicos/digitais (ADC). O Arduino armazena isso como um número de 10 bits (0-1023).

O sensor de temperatura pode ser montado como mostrado na Figura 6.

Fonte: EnsineMe.
 Figura 6 – Montagem de sensor de temperatura com Arduino.

Fonte: EnsineMe.

O código a seguir mostra a aquisição dos dados do sensor e a conversão para temperatura,
com envio do valor em graus Celsius para a porta serial. O código pode ser copiado e editado
no Tinkercad.

char grau = 176; //Valor ASCII de Grau

void setup()

pinMode(A0,INPUT);

Serial.begin(9600);

void loop()

int tmp = analogRead(A0);

float voltage = (tmp * 5.0)/1024;//(5*temp)/1024 para converter em tensão

float milliVolt = voltage * 1000; //para converter em milivolts.

float tmpCel = (milliVolt-500)/10 ; //Para TMP36 sensor. Range(−40°C a +125°C)

Serial.print("Celsius: ");

Serial.print(tmpCel);

Serial.println(grau);

delay(1000);

O valor de temperatura, simulado no sensor, pode ser alterado ao se clicar nos ícones do
Tinkercad durante a simulação, alterando o valor como mostrado na Figura 7.

Fonte: EnsineMe.
 Figura 7 – Alteração de temperatura no simulador.

Fonte: EnsineMe.

SENSOR DE ULTRASSÔNICO COM


ARDUINO
Um sensor muito utilizado em projetos de robótica é o sensor ultrassônico HC-SR04.

Ele usa sonar para determinar a distância de um objeto, assim como os morcegos.

Oferece excelente detecção de alcance sem contato, com alta precisão e leituras estáveis
em um pacote fácil de usar.

Ele vem completo com módulos transmissores e receptores ultrassônicos.

Veja na Figura 8 o que acontece.


Fonte: Adaptado de How to Electronics.


 Figura 8 – Princípio de funcionamento do sensor de distância por ultrassom.

Fonte: Adaptado de How to Electronics.

O transmissor (pino de trigger) envia um sinal, um som de alta frequência. Quando o sinal
encontra um objeto, ele é refletido e o receptor (pino de eco) o recebe. O tempo entre a
transmissão e a recepção do sinal nos permite calcular a distância até um objeto. Isso é
possível porque sabemos a velocidade do som no ar, que é de aproximadamente 343 m/s.

O sensor de distância do simulador Tinkercad usa o mesmo pino para transmitir e receber o
sinal. A Figura 9 mostra uma possível montagem.

Fonte: Wikimedia Commons.


 Figura 9 – Montagem do sensor de distância no simulador Tinkercad.

Fonte: EnsineMe.

A entrada digital 7 da placa é utilizada para realizar a medida de tempo entre o pulso enviado e
recebido. Esse tempo é usado para calcular a distância. O código se encontra a seguir.

int trigPin = 7; // Trigger

int echoPin = 7; // Eco

long duracao, cm;

void setup() {

Serial.begin (9600);

void loop() {

// O sensor é disparado por um pulso alto (HIGH) de 10 ou mais microssegundos.

// Dê um pulso baixo (LOW) curto antes para garantir um pulso alto limpo:

pinMode(trigPin, OUTPUT);

digitalWrite(trigPin, LOW);

delayMicroseconds(5);

digitalWrite(trigPin, HIGH);

delayMicroseconds(10);

digitalWrite(trigPin, LOW);

// Lê o sinal do sensor em uma duração de tempo em microssegundos

pinMode(echoPin, INPUT);

duracao = pulseIn(echoPin, HIGH);

// Converte o tempo em uma distância

cm = (duracao/2) / 29.1; // Divide por 29.1

Serial.print(cm);

Serial.print("cm");

Serial.println();

delay(250);

Veja que um pulso alto é preciso para disparar o envio do pulso ultrassônico. Para se ler o
intervalo de tempo entre o pulso de envio e retorno é usada a função pulseIn().
Se o valor HIGH é passado para a função (como nesse exemplo), esta última espera o pino ir
do estado LOW para o HIGH, começa a contar o tempo e, então, espera o pino ir para o estado
LOW para finalizar essa contagem, devolvida em microssegundos.

Como queremos saber a distância em centímetros, tivemos que usar a velocidade do som no
ar em centímetros por segundo, 34300 cm/s. Depois, calculamos quantos microssegundos o
som leva para percorrer um centímetro. Para isso, basta resolver a seguinte regra de três:

1000000 µs => 34300 cm

Xµs => 1cm

De acordo com o resultado, são necessários 29.15 µs para o som percorrer um centímetro.

Agora, com o valor da medida de tempo em microssegundos para o som ir e voltar ao sensor
(variável duração), basta dividir esse tempo por 29.15 para descobrir quantos centímetros ele
teve que percorrer.

Como o som precisa fazer o trajeto de ida e volta, finalizamos com a divisão do resultado por
dois para termos a distância entre o sensor e o objeto.

UTILIZAÇÃO DE ATUADORES
Diversos tipos de atuadores podem ser usados em sistemas embarcados com
microcontroladores. Além de motores DC, que podem ser acionados com sinal PWM, há
também outros tipos, como os motores de passo e os servo motores.

A característica de um servo motor é permitir que seja colocado em posições angulares


específicas por um sinal codificado. Servos são usados em aviões controlados por rádio, para
posicionar superfícies de controle como elevadores e lemes em robótica e muitas outras
aplicações.

 COMENTÁRIO

Os servos variam em tamanho, desde miniatura para pequenos projetos até tamanhos maiores
para fins industriais.
Dentro da biblioteca do Arduino existe um conjunto de funções que permite o envio de
comando para servo motores pequenos. O código a seguir faz uso dessas funções, que estão
na biblioteca servo.h.

Veja que a variável tipo servo que foi criada pode receber o comando write e, como parâmetro,
o ângulo para posicionar o servo motor.

Assim, um loop for para movimentar o servo de 0 a 90 graus, e de 90 a 0, continuamente, é


criado com a variação do ângulo de 1 em 1 grau.

O intervalo de 50 milissegundos após cada alteração em 1 grau permite o movimento contínuo,


que leva no total 9 segundos (180x50 milissegundos) para retornar ao ponto inicial.

A montagem no simulador Tinkercad é mostrada na Figura 10.

#include < Servo.h >

#define SERVO 9 // Porta Digital 9 PWM

Servo s; // Variável tipo Servo

int pos; // Posição Servo

void setup ()

s.attach(SERVO);

s.write(0); // Inicia o motor na posição zero

void loop()

for(pos = 0; pos <= 90; pos++)

s.write(pos);

delay(50);

for(pos = 90; pos >= 0; pos--)

s.write(pos);

delay(50);

Fonte: EnsineMe.
 Figura 10 – Montagem de atuador servo motor no simulador Tinkercad.

Fonte: EnsineMe.

EMPREGANDO SENSORES E ATUADORES


VERIFICANDO O APRENDIZADO

1. UM SENSOR DE TEMPERATURA POSSUI UMA CONVERSÃO DE


TENSÃO PARA TEMPERATURA DADA PELA FÓRMULA ABAIXO:

TEMPERATURA EM GRAUS CELSIUS (°C) = [(TENSÃO EM MV) - 500] / 10.

SABENDO QUE UM MICROCONTROLADOR PODE LER VALORES DE 0 A


3,3 VOLTS, QUAL O RANGE DE TEMPERATURA QUE PODERÁ SER
MEDIDO?

A) -45 °C a 500 °C

B) 50 °C a 1000 °C

C) -50 °C a 280 °C

D) 0 a 500 °C

E) -45 °C a -450 °C

2. FOI SOLICITADO UM PROJETO, UTILIZANDO ARDUINO, QUE


CONTROLA A FREQUÊNCIA COM QUE UM LED PISCA EM FUNÇÃO DA
PROXIMIDADE DE UM OBJETO. PARA ISSO, SE UTILIZOU UM SENSOR
DE DISTÂNCIA POR ULTRASSOM. DESSA FORMA, QUANTO MAIS
PRÓXIMO O OBJETO DO SENSOR, MAIS RÁPIDO O LED DEVERÁ
PISCAR. ESCOLHA A OPÇÃO QUE COMPLETA O CÓDIGO A SEGUIR DE
FORMA CORRETA PARA CONSEGUIR ESSE EFEITO NO LED DO PINO 13
DO ARDUINO UNO.

INT TRIGPIN = 7; // TRIGGER DO SENSOR DE DISTÂNCIA

INT ECHOPIN = 7; // ECO DO SENSOR DE DISTÂNCIA

LONG DURACAO, MM;

VOID SETUP() {

VOID LOOP() {

PINMODE(TRIGPIN, OUTPUT);

DIGITALWRITE(TRIGPIN, LOW);

DELAYMICROSECONDS(5);

DIGITALWRITE(TRIGPIN, HIGH);

DELAYMICROSECONDS(10);

DIGITALWRITE(TRIGPIN, LOW);

// LÊ O SINAL DO SENSOR EM UMA DURAÇÃO DE TEMPO EM MICROSSEGUNDOS

PINMODE(ECHOPIN, INPUT);

DURACAO = PULSEIN(ECHOPIN, HIGH);

// CONVERTE O TEMPO EM UMA DISTÂNCIA EM MM (MILÍMETROS)

MM = (DURACAO/2) / 291;

// CÓDIGO PARA CONSEGUIR O EFEITO DESEJADO

A)
analogWrite (13, HIGH);

delay (mm);

A)

B)

digitalWrite (13, HIGH);

delay (mm);

B)

C)

digitalWrite (13, LOW);

delay (mm);

digitalWrite (trigPin, HIGH);

delay (mm);

C)

D)

digitalWrite (trigPin, HIGH);

delay (mm);

digitalWrite (trigPin, LOW);


delay (mm);

D)

E)

digitalWrite (13, HIGH);

delay (mm);

digitalWrite (13, LOW);

delay (mm);

E)

GABARITO

1. Um sensor de temperatura possui uma conversão de tensão para temperatura dada


pela fórmula abaixo:

Temperatura em graus Celsius (°C) = [(tensão em mV) - 500] / 10.

Sabendo que um microcontrolador pode ler valores de 0 a 3,3 volts, qual o range de
temperatura que poderá ser medido?

A alternativa "C " está correta.

Para tensão de 0 V, a temperatura é igual a (0-500)/10 = -50 °C.

Para tensão de 3,3 V, a temperatura é igual a (3300 – 500)/10 = 280 °C.

Logo, o range varia de -50 °C a 280 °C.

2. Foi solicitado um projeto, utilizando Arduino, que controla a frequência com que um
LED pisca em função da proximidade de um objeto. Para isso, se utilizou um sensor de
distância por ultrassom. Dessa forma, quanto mais próximo o objeto do sensor, mais
rápido o LED deverá piscar. Escolha a opção que completa o código a seguir de forma
correta para conseguir esse efeito no LED do pino 13 do Arduino Uno.

int trigPin = 7; // Trigger do sensor de distância

int echoPin = 7; // Eco do sensor de distância

long duracao, mm;

void setup() {

void loop() {

pinMode(trigPin, OUTPUT);

digitalWrite(trigPin, LOW);

delayMicroseconds(5);

digitalWrite(trigPin, HIGH);

delayMicroseconds(10);

digitalWrite(trigPin, LOW);

// Lê o sinal do sensor em uma duração de tempo em microssegundos

pinMode(echoPin, INPUT);

duracao = pulseIn(echoPin, HIGH);

// Converte o tempo em uma distância em mm (milímetros)

mm = (duracao/2) / 291;

// CÓDIGO para conseguir o efeito desejado

A alternativa "E " está correta.

O código acende o LED no pino 13 com a função digitalWrite (13, HIGH). Em seguida, aguarda
um intervalo em milissegundos, com delay (mm). Esse valor em milissegundos (mm) é igual ao
valor da distância para o obstáculo em milímetros, calculada anteriormente. Depois apaga o
LED pelo mesmo intervalo de tempo. Com isso, quanto menor a distância, mais rápido o LED
piscará.

MÓDULO 3

 Identificar as funções para a programação de mostradores na criação de interfaces


com o usuário

DISPLAY DE CARACTERES E GRÁFICO


Caso o objetivo seja que o microcontrolador compartilhe informações ou mostre o que ele está
tentando fazer, é preciso conectar um mostrador de saída ou display.

Um mostrador de saída é algo que fornece uma maneira de mostrar informações do


microcontrolador.

Ou seja, o mostrador permite que o microcontrolador envie informações para ele. Já


trabalhamos com outro mostrador de saída bem simples, chamado LED (Light Emitting Diode),
que emite luz quando é programado para isso.

Começaremos agora a ver mais detalhes para a programação do LCD (Liquid Crystal Display –
Display de Cristal Líquido), de displays de 7 segmentos e de displays gráficos.
LCD (LIQUID CRYSTAL DISPLAY – DISPLAY
DE CRISTAL LÍQUIDO)
O LCD possui um material que une as propriedades do líquido e dos cristais. Ele tem uma faixa
de temperatura dentro da qual as partículas são essencialmente tão móveis quanto poderiam
ser em um líquido, no entanto, são reunidas em uma forma semelhante a um cristal.

 COMENTÁRIO

O LCD é um mostrador de saída muito mais informativo do que um único LED, pois é um
display que pode mostrar facilmente os caracteres em sua tela. Eles variam em tamanho, preço
e configuração, e podem ter desde algumas linhas até telas grandes. Alguns são até mesmo
projetados especificamente para um único aplicativo, tendo apenas a capacidade de exibir
gráficos definidos.

O LCD recebe informações e controle, e os requisitos são restritos para garantir que as
informações sejam enviadas a ele de forma que possa aceitá-las apropriadamente.

Primeiro, há uma discrepância de velocidade entre o LCD e o microcontrolador, pois este último
é muito mais rápido que o primeiro. Portanto, o programa do microcontrolador deve estar
totalmente ciente disso e compensar o tempo que o LCD fica ocupado trabalhando em dados
enviados anteriormente.

Os compiladores atuais já conhecem essa necessidade de temporização para acionar LCDs e


automaticamente atuam para que eles recebam os comandos e dados no tempo certo.

INTERFACE DE LCD 16×2 COM


MICROCONTROLADOR
Um LCD muito popular é o que tem duas linhas e 16 caracteres por linha.
Um LCD desse tipo pode ser operado em dois modos: modo de 4 bits e modo de 8 bits. Ele
possui dois registradores: um de comando e outro de dados.

Além do pino GND e Vcc (alimentação de 5 V), o display possui 8 pinos para envio de dados
em paralelo (DB0 a DB7), ajuste de contrastes (VEE), pino de controle para selecionar envio de
dados para o registro de comando ou de dados (RS), leitura ou escrita no registro (RW), pino
de habilitação do dispositivo (EN) e entrada para alimentação da luz de fundo (backlight) nos
pinos Led+ e Led-.

Ao conectar as três linhas de seleção e as linhas de dados com o microcontrolador, as


mensagens podem ser exibidas no LCD.

Os caracteres escritos se baseiam na tabela ASCII. Neste LCD, cada caractere é exibido em
uma matriz de 5×7 pixels.

A Figura 11 apresenta a pinagem desse módulo. O pino de terra (GND) é o pino 1.

Fonte: EnsineMe.
 Figura 11 – Pinagem de LCD 16x2.

Fonte: EnsineMe.

 ATENÇÃO
Quando o display LCD não está habilitado, as linhas de dados ficam em um estado de alta
impedância. Isso significa que não interferem na operação do microcontrolador quando o
display não é usado.

A linha de controle EN (habilitar) é usada para permitir o envio de dados para o LCD. Uma
transição de alto para baixo nesse pino habilitará o módulo. Quando RS (Seleção de
Registrador) é baixo, os dados devem ser tratados como uma instrução de comando. Quando
RS está alto, os dados que estão sendo enviados são exibidos na tela. Assim, para exibir
qualquer caractere na tela, definimos RS alto.

Quando RW (linha de controle de leitura/gravação) está baixa, as informações no barramento


de dados estão sendo gravadas no LCD. Quando RW está alto, o programa está efetivamente
lendo o LCD. A linha RW sempre será baixa.

Funções para uso de LCD de caracteres no PIC

Para programar a escrita em displays LCD de caracteres, é preciso seguir passos de envio de
comandos com temporização adequada, para que o display esteja preparado para receber os
dados, o valor ASCII de cada caractere que se deseja escrever.

Todo display possui um controlador que define que comandos devem ser utilizados. É esse
controlador que designa como devem ser os comandos para operar o LCD.

As funções auxiliam a utilização do LCD, realizam sua inicialização e também a dos passos e
temporizações necessárias para que o controlador tenha tempo de enviar os dados e acender
os caracteres no display.

No compilador CCS, as funções lcd_init() e LCD_PUTC (utilizada como parâmetro da função


printf) são as principais usadas para operar o LCD.

O código a seguir pode ser utilizado no LCD da placa 4 (PICGenios) do simulador PICSimLab.

#include < 18F4550.h >

#fuses HS, NOMCLR, NOWDT


#use delay(clock=20MHz)

#define LCD_DB0 PIN_D0

#define LCD_DB1 PIN_D1

#define LCD_DB2 PIN_D2

#define LCD_DB3 PIN_D3

#define LCD_DB4 PIN_D4

#define LCD_DB5 PIN_D5

#define LCD_DB6 PIN_D6

#define LCD_DB7 PIN_D7

#define LCD_E PIN_E1

#define LCD_RS PIN_E2

#include < meu_lcd.c >

void main () {

lcd_init();

printf(LCD_PUTC, "Usando LCD");

delay_ms(1000);

printf(LCD_PUTC, "\nTemp = %f", 23.6);

Funções para uso de LCD de caracteres em Arduino

No IDE do Arduino existem muitas funções, dentro da biblioteca LiquidCrystal, para operar um
display LCD. Essa biblioteca permite que uma placa Arduino controle as telas (LCDs) baseadas
no chipset Hitachi HD44780 (ou compatível), encontrado na maioria dos LCDs baseados em
caracteres.

As principais funções dessa biblioteca são encontradas a seguir.

LIQUIDCRYSTAL()
Cria uma variável do tipo LiquidCrystal. O display pode ser controlado usando 4 ou 8 linhas de
dados. Para usar 4 bits, omita os números dos pinos de d0 a d3 e deixe essas linhas
desconectadas. O pino RW pode ser ligado ao terra (GND). Nesse caso, omita-o dos
parâmetros dessa função.

Sintaxe: os parâmetros a seguir devem ser substituídos pelo número do pino do Arduino ao
qual o pino correspondente do display está ligado.

LiquidCrystal(rs, enable, d4, d5, d6,


d7).

LiquidCrystal(rs, rw, enable, d4, d5,


d6, d7).

LiquidCrystal(rs, enable, d0, d1, d2,


d3, d4, d5, d6, d7).

LiquidCrystal(rs, rw, enable, d0, d1, d2, d3, d4, d5, d6, d7).
BEGIN()
Inicializa a interface para a tela LCD e especifica as dimensões (largura e altura) da tela. A
função begin() precisa ser chamada antes de qualquer outro comando da biblioteca LCD.

Sintaxe: lcd.begin (colunas, linhas).

SETCURSOR()
Posiciona o cursor do LCD, isto é, define a localização na qual o texto subsequente gravado no
LCD será exibido.

Sintaxe: lcd.setCursor (coluna, linha).

CLEAR()
Limpa a tela LCD e posiciona o cursor no canto superior esquerdo.

Sintaxe: lcd.clear().

Uma montagem do display LCD no simulador Tinkercad é mostrada na Figura 12.

Fonte: Fonte: EnsineMe.


 Figura 12 – Montagem do display 16x2 no Tinkercad.

Fonte: EnsineMe.

O código a seguir mostra um contador sendo apresentado no display.

/*

Circuito:

* Pino LCD RS ligado ao pino digital 12

* Pino LCD Enable ligado ao pino digital 11

* Pino LCD D4 ligado ao pino digital 5

* Pino LCD D5 ligado ao pino digital 4

* Pino LCD D6 ligado ao pino digital 3

* Pino LCD D7 ligado ao pino digital 2

* Pino LCD R/W ligado ao GND

* Pino LCD GND ligado ao GND

* Pino LCD VCC ligado ao 5V

* Pino LCD VO ligado ao GND

* Pino LED+ ligado ao Resistor 220 ohms

* Resistor 220 Ohms ligado ao 5V

* Pino LED- ligado ao GND

*/

#include < LiquidCrystal.h >

LiquidCrystal lcd(12, 11, 5, 4, 3, 2);

void setup() {

lcd.begin(16, 2);

lcd.print("Usando o LCD");

void loop() {

lcd.setCursor(0, 1);

// Imprime o número de segundos

delay(1000);

lcd.print(millis() / 1000);

A função lcd.setCursor(0, 1) define a posição no LCD onde serão escritos os próximos


caracteres. O parâmetro (0, 1) corresponde à coluna e à linha respectivamente, começando em
0. Assim, nessa instrução o LCD irá escrever na segunda e na primeira coluna.

DISPLAY DE 7 SEGMENTOS
O display de 7 segmentos consiste em sete LEDs organizados de forma retangular, conforme
mostrado na Figura 13.

Cada um dos sete LEDs é chamado de segmento porque, quando iluminado, o segmento faz
parte de um dígito numérico (decimal e hexadecimal) a ser exibido.
Um oitavo LED adicional às vezes é usado dentro do mesmo pacote, permitindo a indicação de
um ponto decimal (DP) quando dois ou mais monitores de 7 segmentos são conectados juntos
para exibir números maiores que dez.

Fonte: Uln2003/Wikimedia Commons.


 Figura 13 – Display de 7 segmentos.

Fonte: Uln2003/Wikimedia Commons.

Cada um dos sete LEDs no display recebe um segmento posicional com um de seus pinos de
conexão. Esses pinos LED individuais são rotulados de A até G, representando cada LED
individual. Os outros pinos LED são conectados juntos para formar um pino comum.

Portanto, ao polarizar os pinos apropriados dos segmentos de LED em uma ordem específica,
alguns segmentos estarão acesos e outros não, permitindo que o padrão de caracteres
desejado do número seja gerado na tela. Isso nos permite exibir cada um dos dez dígitos
decimais de 0 a 9 na mesma exibição de 7 segmentos.

Dependendo do dígito decimal a ser exibido, um conjunto específico de LEDs é polarizado


diretamente.

 EXEMPLO
Para exibir o dígito numérico 0, precisaremos acender seis dos segmentos de LED
correspondentes a A, B, C, D, E e F. Assim, os vários dígitos de 0 a 9 podem ser exibidos
usando um display de 7 segmentos.

CÓDIGO PARA USAR DISPLAY DE 7


SEGMENTOS NO PIC
Para escrever os dígitos no display de 7 segmentos, usaremos alguns artifícios de
programação para facilitar.

Criaremos um vetor com 10 posições, uma para cada dígito.


Cada posição será um número binário de 8 dígitos, um para cada segmento do display (A, B,
C, D, E, F, G e ponto).


Nesse número binário, consideraremos o dígito menos significativo como o segmento A, o
seguinte como B, e assim sucessivamente.


Dessa forma, para acender somente o segmento A, escreveríamos o número binário
0b00000001.

O vetor dígitos[], cujo índice corresponde a um número de 0 a 9, fica assim:

digitos[] = {

0b00111111, 0b00000110, 0b01011011, 0b01001111,

0b01100110, 0b01101101, 0b01111100, 0b00000111,

0b01111111, 0b01100111}
A placa 4 do simulador PICSimLab possui 4 displays de 7 segmentos. Eles estão ligados na
porta D (RD0 a RD7 correspondendo de A até ponto) e são multiplexados por quatro chaves
transistores, acionadas pelos pinos RA2, RA3, RA4 e RA5 (entradas DISP1, DISP2, DISP3 e
DISP4).

Como são multiplexados, cada display deve ser aceso por vez para apresentar um dígito. Esse
intervalo entre acender um display e outro deve ser rápido o suficiente para dar a ilusão visual
de que todos estão acesos ao mesmo tempo.

O código a seguir permite que se escreva os dígitos 1, 2, 3 e 4 em cada display. Ele pode ser
usado para criar um projeto no MPLAB para o PIC18F4550, compilar e usar o arquivo .hex
gerado para rodar o simulador PICSimLab.

#include < 18F4550.h >

#fuses XT, NOMCLR, NOWDT

#use delay(clock=4MHz)

int8 digitos[] = {

0b00111111, 0b00000110, 0b01011011, 0b01001111,

0b01100110, 0b01101101, 0b01111100, 0b00000111,

0b01111111, 0b01100111

};

void ligaDisplay(int pino, int numero) {

output_bit (PIN_A3, 0);

output_bit (PIN_A4, 0);

output_bit (PIN_A5, 0);

output_bit (PIN_A2, 0);

output_bit (pino, 1);

output_d(digito[numero]);

delay_ms(25);

void main () {

while(true) {

ligaDisplay (PIN_A2, 1);

ligaDisplay (PIN_A3, 2);

ligaDisplay (PIN_A4, 3);

ligaDisplay (PIN_A5, 4);

}
DISPLAYS GRÁFICOS
Um display gráfico é exatamente o que seu nome sugere: um visor matricial capaz de exibir
imagens, letras e números gerados por meio do firmware do microcontrolador. Qualquer
aplicativo que precisa exibir mais do que apenas letras e números pode usar um display
gráfico.

Os visores matriciais são identificados por dois conjuntos de números.

 EXEMPLO

É o caso de 128x64. Esse display contém 128 pontos ao longo do eixo X (ou horizontal) e 64
pontos ao longo do eixo Y (ou vertical).

Cada um desses pontos, às vezes chamados de pixel, pode ser ligado e desligado
independentemente um do outro.

O programador faz uso do software para informar a cada ponto quando ligar e desligar, e a
tecnologia utilizada pode ser a do próprio LCD.

As configurações comuns de display LCD gráfico incluem 122x32, 160x160, 240x128 e


320x240.

Outra tecnologia para display gráfico que vem crescendo e se popularizando é a de OLED.

DISPLAY OLED
Os displays OLED (Organic Light-Emitting Diode – Diodo Orgânico Emissor de Luz) para uso
em monitores de TV têm uma excelente qualidade de imagem, apresentando cores brilhantes,
contraste infinito, taxa de resposta rápida e ângulos de visão amplos.

Fonte: Shutterstock.com
 Fonte: Shutterstock.com

O principal componente de um display OLED é o emissor OLED, um material orgânico (à base


de carbono) que emite luz quando a eletricidade é aplicada. A estrutura básica de um OLED é
uma camada emissiva “ensanduichada” entre um cátodo (que injeta elétrons) e um ânodo (que
remove elétrons).

 COMENTÁRIO

Os dispositivos OLED modernos usam muito mais camadas para torná-los mais eficientes e
duráveis, mas a funcionalidade básica permanece a mesma.

Os OLEDs são muito sensíveis ao oxigênio e à umidade, portanto, a camada de


encapsulamento é crítica.

Mostradores gráficos de OLED para uso em sistemas embarcados podem ser encontrados por
baixíssimo custo no mercado. Os que trabalham com o controlador SSD1306 são um
exemplo.

Ele pode se comunicar com o microcontrolador de várias maneiras, incluindo I2C e SPI.

O SPI é geralmente mais rápido do que o I2C, mas requer mais pinos de E/S (Entrada e
Saída) . Por outro lado, o I2C requer apenas dois pinos e pode ser compartilhado com outros
periféricos I2C. Graças à versatilidade do controlador SSD1306, o módulo vem em diferentes
tamanhos e cores, por exemplo, 128x64 e 128×32, com OLEDs brancos ou azuis. 

A tensão de operação do controlador SSD1306 é de 1,65 V a 3,3 V, enquanto o painel OLED


requer uma tensão de alimentação de 7 V a 15 V.

Todos esses diferentes requisitos de energia são satisfeitos usando os circuitos internos de
carga. Isso torna possível conectá-lo a um Arduino ou a qualquer microcontrolador de 5 V
facilmente, sem usar nenhum conversor de nível lógico.

CÓDIGO PARA USAR UM OLED NO


ARDUINO
Usando a biblioteca Wire para o protocolo I2C e mais uma biblioteca específica para o
controlador SSD1306, é possível acionar um display OLED com o código a seguir no Arduino.

#include < Wire.h >

#include < Adafruit_SSD1306.h >

#include < Adafruit_GFX.h >

// OLED display endereço


#define OLED_ADDR 0x3C

Adafruit_SSD1306 display(-1);

void setup() {

// Inicializa e limpa o display

display.begin(SSD1306_SWITCHCAPVCC, OLED_ADDR);

display.clearDisplay();

display.display();

// Escreve uma linha de texto

display.setTextSize(1);

display.setTextColor(WHITE);

display.setCursor(27,30);

display.print("Ola!");

// Atualiza o display

display.display();

void loop() {

//

EMPREGANDO DISPLAYS

VERIFICANDO O APRENDIZADO
1. O TRECHO DE CÓDIGO A SEGUIR FOI USADO PARA ESCREVER EM
UM LCD DE 16 CARACTERES (COLUNAS) POR LINHA EM DUAS LINHAS,
LIGADO A UMA PLACA ARDUINO. ONDE SERÁ ESCRITO O DÍGITO 5?

LCD.SETCURSOR(0, 1);

LCD.PRINT(“125”);

A) Na segunda coluna da segunda linha

B) Na primeira coluna da segunda linha

C) Na terceira coluna da primeira linha

D) Na terceira coluna da segunda linha

E) Na segunda coluna da primeira linha

2. O TRECHO DE CÓDIGO A SEGUIR PARA O COMPILADOR CCS C FOI


USADO PARA ESCREVER EM UM LCD DE 16 CARACTERES POR LINHA
EM DUAS LINHAS, LIGADO A UM MICROCONTROLADOR PIC. O QUE
SERÁ ESCRITO E EM QUE POSIÇÃO NO LCD?

VOID MAIN () {<BR/>

  LCD_INIT();<BR/>

  PRINTF(LCD_PUTC, “
LCD 123”);<BR/>

A) Será escrito “
LCD 123” na primeira linha.

B) Será escrito “LCD 123” na primeira linha.

C) Será escrito “
LCD 123” na segunda linha.

D) Será escrito “LCD 123” na segunda linha.

E) Será escrito “123” na segunda linha.

GABARITO
1. O trecho de código a seguir foi usado para escrever em um LCD de 16 caracteres
(colunas) por linha em duas linhas, ligado a uma placa Arduino. Onde será escrito o
dígito 5?

lcd.setCursor(0, 1);

lcd.print(“125”);

A alternativa "D " está correta.

A função lcd.setCursor(0, 1) indica a posição no LCD onde serão escritos os próximos


caracteres. O parâmetro (0, 1) corresponde à coluna e à linha respectivamente, começando em
0. Assim, nesta instrução o LCD começará a escrever o número 125 na segunda e na primeira
coluna. Logo, o digito 5 será escrito na terceira coluna da segunda linha.

2. O trecho de código a seguir para o compilador CCS C foi usado para escrever em um
LCD de 16 caracteres por linha em duas linhas, ligado a um microcontrolador PIC. O que
será escrito e em que posição no LCD?

void main () {<br/>

  lcd_init();<br/>

  printf(LCD_PUTC, “
LCD 123”);<br/>

A alternativa "D " está correta.

No CCS C a função printf foi adaptada para enviar caracteres ao LCD quando recebe o
parâmetro LCD_PUTC. Os caracteres são escritos como na função printf já conhecida.

Assim, o comando \n indica pular linha e a frase “LCD 123” é escrita na segunda linha.

MÓDULO 4

 Reconhecer a importância da programação de interrupções para o recebimento


otimizado de dados de dispositivos externos ao microcontrolador
INTERRUPÇÕES
Considere este cenário:

Precisamos monitorar uma entrada de microcontrolador e responder a essa entrada ativa


dentro de apenas alguns microssegundos, e não em dezenas de milissegundos, como
acontecia com os apertos de botão humanos.


O programa pode estar em qualquer ponto de sua execução quando essa entrada for ativada.


É necessário fazer o equivalente a monitorar esse sinal não cem vezes por segundo, mas
centenas de milhares de vezes por segundo, enquanto são efetuadas todas as outras
operações que nosso programa precisa realizar.

Como podemos responder tão rapidamente e ainda ter tempo para fazer qualquer outro
trabalho em nosso programa?

As interrupções são a resposta.

MECANISMO DE INTERRUPÇÃO
Uma interrupção é um sinal assíncrono para a atenção do processador que pode ser originado
no hardware ou no software. O mecanismo de interrupção fornece uma maneira de evitar o
desperdício de tempo do processador, livrando-se de rotinas de pesquisa ineficazes em loops
fechados.

As interrupções permitem que o processador continue seu trabalho até que o evento que
dispara a interrupção ocorra. Também garante que a CPU receberá um sinal sempre que
ocorrer um evento que requeira sua atenção.
Uma interrupção é, portanto, um sinal para a CPU começar imediatamente a executar um
código diferente, código este que é escrito para responder à causa da interrupção.

 ATENÇÃO

O advérbio “imediatamente”, utilizado aqui, pode significar, na melhor das hipóteses, “assim
que terminar a instrução atual”.

O tempo entre a geração da solicitação de interrupção e a entrada na rotina que a atende é


chamado de latência de interrupção.


E, quanto menor for esse intervalo (latência mais baixa), será sempre melhor.

A CPU se lembrará da localização da próxima instrução que iria executar, armazenando seu
endereço em um registrador ou local de memória, e então pulará diretamente para o código
designado pelo programador para aquela interrupção particular.

Além de salvar o endereço da próxima instrução, muitas vezes também salvará o registrador
de status da CPU e desabilitará outras interrupções. Ela também irá, na maioria dos casos,
limpar automaticamente a solicitação de interrupção que acionou, de forma que uma única
solicitação de interrupção não resulte na entrada de uma rotina várias vezes.

Uma CPU de um microcontrolador é projetada para responder a um número de fontes de


interrupção diferentes (mais de 10, normalmente), e cada fonte pode ter um código específico,
escrito pelo usuário, que é executado quando a interrupção é acionada. O código executado
para uma interrupção é chamado de Rotina de Serviço de Interrupção ou ISR.

 VOCÊ SABIA

Caso fosse possível assistir à execução do código em câmera lenta, o contador do programa
estaria se movendo de uma instrução para a próxima e, então, quando a interrupção fosse
acionada, o contador de programa iria repentinamente parar em alguma área totalmente
diferente do programa, o ponto de entrada da ISR. Então, quando o ISR estivesse completo, o
contador de programa apontaria de repente para a próxima instrução como se nada tivesse
acontecido.

As interrupções estão sempre desligadas ou desabilitadas ao sair de reset. Na verdade, elas


são duplamente desativadas.

Cada fonte de interrupção individual é desabilitada, assim como o sinalizador de interrupção


global da CPU. Para que uma interrupção aconteça e seu ISR seja executado, ambas as
interrupções individuais devem ser habilitadas e o mesmo deve suceder com as interrupções
globais da CPU.

Finalmente, e como não poderia deixar de ser, a própria condição de interrupção deve ocorrer.
Um pino do microcontrolador sendo direcionado para nível baixo por um evento externo, por
exemplo, pode ser um acionamento programado de uma interrupção específica. Nesse caso,
seria uma interrupção externa.

Cabe ao programador selecionar quais interrupções habilitar e em quais pontos da execução


do programa habilitá-las. Uma vez habilitada, a interrupção pode ser desabilitada novamente e
então reabilitada, tão frequentemente quanto for necessário.

O programa também terá uma ISR (muito semelhante a uma função) para cada interrupção
que será habilitada, e cada uma dessas ISRs será mapeada para sua fonte de interrupção
correspondente.

 ATENÇÃO

É crucial que qualquer interrupção ativada sempre tenha uma ISR válida mapeada para ela.
Caso contrário, quando a interrupção disparar, a CPU começará a tentar executar o código em
algum local sem instruções, causando uma quebra no programa.

APLICAÇÕES DE INTERRUPÇÕES
Interrupções são úteis em muitas situações de programação. Por exemplo:

Para evitar que a CPU seja travada enquanto aguarda um processo iniciar. Um uso comum
para uma interrupção é notificar o processador de que uma transferência de dados pode
ocorrer.

Para responder a condições importantes do hardware, tais como o pressionamento de um


interruptor, o desencaixe de uma alavanca ou a ação em um sensor.

Para responder a eventos críticos quanto ao tempo, como uma ação que deve ocorrer
imediatamente em uma condição de falha de energia.

Para fornecer uma saída de uma rotina na ocorrência de uma condição de erro.

Para manter o controle do tempo entre execuções de uma rotina.

Como podem ser habilitadas várias interrupções no programa, elas são comumente associadas
para a realização de uma tarefa.

Imagine que se deseje contar quantos pulsos aparecem em um pino de um microcontrolador a


cada segundo.


Uma forma de interrupção à qual todos os microcontroladores podem responder é uma simples
mudança digital de estado em um pino especificado (geralmente chamado de pino de
interrupção externa).


É possível programar uma interrupção externa de modo que, cada vez que um pulso apareça,
uma rotina de interrupção seja ativada para acumular a contagem desses pulsos em uma
variável contador.

Outra rotina, disparada pelo temporizador e chamada a cada segundo, pode pegar esse
contador e guardar o valor resultante, este valor representa a contagem de pulsos por
segundo, gerando assim, uma taxa de contagem.

Essa mesma rotina pode fazer cálculos, apresentar os dados ao usuário ou enviá-los por rede
para outro sistema.
TIPOS E FORMAS DE INTERRUPÇÕES
EXTERNAS
Existem algumas maneiras diferentes de configurar uma interrupção externa, dependendo do
que o microcontrolador permite.

Em primeiro lugar, as interrupções externas podem ser disparadas por borda ou por nível.

Uma interrupção disparada por borda gera uma solicitação de interrupção apenas em uma
borda, isto é, quando a linha de interrupção vai de um estado para o estado oposto (1 -> 0 ou 0
-> 1).

Uma interrupção acionada por nível gera uma solicitação de interrupção sempre que a linha de
interrupção está no estado ativo. Sendo assim, uma interrupção acionada por baixo nível irá
gerar solicitações sempre que a linha estiver baixa. Além disso, um dado importante de ser
registrado é que ela continuará a gerar solicitações até que a linha seja elevada.

Se o software ou o hardware forem projetados incorretamente, uma única interrupção


disparada por nível pode resultar em um ciclo furioso de interrupções, com milhares ou milhões
de vezes por segundo.

O segundo aspecto de uma interrupção externa é a polaridade da interrupção.

Para uma interrupção disparada por borda, a polaridade pode ser disparada pela borda
ascendente (0 -> 1) ou pela borda descendente (1 -> 0).

Para uma interrupção acionada por nível, a polaridade pode ser acionada por nível baixo
(sempre que a entrada for 0) ou por nível alto (sempre que a entrada for 1).

Novamente, essas são opções de configuração possíveis, mas nem toda entrada de
interrupção externa em cada microcontrolador oferecerá suporte a todas essas opções.

É ainda possível haver uma entrada de interrupção externa que dispara em qualquer mudança
lógica, ou seja, gerará uma solicitação de interrupção em uma transição alto-baixo (1 -> 0) ou
em uma transição baixo-alto (0 -> 1). Essa é apenas uma interrupção disparada por borda que
dispara automaticamente em ambas as bordas possíveis, em vez de apenas em uma borda
especificada.
 ATENÇÃO

Observe também que a distinção entre interrupções acionadas por borda e por nível é
fundamental para todos os tipos de interrupções, não apenas as externas.

É importante, ao escrever a rotina de interrupção, que se tenha uma ideia do que precisa ser
feito imediatamente, no início da rotina, e o que pode ser feito depois.

Frequentemente, se desejará alterar uma ou mais saídas ou registradores ou ler uma ou mais
entradas/saídas bem no início da rotina.

Em seguida, é possível concluir todas as outras tarefas que devem ser feitas na rotina. Essa
regra depende intimamente de qual é a fonte de interrupção, de qual é a ação rotina desejada
e, na maioria dos casos, do que o hardware associado precisa.

SINALIZADORES E PRIORIDADE DE
INTERRUPÇÃO
Qualquer sinalizador de solicitação de interrupção definido, considerando que a permissão de
interrupção associada esteja definida, é considerado “pendente”.

Se as interrupções globais do microcontrolador estiverem habilitadas e o mesmo


acontecer com uma ou mais interrupções separadas, o hardware da CPU verificará
automaticamente todos esses sinalizadores de interrupção separados durante a execução de
cada instrução.

Se apenas um sinalizador for encontrado definido, no final da instrução atual a CPU salta
para a rotina dessa interrupção. Ao fazer isso, o sinalizador de interrupção pode ser apagado
automaticamente (isso depende do projeto), o endereço de retorno (o endereço da próxima
instrução que seria executada) é salvo e talvez alguns sinalizadores de CPU também o sejam.
Com isso, possivelmente, alguns registradores de CPU serão comutados e a rotina de
interrupção começará a ser executada.

Se os sinalizadores de solicitação de mais de uma interrupção habilitada forem


encontrados definidos, isto é, se mais de uma interrupção estiver pendente, a situação fica
mais complicada.
Sempre haverá uma prioridade atribuída a cada fonte de interrupção. Essa prioridade pode ser
fixada no hardware ou configurável pelo programador.

A interrupção pendente com a prioridade mais alta é a que será atendida.

Os outros sinalizadores de solicitação de interrupção permanecerão definidos, ou seja, as


outras interrupções permanecerão em um estado pendente.

O que acontece, a seguir, com uma rotina de interrupção em execução e outras interrupções
pendentes também depende do projeto da CPU.

Alguns microcontroladores desabilitam interrupções globais como parte do processo de


vetorização de interrupção.

Assim, a rotina começa a funcionar com as interrupções globais desabilitadas e quaisquer


outras interrupções pendentes “em espera”.

Quando a rotina termina, o estado de interrupção global será definido como parte da
restauração do estado que ocorre no final de uma rotina de interrupção.

Definir o estado de interrupção global aqui é legítimo, uma vez que ele teve que ser habilitado
para que a rotina fosse inserida em primeiro lugar. Portanto, a partir daí a próxima instrução do
código de fundo interrompido começará a ser executada e a CPU reconhecerá que há uma ou
mais interrupções pendentes. Novamente, a interrupção pendente de maior prioridade será
atendida no final dessa instrução, com quaisquer outras restantes pendentes, e assim por
diante.

Até agora, foram descritas apenas interrupções que interceptam o código de segundo plano.
Mas o que acontece quando uma interrupção tenta barrar outra interrupção?

 EXEMPLO

Suponha duas interrupções externas habilitadas, INT1 e INT2.

INT1 foi acionada e sua rotina de interrupção está em execução, então vem uma solicitação de
interrupção em INT2. O que acontece agora?

Nesse caso, uma das duas situações a seguir pode ocorrer. A nova interrupção pode ser
bloqueada até que a rotina INT1 termine, quando, então, ela será atendida. Ou, se as
interrupções foram habilitadas dentro da rotina de INT1, ou se INT2 foi definido com uma
prioridade mais alta do que INT1, a rotina de INT1 será interrompida assim como aconteceu
com o código de segundo plano, então a rotina de interrupção de INT2 será executada.

Quando terminar INT2, a rotina de INT1 continuará a funcionar.

CUIDADOS NO USO DE INTERRUPÇÃO


Com tamanha flexibilidade e potencial, o uso de interrupções é recomendado.

Elas são essenciais para o cumprimento de requisitos de tempo em sistemas embarcados, mas
também podem destruir os dados de maneiras que pareceriam impossíveis.

É importante prestar atenção nas chamadas de interrupção programadas e nas prioridades


atribuídas para que seus perigos sejam evitados.

A maioria dos problemas com interrupções está relacionada ao seu principal benefício, que é a
execução do código poder ser interrompida praticamente a qualquer momento. E o código
nunca sabe que foi interrompido.

É como se fosse possível congelar o tempo para o código de segundo plano, fazer o que quiser
com os dados ou E/S e, em seguida, reiniciar o tempo para o código de segundo plano.

Em uma analogia, seria como se alguém muito mais rápido que você retirasse a sua cadeira no
exato instante em que se preparava para se sentar. É assim que pode ser um software mal
projetado que usar interrupções.

Um exemplo de cuidado que se deve ter é na passagem de dados entre o código de segundo
plano e a rotina de interrupção. Ela deve ser evitada, mas não é uma circunstância rara para
uma interrupção. Em boa parte das rotinas de interrupção os programadores se utilizam de
passagem de dados para o código de segundo plano e/ou a recuperação de dados do código
de segundo plano, e é aí que algo pode dar errado. A ocorrência de problemas pode se dar
sempre que o código de segundo plano está no meio do acesso aos dados e uma interrupção
acontece, fazendo com que a rotina associada acesse os mesmos dados.

 RECOMENDAÇÃO
Evitar ao máximo que duas rotinas atualizem os mesmos dados é mais seguro, mas nem
sempre contornável. Por isso, alguns testes e uma análise minuciosa no código são sempre
boas práticas na utilização de interrupções.

PROGRAMAÇÃO DE INTERRUPÇÃO
EXTERNA
Um exemplo de programação poderia ser um LED acionado por interrupção ao toque em um
botão. Dessa forma, se o pino é mantido alto por um resistor pullup e, em seguida, é reduzido
por algum sinal ou ação, a transição de alto para baixo no pino pode desencadear uma
interrupção.

Podemos realizar um programa de LED controlado por botão para detectar a transição desse
botão por meio de interrupção externa, alterando o LED de acordo. Outro LED pode ser
acionado por uso de função delay (atraso). Isso nos dará uma introdução simples às
interrupções externas.

O primeiro passo é consultar a ficha técnica do chip para ver qual(is) pino(s) suporta(m)
interrupções externas.

INTERRUPÇÃO EXTERNA NO PIC


O programa a seguir demonstra o uso de interrupção externa com o compilador CCS no
PIC18F4550 e pode ser testado na placa 4 do PICSimLab.

O PIC18F4550 possui três interrupções externas de hardware – INT0, INT1 e INT2.

Elas estão nos pinos RB0, RB1 e RB2 da PORTB. Essas interrupções são acionadas pela
borda, ou seja, disparadas pela borda ascendente ou pela borda descendente do pulso.

#include < 18F4550.h >

#fuses XT, NOWDT, NOMCLR

#use delay(clock=4MHz)

#INT_EXT

void int_externa0() {

output_toggle(PIN_D1);

void main () {

enable_interrupts(GLOBAL);

enable_interrupts(INT_EXT);

while(true) {

output_toggle(PIN_D0);

delay_ms(1000);

Verifica-se que o clique no botão de interrupção 0, botão RBO/INT na placa 4, irá alternar
imediatamente entre acesso e apagado o LED D1, não importando se o programa esteja na
função de atraso (delay) que acende e apaga o LED D0.

Trocando a instrução enable_interrupts(INT_EXT) por enable_interrupts(INT_EXT_H2L) ou


enable_interrupts(INT_EXT_L2H) pode-se controlar se a interrupção ocorre na borda de
descida (H2L) ou subida (L2H), respectivamente.

Para habilitar e usar outras interrupções externas, por exemplo, a interrupção 1, basta usar as
diretivas:

#INT_EXT1

void externa1() {

………………;

E também

enable_interrupts(GLOBAL);

enable_interrupts(INT_EXT1);

INTERRUPÇÃO EXTERNA NO ARDUINO


O Arduino Uno usa os pinos 2 e 3 como pinos de interrupção externa. Na montagem da Figura
14, o pino 2 permanece em nível alto, indo a nível baixo quando se aperta o botão.

Fonte: EnsineMe
 Figura 14 – Montagem no simulador Tinkercad.

Fonte: EnsineMe

Essa transição de nível alto para baixo é capturada pelo programa a seguir utilizando a
interrupção externa com a função attachInterrupt() que chama a ISR ledchange.

bool on=false;

void setup()

pinMode(10, OUTPUT);

attachInterrupt(0, ledchange, FALLING);

void loop()

delay(10000);

void ledchange(){

on=!on;

if(on)

digitalWrite(10, LOW );

else

digitalWrite(10, HIGH);

}
EMPREGANDO INTERRUPÇÕES

VERIFICANDO O APRENDIZADO

1. A CONTAGEM DE PULSOS POR SEGUNDO DEVE SER USADA PARA


SE SABER A TAXA DE RADIAÇÃO MEDIDA POR UM DETECTOR DE
RADIAÇÃO. QUANTAS INTERRUPÇÕES SÃO NECESSÁRIAS PARA
CONTAR OS PULSOS POR SEGUNDO QUE CHEGAM A UM PINO DE UM
MICROCONTROLADOR?

A) Duas interrupções externas

B) Uma interrupção externa e uma interna

C) Duas interrupções internas e uma externa

D) Nenhuma interrupção

E) Uma interrupção interna

2. O QUE O PROGRAMA PARA O COMPILADOR CCS C A SEGUIR FAZ EM


UMA PLACA COM PIC?

#INCLUDE < 18F4550.H >

#FUSES XT, NOWDT, NOMCLR

#USE DELAY(CLOCK=4MHZ)

INT16 C = 0;

#INT_EXT

VOID INT_EXTERNA0() {

C++;

VOID MAIN () {

ENABLE_INTERRUPTS(GLOBAL);

ENABLE_INTERRUPTS(INT_EXT_L2H);

WHILE(TRUE) {

}
A) Incrementa a variável c toda vez que o pino da interrupção externa 0 muda de estado baixo
para alto.

B) Incrementa a variável c toda vez que o pino da interrupção externa 0 muda de estado alto
para baixo.

C) Incrementa a variável c toda vez que o pino da interrupção externa 0 muda de estado.

D) Incrementa a variável c a cada segundo.

E) Incrementa a variável c a cada segundo se o pino da interrupção externa 0 muda de estado.

GABARITO

1. A contagem de pulsos por segundo deve ser usada para se saber a taxa de radiação
medida por um detector de radiação. Quantas interrupções são necessárias para contar
os pulsos por segundo que chegam a um pino de um microcontrolador?

A alternativa "B " está correta.

Uma interrupção externa permite chamar uma rotina de interrupção que incremente um
contador a cada pulso percebido no pino externo. Uma interrupção interna por temporizador
permite que a cada intervalo de um segundo uma rotina informe quantos pulsos foram
contados e indique a taxa de radiação.

2. O que o programa para o compilador CCS C a seguir faz em uma placa com PIC?

#include < 18F4550.h >

#fuses XT, NOWDT, NOMCLR

#use delay(clock=4MHz)

Int16 c = 0;

#INT_EXT

void int_externa0() {

c++;

void main () {

enable_interrupts(GLOBAL);

enable_interrupts(INT_EXT_L2H);

while(true) {

A alternativa "A " está correta.

As aplicações possuem motores DC com acionadores de movimento e, portanto, a modulação


por largura de pulso, PWM, pode ser usada nessas aplicações.

CONCLUSÃO

CONSIDERAÇÕES FINAIS
Neste tema, discutimos os tipos mais comuns de periféricos externos aos microcontroladores e
como acioná-los usando protocolos de comunicação, bem como receber acionamento destes
por interrupção.

Também exemplificamos a programação e a comunicação com periféricos externos em


microcontroladores da família PIC e da plataforma Arduino, mostrando como testar esses
códigos em simuladores.

Agora você tem condições de projetar sistemas que empreguem periféricos externos
conectados aos microcontroladores.
AVALIAÇÃO DO TEMA:

REFERÊNCIAS
MONK, S. Programação com Arduino: começando com sketches. 1. ed. Porto Alegre:
Bookman, 2017.

OLIVEIRA, A. S. de; ANDRADE, F. S. de. Sistemas embarcados: hardware e firmware na


prática. 1. ed. São Paulo: Érica, 2010.

PECKOL, J. K. Embedded Systems: a contemporary design tool.  1 ed. Nova Jersey, EUA:
Wiley, 2019.

ZANCO, W. da S. Microcontroladores PIC18 com Linguagem C: uma abordagem prática e


objetiva. São Paulo: Érica, 2010.

EXPLORE+
Para saber mais sobre os assuntos tratados neste tema, leia os artigos:
Automação de tanques para aquicultura, de Kevin Manoel Guimarães e Daniel Lohmann.

Construção de um protótipo de um braço robótico microcontrolado com interface de


comunicação com um microcomputador, de Leandro Almeida Santos e Enrique Peter
Rivas Padilla.

CONTEUDISTA
Marcos Santana Farias

 CURRÍCULO LATTES

Você também pode gostar