Escolar Documentos
Profissional Documentos
Cultura Documentos
Visão Geral
O Arduino Uno R3 apresenta 14 pinos que podem ser utilizados como entradas
ou saídas digitais (pinos 0 a 13), sendo que os pinos 3, 5, 6, 9, 10 e 11 também podem
utilizar Pulse Width Modulation (PWM) para gerar um conjunto de valores inteiros
entre 0 e 255. Os pinos A0 a A5 correspondem às entradas analógicas, que recebem
uma tensão entre 0 e 5V e o produzem em uma escala de 0 a 1023. Também temos os
pinos 3,3V, 5V e GND (Terra) permitem alimentar os componentes dos circuitos
conectados ao Arduino. Possui um microprocessador ATmega328P, com uma memória
RAM de 2KB, memória Flash de 32KB e clock de 16MHz.
Os 32 registradores de uso geral são identificados como R0, R1, R2 até R31,
conforme ilustra a Figura 3.
Cada bit desses registradores corresponde a um único pino, por exemplo, o bit
mais baixo dos registradores DDRB, PORTB e PINB se refere exclusivamente ao pino
PB0, isto é, ao pino digital 8 do Arduino.
I T H S V N Z C SREG
7 6 5 4 3 2 1 0
#include <stdio.h>
Variáveis e Operadores
Uma variável, atributo ou até mesmo o valor de retorno de uma função, deve
ser previamente declarado devendo apresentar um tipo de dado. A seguir são
apresentados os principais tipos de dado existentes na Linguagem C:
A declaração de uma variável deve apresentar o seu tipo de dado, o nome que
a variável terá e, opcionalmente, o seu valor inicial. Por exemplo, no seguinte
programa declaramos duas variáveis inteiras.
#include <stdio.h>
Representação Representação
Operador Operador
Simbólica Simbólica
Atribuição = Maior >
Adição + Maior ou igual >=
Subtração - Menor <
Multiplicação * Menor ou igual <=
Divisão / Igual a ==
Resto % Diferente de !=
Condicional ?: E (And) &&
Incremento ++ Ou (Or) ||
Decremento -- Não (Not) !
Representação Representação
Operador Operador
Simbólica Simbólica
E (And) & Ou (Or) |
Ou Exclusivo (Xor) ^ Não (Not) !
Deslocamento a Deslocamento a
<< >>
Esquerda Direita
#include <stdio.h>
27 26 25 24 23 22 21 20
0 0 0 0 0 0 1 1
27 26 25 24 23 22 21 20
0 0 0 0 1 1 0 0
Obtendo, desta forma, o valor 12 que será exibido através da função printf.
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <stdio.h>
int main() {
int i = 1;
while (i <= 10) {
printf("%d, ", i++);
}
}
#include <stdio.h>
int main() {
int i = 1;
do {
printf("%d, ", i++);
} while (i <= 10);
}
#include <stdio.h>
int main() {
for (int i = 1; i <= 10; i++) {
printf("%d, ", i);
}
}
int v[5];
Observe que realizamos a declaração de um vetor que poderá receber até cinco
números inteiros. Em vetores e matrizes o primeiro elemento é acessado com o índice
recebendo o valor 0 (zero). Para acessar um elemento dentro de um vetor devemos
referenciar entre colchetes o índice da posição desejada, por exemplo, para atribuir
um valor para a primeira posição do vetor devemos realizar o seguinte comando:
v[0] = 45;
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#define TAM 10
Como exemplo, vamos criar uma função chamada somar que irá receber, como
parâmetros, dois números reais. Em seguida, realizará a soma deles e retornará o
resultado obtido, ou seja:
Uma vez declarada (criada) a função é só realizar a chamada sempre que for
necessário o seu uso, como mostrado a seguir.
#include <stdio.h>
Este ponteiro irá apontar para o endereço de memória alocado para a variável
x. O uso do símbolo “e” comercial antes do nome da variável indica que estamos
obtendo “o endereço” da variável ao invés do seu conteúdo. Desta forma, na linha p =
&x, temos que o ponteiro p recebeu o endereço da variável x, ou seja, aponta para o
endereço de x.
#include <stdio.h>
int main(void) {
int x = 10;
int *p;
p = &x;
printf("O endereco de X é: %p\n", p);
printf("O valor armazenado no endereco: %d\n", *p);
}
void setup() {
pinMode(13, OUTPUT);
}
void loop() {
digitalWrite(13, HIGH);
delay(1000);
digitalWrite(13, LOW);
delay(1000);
}
int main(void) {
// Special Function Registers (SFR): DDRB e PORTB
Na sequência vamos definir o Pino PB5 como saída, para isso devemos ligar o
bit 5 (DDB5) do registrador DDRB, conforme mostra a instrução a seguir. Com o intuito
de preservar a configuração dos demais bits do registrador DDRB, usamos a operação
lógica OU (OR) ao atualizarmos o conteúdo.
*dado_porta_b |= 0b00100000;
Sendo relevante salientar que a mesma operação pode ser especificada desta
outra maneira:
O LED será aceso quando o bit 5 do registrador PORTB for ligado, desta forma,
temos:
*dado_porta_b |= 0b00100000;
Por outro lado, para desligar o LED, devemos colocar zero no respectivo bit, ou
seja:
Como queremos usar este pino como saída (OUTPUT), na linha 6 do programa,
precisamos ligar (1) o bit 5 do registrador DDRB (Port B Data Direction Register).
Salientando que iniciamos a contagem dos bits em zero e da direita para a esquerda.
Além disso, para preservar a configuração dos demais bits do registrador DDRB,
usamos a operação lógica OU (OR) ao atualizarmos o conteúdo.
Ou:
DDRB |= 0b00100000;
Em seguida, o pino 13 (PB5) será ativado, ligando o LED que está conectado a
ele. Então, o registrador PORTB será usado, note que usamos a operação de OU de
modo a preservar o estado dos demais pinos.
PORTB |= 0b00100000;
Após aguardar 1 segundo desligamos o pino PB5 (Linha 10). Para fazer isso o bit
5 deverá ser colocado em zero. Neste caso, para preservar o estado das demais pinos,
usamos a operação de AND, sendo que apenas o bit a ser desligado (PB5) deverá
conter o valor zero.
int main(void) {
DDRB |= 0b00100000;
while (true) {
PORTB |= 0b00100000;
_delay_ms(1000);
PORTB &= 0b11011111;
_delay_ms(1000);
}
}
int main(void) {
DDRB |= (1 << DDB5);
while (1) {
PORTB |= (1 << PORTB5);
_delay_ms(1000);
PORTB &= ~(1 << PORTB5);
_delay_ms(1000);
}
}
Neste caso, DDB5 e PORTB5 possuem o valor 5, que representa o bit 5 dos
registradores DDRB e PORTB, respectivamente. Então, observe a linha:
Note que foi usado o operador << (deslocamento de bits à esquerda) sobre o
valor 1. Para entender o uso do operador, temos que 1 é representado em binário do
seguinte modo:
0 0 0 0 0 0 0 1
int main(void) {
DDRB |= (1 << DDB5);
while (1) {
PORTB ^= (1 << PORTB5);
_delay_ms(1000);
}
}
Neste ponto, o programa está pronto para ser transferido para a memória flash
do ATmega328P, para realizar este passo vamos utilizar o utilitário avrdude da
maneira indicada pela linha de comando mostrada a seguir. Onde porta corresponde à
porta no qual o Arquino está conectado ao computador, por exemplo, COM3.
Material necessário:
• 1 Arduino.
• 3 Resistores de 220 Ohms (vermelho, vermelho, marrom) ou de 330 Ohms
(laranja, laranja, marrom).
• 3 LEDs (1 vermelho, 1 verde e 1 amarelo).
• 1 Protoboard.
• Cabos de ligação.
int main(void) {
DDRB |= (1 << DDB5);
DDRB |= (1 << DDB4);
DDRB |= (1 << DDB3);
while (1) {
PORTB |= (1 << PORTB3);
PORTB &= ~ (1 << PORTB5);
_delay_ms(1000);
PORTB &= ~ (1 << PORTB3);
PORTB |= (1 << PORTB4);
_delay_ms(1000);
PORTB &= ~ (1 << PORTB4);
PORTB |= (1 << PORTB5);
_delay_ms(1000);
}
}
Material necessário:
• 1 Arduino.
• 1 Resistor de 220 Ohms (vermelho, vermelho, marrom) ou 330 Ohms (laranja,
laranja, marrom).
• 1 Display de LEDs de 7 segmentos (cátodo ou ânodo comum).
• 1 Protoboard.
• Cabos de ligação.
void limpar() {
PORTD &= ~(1 << PORTD7);
PORTB &= ~(1 << PORTB0);
PORTB &= ~(1 << PORTB1);
PORTB &= ~(1 << PORTB2);
PORTB &= ~(1 << PORTB3);
PORTB &= ~(1 << PORTB4);
PORTB &= ~(1 << PORTB5);
}
int main(void) {
DDRD |= (1 << DDD7); // Segmento "a"
DDRB |= (1 << DDB0); // Segmento "b"
DDRB |= (1 << DDB1); // Segmento "c"
DDRB |= (1 << DDB2); // Segmento "d"
DDRB |= (1 << DDB3); // Segmento "e"
DDRB |= (1 << DDB4); // Segmento "f"
DDRB |= (1 << DDB5); // Segmento "g"
while (1) {
limpar();
for (int cont = 9; cont >= 0; cont--) {
exibir(cont);
_delay_ms(1000);
}
}
}
Material necessário:
• 1 Arduino.
• 1 Chave Táctil (push button).
• 1 Resistor de 220 Ohms (vermelho, vermelho, marrom) ou 330 Ohms (laranja,
laranja, marrom).
• 1 Resistor de 10k Ohms (marrom, preto laranja).
• 1 LED (qualquer cor).
• 1 Protoboard.
• Cabos de ligação.
int main(void) {
DDRB |= (1 << DDB5);
DDRD &= ~(1<< DDD2);
while (1) {
if ((PIND & (1 << PIND2)) == (1 << PIND2))
PORTB |= (1 << PORTB5);
else
PORTB &= ~(1 << PORTB5);
_delay_ms(50);
}
}
Neste caso, o LED permanece aceso apenas enquanto a chave táctil estiver
pressionada. Podemos alterar a lógica do programa de modo a manter o estado, ou
seja, ao pressionar e soltar o botão o LED permanece acesso, até que um novo
pressionamento ocorre. Para isso, usaremos uma técnica chamada debounce que,
basicamente, consiste em identificar quando ocorre a transição de estado da chave
táctil, comparando o estado atual com o estado anterior.
Observe neste código-fonte o uso das variáveis valor e anterior, sendo que
apenas ocorrerá mudança no estado do LED quando ocorre uma transição na chave
táctil, ou seja, a variável valor contém 1 (o botão foi pressionado) e a variável anterior
tem o valor 0 (o botão não estava pressionado).
int main(void) {
DDRB |= (1 << DDB5);
DDRD &= ~(1<< DDD2);
char valor;
char estado = 0;
if (estado == 1)
PORTB |= (1 << PORTB5);
else
PORTB &= ~(1 << PORTB5);
anterior = valor;
_delay_ms(50);
}
}
Material necessário:
• 1 Arduino.
• 1 Chave Táctil (push button).
• 3 Resistores de 220 Ohms (vermelho, vermelho, marrom) ou 330 Ohms
(laranja, laranja, marrom).
• 1 Resistor de 10k Ohms (marrom, preto laranja).
• 3 LEDs (qualquer cor).
• 1 Protoboard.
• Cabos de ligação.
int main(void) {
DDRB |= (1 << DDB5);
DDRB |= (1 << DDB4);
DDRB |= (1 << DDB3);
DDRD &= ~(1<< DDD2);
char valor;
char estado = 0;
char anterior = 0;
while (1) {
valor = (PIND & (1 << PIND2)) == (1 << PIND2);
if (valor == 1 && anterior == 0) {
estado++;
if (estado == 8)
estado = 0;
}
PORTB &= 0b000111;
PORTB |= (estado << PORTB3);
anterior = valor;
_delay_ms(50);
}
}
Onde:
Onde:
• RXCIE0 (RX Complete Interrupt Enable) permite habilitar interrupção com RXC0.
• TXCIE0 (TX Complete Interrupt Enable) possibilita habilitar a interrupção com
TXC0.
• UDRIE0 (Data Register Empty Interrupt Enable) habilitar a interrupção com
UDRE0.
• RXEN0 (Receiver Enable) permite a recepção de dados através da USART.
• TXEN0 (Transmitter Enable): habilita a transmissão de dados através da USART.
• UCSZ02 (UART Character Size): em conjunto com UCSZ00 e UCSZ01 determina
o tamanho do quadro de dados.
• RXB80 (Receive Data Bit 8): nono bit recebido para quadros de dados de 9 bits.
• TXB80 (Transmit Data Bit 8): nono bit transmitido para quadros de dados de 9
bits.
Sendo que os bits UMSEL01 e UMSEL00 (USART Mode Select) definem o modo
de operação considerando a seguinte tabela:
O bit USBS0 (USART Stop Bit Select) deve conter o valor 0 para selecionar um
bit de parada ou 1 para definir 2 bits de parada (Stop Bit).
UCSR0B UCSR0C
Bits de Dados UCSZ02 UCSZ01 UCSZ00
5-bit 0 0 0
6-bit 0 0 1
7-bit 0 1 0
8-bit 0 1 1
Reservado 1 0 0
Reservado 1 0 1
Reservado 1 1 0
9-bit 1 1 1
O registrador UDR0 (USART Data Register) contém os bits de dados que serão
transmitidos ou que foram recebidos.
Material necessário:
• 1 Arduino.
• 1 Resistor de 220 Ohms (vermelho, vermelho, marrom) ou 330 Ohms (laranja,
laranja, marrom).
• 1 Led (qualquer cor).
• 1 Protoboard.
• Cabos de ligação.
void USART_Init() {
UBRR0L = BAUD_PRESCALE;
UBRR0H = (BAUD_PRESCALE >> 8);
UCSR0B = (1 << TXEN0) | (1 << RXEN0);
UCSR0C = (1 << UCSZ01) | (1 << UCSZ00);
}
char USART_Rx() {
while (!(UCSR0A & (1<<RXC0)));
return (UDR0);
}
int main(void) {
DDRB |= (1 << DDB5);
USART_Init();
USART_Tx_String("Pressione A para acender ou apagar o
LED:\n\r");
while (1) {
switch (USART_Rx()) {
case 'A':
case 'a':
Por outro lado, a função USART_Rx monitora o bit RXC0 (Receive Complete) do
registrador UCSR0A. Este bit apresentará o valor 1 quando os dados forem recebidos e
estiverem disponíveis no registrador UDR0 (USART Data Register). Quando isto ocorre
a inscrição while é encerrada e o conteúdo o registrador UDR0 e retornado pela
função.
Com base neste conceito, podemos usar a seguinte fórmula para determinar a
tensão que está sendo recebida pelo ADC.
Onde o valor binário especificado nos bits MUX3, MUX2, MUX1 e MUX0
representa o canal a ser selecionado, ou seja:
3 2 1 0
MUX3 MUX2 MUX1 MUX0 Canal
0 0 0 0 ADC0
0 0 0 1 ADC1
0 0 1 0 ADC2
0 0 1 1 ADC3
0 1 0 0 ADC4
0 1 0 1 ADC5
0 1 1 0 ADC6
0 1 1 1 ADC7
1 0 0 0 ADC8
Em seguida, usamos o bit 5 (ADLAR) para definir como os 10 bits do valor digital
da conversão vai ser armazenado nos registradores ADCH e ADCL.
ADLAR = 0
Registrador 7 6 5 4 3 2 1 0
ADCH - - - - - - Bit9 Bit8
ADCL Bit7 Bit6 Bit5 Bit4 Bit3 Bit2 Bit1 Bit0
ADLAR = 1
Registrador 7 6 5 4 3 2 1 0
ADCH Bit9 Bit8 Bit7 Bit6 Bit5 Bit4 Bit3 Bit2
ADCL Bit1 Bit0 - - - - - -
Por fim, os bits nos bits 6 (REFS0) e 7 (REFS1) definimos a tensão de referência
que será adotada pelo circuito comparador.
Bit Função
ADIE Gerar uma interrupção ao final da conversão.
ADIF Indicar o final da conversão.
ADATE Habilitar o Auto Triggering do ADC.
ADSC Iniciar a conversão.
ADEN Ativar o ADC.
Material necessário:
• 1 Arduino.
• 1 Potenciômetro de 10k Ohms.
• 1 Resistor de 220 Ohms (vermelho, vermelho, marrom) ou 330 Ohms (laranja,
laranja, marrom).
• 1 LED de qualquer cor.
• 1 Protoboard.
• Cabos de ligação.
int main(void) {
DDRB |= (1 << DDB3);
ADMUX = 0b01000000;
ADCSRA = 0b10000111;
int valor_adc;
while (1) {
PORTB ^= (1 << PORTB3);
valor_adc = ADCL;
valor_adc |= (ADCH << 8);
esperar_ms(valor_adc);
}
}
Note que nos bits 0, 1, 2 e 3 (MUX0 até MUX3) selecionamos o canal ADC0. O
bit 5 (ADLAR) definido como zero indica que os 10 bits contendo o resultado da
conversão estarão organizados da seguinte maneira:
ADCH ADCL
Bit9 Bit8 Bit7 Bit6 Bit5 Bit4 Bit3 Bit2 Bit1 Bit0
Os bits 6 (REFS0) e 7 (REFS1) mostram que será usada como referência a tensão
de AVCC. Em seguida, passamos para a configuração do registrador ADCSRA que
recebeu o valor binário 10000111, ou seja:
valor_adc = ADCL;
valor_adc |= (ADCH << 8);
Bits
15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0
valor_adc = 0 0 0 0 0 0 0 0 0 0 0 1 0 0 1 1
Bits
15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0
ADCH<<8 = 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0
int main(void) {
DDRB |= (1 << DDB3);
ADMUX |= (1 << REFS0);
ADCSRA |= (1 << ADPS2) | (1 << ADPS1) | (1 << ADPS0);
ADCSRA |= (1 << ADEN);
int valor_adc;
while (1) {
PORTB ^= (1 << PORTB3);
valor_adc = ADCL;
valor_adc |= (ADCH << 8);
esperar_ms(valor_adc);
}
}
void USART_Init() {
UBRR0L = BAUD_PRESCALE;
UBRR0H = (BAUD_PRESCALE >> 8);
UCSR0B = (1 << TXEN0) | (1 << RXEN0);
UCSR0C = (1 << UCSZ01) | (1 << UCSZ00);
}
char USART_Rx() {
while (!(UCSR0A & (1<<RXC0)));
return (UDR0);
}
int main(void) {
USART_Init();
DDRB |= (1 << DDB3);
ADMUX |= (1 << REFS0);
ADCSRA |= (1 << ADPS2) | (1 << ADPS1) | (1 << ADPS0);
valor_adc = ADCL;
valor_adc |= (ADCH << 8);
#ifndef F_CPU
#define F_CPU 16000000UL
#endif
#ifndef BAUD
#define BAUD 9600
#endif
#define BAUD_PRESCALE ((F_CPU / (16UL * BAUD)) - 1)
#include <avr/io.h>
#include <stdio.h>
void USART_Init() {
UBRR0L = BAUD_PRESCALE;
UBRR0H = (BAUD_PRESCALE >> 8);
UCSR0B = (1 << TXEN0) | (1 << RXEN0);
UCSR0C = (1 << UCSZ01) | (1 << UCSZ00);
}
char USART_Rx() {
while (!(UCSR0A & (1<<RXC0)));
return (UDR0);
}
int temp;
int main(void) {
USART_Init();
ADMUX = (1 << REFS1) | (1 << REFS0) | (1 << MUX3);
ADCSRA = (1 << ADPS2) | (1 << ADPS1) | (1 << ADPS0)| (1 <<
ADEN);
_delay_ms(100);
while (1) {
ADCSRA |= (1 << ADSC);
while ((ADCSRA & (1 << ADSC)) != 0);
temp = (ADC - 324.31) / 1.22;
USART_Tx_String("Temperatura: ");
USART_Tx_Integer(temp);
USART_Tx_String("C\r\n");
_delay_ms(5000);
}
}
Após realizar a leitura, utilizamos o ponteiro ADC (16 bits) que contém a junção
dos registradores ADCH e ADCL para obter o valor da leitura. Na sequência aplicamos a
fórmula para conversão em graus Celsius.
int main(void) {
DDRB |= (1 << DDB5);
DDRB |= (1 << DDB4);
DDRB |= (1 << DDB3);
ADMUX |= (1 << REFS0);
ADCSRA |= (1 << ADPS2) | (1 << ADPS1) | (1 << ADPS0);
ADCSRA |= (1 << ADEN);
int valor_adc;
while (1) {
PORTB ^= (1 << PORTB5);
valor_adc = ADCL;
valor_adc |= (ADCH << 8);
• Leitura abaixo de 25% do valor máximo do ADC (1023), todos os LEDs ficam
apagados.
• Leitura entre 25% e menor que 50% do valor máximo do ADC (1023), apenas o
LED Verde conectado a PORTB5 acende.
• Leitura entre 50% e menor que 75% do valor máximo do ADC (1023), o LED
Verde conectado a PORTB5 e o LED Amarelo conectado a PORTB4 acendem.
• Leitura igual ou superior a 75% do valor máximo do ADC (1023), todos os LEDs
ficam acesos.
• Internas: São geradas por eventos internos, como exemplo podemos citar o
reset do microcontrolador, estouro (overflow) de um temporizador,
transferência completa de dados em uma das interfaces de comunicação, entre
outros.
• Externas: São interrupções geradas por sensores conectados aos pinos PD2
(INT0) e PD3 (INT1) e possibilitam diversas configurações, podendo ser ativadas
nas bordas de subida ou descida, em ambas ou com o nível lógico baixo. Além
disso, tem prioridade de execução. São configuradas através dos registradores
EICRA e EIMSK e monitoradas pelo registrador EIFR.
• Mudança de Nível no Pino (Pin Change Interrupts): Disponível em todos os
pinos digitais e podem ser ativadas quando ocorre mudança de nível em um
dos pinos. São configuradas nos registradores PCMSK0, PCMSK1, PCMSK2 e
PCICR e monitoradas pelo registrador PCIFR.
Material necessário:
• 1 Arduino.
• 1 Chave Táctil (push button).
• 1 Resistor de 220 Ohms (vermelho, vermelho, marrom) ou 330 Ohms (laranja,
laranja, marrom).
• 1 Resistor de 10k Ohms (marrom, preto laranja).
• 1 LED (qualquer cor).
• 1 Protoboard.
• Cabos de ligação.
Neste programa vamos usar uma interrupção externa que será gerada pelo
pressionamento da chave táctil. Observe que na função main configuramos os pinos e
a interrupção que, neste exemplo, será gerada na borda de descida (bit ISC01 = 1). Em
seguida o programa ficará executando a instrução while indefinidamente. A ideia
básica consiste na execução da função ISR quando ocorre uma interrupção. Neste
momento, a execução do while e, por consequência, a função main é suspensa até o
término da função ISR.
int main(void) {
DDRB |= (1 << DDB5);
DDRD &= ~(1 << DDD2);
while(1);
}
ISR(INT0_vect) {
PORTB ^= (1 << PORTB5);
}
Sendo que:
De maneira análoga:
Onde:
• Bit 1 - INT1 (External Interrupt Request 1 Enable)
• Bit 0 - INT0 (External Interrupt Request 0 Enable)
Não foi usado neste programa, mas temos também o registrador EIFR –
External Interrupt Flag Register.
Note neste próximo exemplo que as demais variáveis e instruções são usadas
para implementar a rotina de debounce na chave táctil, rotina esta já abordada no
assunto alusivo às entradas digitais.
char valor;
char estado = 0;
char anterior = 0;
int main(void) {
DDRB |= (1 << DDB5);
DDRD &= ~(1 << DDD2);
while(1);
}
ISR(INT0_vect) {
valor = (PIND & (1 << PIND2)) == (1 << PIND2);
if (valor == 1 && anterior == 0) {
if (estado == 1)
estado = 0;
else
estado = 1;
}
if (estado == 1)
PORTB |= (1 << PORTB5);
else
PORTB &= ~(1 << PORTB5);
anterior = valor;
_delay_ms(50);
}
char valor;
char estado = 0;
char anterior = 0;
int main(void) {
DDRB |= (1 << DDB5);
DDRD &= ~(1 << DDD2);
while(1);
}
ISR(PCINT2_vect) {
valor = (PIND & (1 << PIND2)) == (1 << PIND2);
if (valor == 1 && anterior == 0) {
if (estado == 1)
estado = 0;
else
estado = 1;
}
Onde:
• Bit 2 - PCIE2 (Pin Change Interrupt Enable 2): Habilita das interrupções de
PCINT23 até PCINT16.
• Bit 1 - PCIE1 (Pin Change Interrupt Enable 1): Habilita das interrupções de
PCINT14 até PCINT8.
• Bit 0 - PCIE0 (Pin Change Interrupt Enable 0): Habilita das interrupções de
PCINT7 até PCINT0.
Neste projeto precisamos habilitar PCINT18, desta maneira, o bit PCIE2 deverá
ser ligado através da instrução PCICR |= (1 << PCIE2). Em seguida, temos 3
registradores que são usados para identificar a interrupção de forma específica.
Apesar de não ser usado neste programa o registrador PCIFR pode ser usado
para identificar a ocorrência uma interrupção.
Onde:
• Bit 2: PCIF2 (Pin Change Interrupt Flag 2) – Quando ligado, indica que ocorreu
uma interrupção de PCINT23 até PCINT16.
• Bit 1: PCIF1 (Pin Change Interrupt Flag 1) – Indica que ocorreu uma interrupção
de PCINT14 até PCINT8.
• Bit 0: PCIF0 (Pin Change Interrupt Flag 0) – Indica uma interrupção de PCINT7
até PCINT0.
Valor
Função Modo WGM02 WGM01 WGM00
Máximo
Normal 0 0xFF 0 0 0
PWM com Fase Corrigida 1 0xFF 0 0 1
Limpar temporizador na
2 OCRA 0 1 0
comparação (CTC)
PWM Rápido 3 0xFF 0 1 1
Reservado 4 - 1 0 0
PWM com Fase Corrigida 5 OCRA 1 0 1
Reservado 6 - 1 1 0
PWM Rápido 7 OCRA 1 1 1
Onde:
Sendo:
int main(void) {
DDRB |= (1 << DDB5);
TCCR0B |= (1 << CS02) | (1 << CS00);
TCNT0 = 0;
unsigned char tempoextra = 0;
while(1) {
if (TCNT0 >= 0xFF) {
tempoextra++;
TCNT0 = 0;
int main(void) {
TCCR0B |= (1 << CS02) | (1 << CS00);
TIMSK0 |= (1 << TOIE0);
SREG |= (1 << SREG_I);
DDRB |= (1 << DDB5);
while(1);
}
ISR(TIMER0_OVF_vect) {
tempoextra++;
if(tempoextra > 77){
PORTB ^= (1 << PORTB5);
tempoextra = 0;
}
}
O overflow do temporizador/contador em conjunto com a variável tempoextra
contendo o valor 77 irá realizar a mudança do estado do LED aproximadamente a cada
1 segundo.
COM1A1 COM1A0
Modo de Comparação
COM1B1 COM1B1
Modo normal de operação, OC1A/OC1B desconectados. 0 0
Modos 9, 11, 14 e 15* apenas: Habilita OC1A e OC1B permanece 0 1
desconectado.
Modo Não Invertido (Nível HIGH na borda inferior, LOW na 1 0
igualdade)
Modo Invertido (LOW na borda inferior, HIGH na igualdade) 1 1
* Modos definidos pelos bits bits 1 (WGM11) e 0 (WGM10) do registrador TCCR1A e
bits 4 (WGM13) e 3 (WGM12) do registrador TCCR1B.
Valor
Função Modo WGM13 WGM12 WGM11 WGM10
Máximo
Normal 0 0xFFFF 0 0 0 0
PWM com Fase
1 0x00FF 0 0 0 1
Corrigida, 8 bits
PWM com Fase
2 0x01FF 0 0 1 0
Corrigida, 9 bits
PWM com Fase
3 0x03FF 0 0 1 1
Corrigida, 10 bits
Reservado 4 - 0 1 0 0
PWM Rápido, 8bits 5 0x00FF 0 1 0 1
PWM Rápido, 9 bits 6 0x01FF 0 1 1 0
PWM Rápido, 10 bits 7 0x03FF 0 1 1 1
PWM com Frequência
8 ICR1 1 0 0 0
e Fase Corrigida
PWM com Frequência
9 OCR1A 1 0 0 1
e Fase Corrigida
PWM com Fase
10 ICR1 1 0 1 0
Corrigida
PWM com Fase
11 OCR1A 1 0 1 1
Corrigida
Reservado 12 - 1 1 0 0
Reservado 13 - 1 1 0 1
PWM Rápido 14 ICR1 1 1 1 0
PWM Rápido 15 OCR1A 1 1 1 1
Onde:
Sendo:
Timer/Counter Registers
TCNT1H TCNT1L
7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0
D D D D D D D D D D D D D D D D
D – Bits de Dados
Valor
Função Modo WGM22 WGM21 WGM20
Máximo
Normal 0 0xFF 0 0 0
PWM com Fase Corrigida 1 0xFF 0 0 1
Limpar temporizador na
2 OCRA 0 1 0
comparação (CTC)
PWM Rápido 3 0xFF 0 1 1
Reservado 4 - 1 0 0
PWM com Fase Corrigida 5 OCRA 1 0 1
Reservado 6 - 1 1 0
PWM Rápido 7 OCRA 1 1 1
Onde:
Sendo:
Por outro lado, se durante todo o período o sinal permanecer apenas um nível
1 (HIGH), teremos 100% da razão de ciclo (Figura 27), resultando no valor 255 se
considerarmos, nesse exemplo, uma saída digital do Arduino.
Desta forma, podemos calcular a razão de ciclo em função do tempo que o sinal
permanece um nível 1 (HIGH) e em nível 1 (LOW):
O ATmega328P possui 6 saídas PWM que são PD3, PD5, PD6, PB1, PB2 e PB3,
correspondendo respectivamente aos Pinos 3, 5, 6, 9, 10 e 11 do Arduino. Cada
temporizador (TIMER0, TIMER1 e TIMER2) possui duas saídas PWM conectadas. Desta
forma, no TIMER0 temos conectados PD5 e PD6, enquanto no TIMER1 temos
conectados PB1 e PB2 e no TIMER2 temos PD3 e PB3.
Com base nos conceitos apresentados, vamos desenvolver um projeto que irá
mostrar a variação de intensidade de luz emitida por um LED conectado à uma porta
digital, configurada no modo PWM.
Material necessário:
• 1 Arduino.
• 1 Resistor de 220 Ohms (vermelho, vermelho, marrom) ou 330 Ohms (laranja,
laranja, marrom).
• 1 LED de qualquer cor.
• 1 Protoboard.
• Cabos de ligação.
while(1) {
for (unsigned int pwm = 0; pwm < 256; pwm++) {
OCR1A = pwm;
_delay_ms(10);
}
}
}
Material necessário:
• 1 Arduino.
• 1 Potenciômetro de 10k Ohms.
• 1 Resistor de 220 Ohms (vermelho, vermelho, marrom) ou 330 Ohms (laranja,
laranja, marrom).
• 1 LED de qualquer cor.
• 1 Protoboard.
• Cabos de ligação.
while(1) {
ADCSRA = ADCSRA | (1 << ADSC);
while(ADCSRA & (1 << ADSC));
valor_adc = ADCL;
valor_adc |= (ADCH << 8);
OCR2A = valor_adc / 4;
_delay_ms(10);
}
}
Material necessário:
• 1 Arduino.
• 1 Display LCD1602.
• 1 Potenciômetro de 10k Ohms.
• 1 Resistor de 220 Ohms (vermelho, vermelho, marrom) ou 330 Ohms (laranja,
laranja, marrom).
• 1 Protoboard.
• Cabos de ligação.
#ifndef F_CPU
#define F_CPU 16000000UL
#endif
#include <avr/io.h>
#include <util/delay.h>
#ifndef LCD_Port
#define LCD_Port PORTD
#endif
#ifndef LCD_DPin
#define LCD_DPin DDRD
#endif
#ifndef RSPIN
#define RSPIN PD2
#endif
#ifndef ENPIN
#define ENPIN PD3
#endif
void LCD_StartRW() {
LCD_Port |= (1 << ENPIN);
_delay_us(1);
LCD_Port &= ~ (1 << ENPIN);
}
void LCD_Init() {
LCD_DPin |= (1 << PD7) | (1 << PD6) | (1 << PD5) | (1 << PD4)
| (1 << PD3) | (1 << PD2);
_delay_ms(15);
LCD_Cmd(0x03);
_delay_ms(4.1);
LCD_Cmd(0x03);
_delay_us(100);
LCD_Cmd(0x03);
LCD_Cmd(0x02);
LCD_Cmd(0x0C);
LCD_Cmd(0x06);
LCD_Cmd(0x01);
_delay_ms(2);
}
void LCD_Clear() {
LCD_Cmd (0x01);
_delay_ms(2);
LCD_Cmd (0x80);
}
void LCD_StartRW() {
LCD_Port |= (1 << ENPIN);
_delay_us(1);
LCD_Port &= ~ (1 << ENPIN);
}
Código Instrução
0x03 Preparar para receber os comandos
0x02 Modo de 4 bits
0x0C Display ligado e cursor desligado
0x06 Mover o cursor à direita, desligar o deslocamento do display
0x01 Apagar o conteúdo do display
void LCD_Clear() {
LCD_Cmd (0x01);
_delay_ms(2);
LCD_Cmd (0x80);
}
Conforme pode ser observado na Figura 34, a segunda linha do display inicia no
endereço 0x40.
LCD_Cmd(0xC5);
int main() {
LCD_Init();
LCD_PrintXY(6, 0, "Ola!");
Material necessário:
• 1 Arduino.
• 1 Display LCD1602.
• 1 Sensor de Temperatura e Umidade dht11.
• 1 Potenciômetro de 10k Ohms.
• 1 Resistor de 220 Ohms (vermelho, vermelho, marrom) ou 330 Ohms (laranja,
laranja, marrom).
• 1 Protoboard.
• Cabos de ligação.
Desta maneira, de acordo com a Figura 35, notamos que o pino de dados do
DHT11 deverá ser conectado ao pino PB0 (Pino 8 do Arduino), enquanto o display LCD
será ligado do seguinte modo:
Com base nas informações apresentadas podemos elaborar uma rotina para
realizar essa tarefa. Observe, nos comentários ao longo do trecho de programa
apresentado na sequência, a explicação de cada uma das etapas.
// Resetar a porta
DHT_DDR |= (1 << DHT_DATAPIN); // Definir como Saída
DHT_PORT |= (1 << DHT_DATAPIN); // Colocar Nível 1
_delay_ms(100);
Após isso o DHT está pronto para realizar a transmissão de dados. Sendo que
devemos interpretar como um “bit 0” o pino de dados recebendo nível 1 por um
período entre 26us e 28us (Figura 37).
Após estes conceitos, podemos apresentar a rotina completa, que poderá ser
colocada no arquivo lib-dht11.h, permitindo sua reutilização.
// Ler os dados
unsigned char dados[] = {0x00, 0x00, 0x00, 0x00, 0x00};
unsigned char i, j = 0;
int contador = 0;
for (j = 0; j < 5; j++) {
unsigned char result = 0;
for(i = 0; i < 8; i++) {
contador = 0;
while(!(DHT_PIN & (1 << DHT_DATAPIN))) {
contador++;
if(contador > DHT_TIMEOUT) {
return -1;
}
}
_delay_us(30);
if(DHT_PIN & (1 << DHT_DATAPIN))
result |= (1 << (7 - i));
contador = 0;
while(DHT_PIN & (1<<DHT_DATAPIN)) {
contador++;
if(contador > DHT_TIMEOUT) {
return -1;
}
}
}
dados[j] = result;
}
// Realizar o checksum
if ((unsigned char)(dados[0] + dados[1] + dados[2] + dados[3])
== dados[4]) {
*t = dados[2];
*u = dados[0];
return 0;
}
int main() {
LCD_Init();
LCD_PrintXY(0, 0, "DHT11");
_delay_ms(500);
unsigned char temp = 0;
unsigned char umid = 0;
while(1) {
if(DHT_Ler(&temp, &umid) != -1) {
LCD_Clear();
LCD_PrintXY(0, 0, "Temp: ");
char str_valor[10];
sprintf(str_valor, "%d", temp);
LCD_Print(str_valor);
sprintf(str_valor, "%c", 0b11011111);
LCD_Print(str_valor);
LCD_Print("C");
LCD_PrintXY(0, 1, "Umid: ");
sprintf(str_valor, "%d", umid);
LCD_Print(str_valor);
LCD_Print("%");
}
else {
LCD_Clear();
LCD_PrintXY(0, 0, "DHT11: Erro!");
}
_delay_ms(5000);
}
}