Você está na página 1de 52

SISTEMA DE MICROPROCESSADOS

ALCIDES MACHADO GUIMARÃES

ALGORÍTMOS

PIC18F4550 + KIT ACEPIC PRO V2.1


THIAGO BEZERRA TONI
WILLIAM BRUNO MIRANDA SIQUEIRA
BELÉM
2016
SUMÁRIO

Introdução ................................................................................................................................3
1. Diodo emissor de luz (LED) .........................................................................................4
1.1. Pisca-LED ............................................................................................................5
1.2. Contador Binário de 8 bits.....................................................................................5
2. Display de 7 Segmentos................................................................................................5
2.1. Contador Simples .................................................................................................6
2.2. Caractere Personalizada.......................................................................................7
2.3. Contador de 0 a 9999............................................................................................9
3. LCD Alfanumérico........................................................................................................10
3.1. “Hello World”........................................................................................................12
3.2. Contador .............................................................................................................13
3.3. Relógio ................................................................................................................14
4. Conversor A/D...............................................................................................................15
4.1. Conversor A/D + LED’s........................................................................................16
4.2. Conversor A/D + Display de 7 segmentos...........................................................18
4.3. Conversor A/D + LCD..........................................................................................19
4.4. Termômetro..........................................................................................................20
5. EUSART........................................................................................................................22
5.1. EUSART + LED’s.................................................................................................22
5.2. EUSART + LCD....................................................................................................23
5.3. Controle de temperatura via EUSART.................................................................24
6. PWM (Modulação por Largura de Pulso) ...................................................................27
6.1. PWM + LED, Ventoinha e LCD............................................................................28
6.2. PWM + LCD.........................................................................................................29
6.3. PWM + Buzzer.....................................................................................................30
7. Teclado Matricial...........................................................................................................32
7.1. Teclado + Display 7 segmentos...........................................................................33
7.2. Teclado + LCD.....................................................................................................34
8. Comunicação I²C...........................................................................................................37
8.1. Botão + Display 7 segmentos...............................................................................37
8.2. Botão + LCD.........................................................................................................40
8.3. PIC + RTC............................................................................................................43
8.4. Relógio Ajustável..................................................................................................46
Considerações Finais...............................................................................................................50

2
INTRODUÇÃO
Os projetos a seguir foram desenvolvidos no laboratório de Sistemas Micro Processados
alocado nas dependências do Instituto Federal do Pará, IFPA, com fins exclusivamente didáticos e
com intuito de estudar os módulos presentes no Kit de desenvolvimento da Acepic PRO V2.1 e
relacioná-los com o Microcontrolador PIC18F4550.
Todos os programas foram escritos e compilados utilizando o compilador MikroC, que se
trata de um software criado para quem almeja programar, em C, Microcontroladores em geral.
Depois de compilados, os programas foram descarregados no PIC pelo Bootloader, que tem a
função de gravar os algoritmos na memória do Microcontrolador.

1. Diodo Emissor de Luz (LED):


O primeiro módulo a ser estudado é também o mais simples, notando que, o LED é um componente
básico do mundo da Eletrônica e também de grande importância, pois ele tem a finalidade de
“transformar” eletricidade em luz, sendo muito utilizado para sinalizar que uma ação programada
está sendo executada.
Antes de iniciar o trabalho com esse módulo,vale lembrar que, no Kit Acepic:

• Os LED’s são nomeados de L1 à L8.


• Eles estão conectados ao‘PORT D’ do Micro Controlador.
• O registrador ‘PORTD’ tem 8 pinos que podem ser configurados como Inputs ou Outputs.
• Pode-se habilitar ou desabilitar os LED’s através da chave 2 do DIP DP2 na posição LED, conforme
mostra a figura abaixo:

3
1.1. Pisca-LED:
Um programa básico que consiste em testar a comunicação tanto entre o PC e o PIC (no ato
de upload do código do PC para a memória do Micro Controlador) quanto entre o PIC com os
periféricos do Kit, nesse caso, os LED’s.
Para isso, na função principal, o registrador onde se encontram os Leds é configurado como
saída e é iniciado com todos os seus bits em low. No Loop Infinito um bit do PORTD é colocado em
HIGH, e após 500 milissegundos ele passa para LOW.
////////////////////////////////////////////////////////////////////////////////
void main() { // Função Principal
TRISD = 0x00; // Declara todas as portas do Registrador D como SAÍDAS
PORTD = 0x00; // Passa todas os pinos de PORTD para low
while(1) { // Loop Infinito
PORTD = 0x01; // Coloca o pino RD0 em high
delay_ms(500); // Delay de 0,5s
PORTD = 0x00; // Coloca o pino RD0 em low
delay_ms(500); // Delay de 0,5s
} // end while
} // end void main
////////////////////////////////////////////////////////////////////////////////

1.2. Contador de 8 bits +LED’s:


Sabe-se que o PORTD tem 8 pinos (RD0 à RD7) e que se tem um LED associado a cada pino (L1
à L8) logo é possível fazer uma contagem binária de 8 bits (0 a 255), apagando e acendendo
determinados LED’s. Para isso utiliza-se o comando “Incremento” (++), que soma 1 ao estado
anterior da variável, ou seja:
Se X = 1,
X++ será (x + 1), logo X = 2.
E assim sucessivamente até 255, onde automaticamente o PIC reinicia a contagem, pois
atingiu o limite do contador de 8 bits, considerando isso, na Função Principal do programa é
configurado o registrador envolvido no processo, e no Loop Infinito o ele incrementado.

////////////////////////////////////////////////////////////////////////////////
void main(){ // Função principal
TRISD = 0x00; //Declara todos pinos do registrador PORTD como saídas.
PORTD = 0x00; //Inicia todos os pinos do PORTD em low.

while(1){ //Loop infinito


PORTD = PORTD++; //Incrementa em 1 o PORTD
delay_ms(500)//delay de 0,5s
} // end while
} // end void main
Tarefa: gerar efeitos iluminação nos led’s, como “colisão” de um led , “vai e volta”, deslocamento de dois leds etc...

////////////////////////////////////////////////////////////////////////////////

2. Display de 7 Segmentos:
Além dos LED’s outros itens também estão associados ao PORTD, como os 4 displays de 7
segmentos, que serão alvos de nosso estudo a partir de agora.Os componentes em questão são de
catodo comum, considerando o fato de que um display basicamente trata-se de um conjunto de
LED’s, logo,é necessário citar o procedimento para se formar um caractere no mesmo:
O “Seven Segment Editor” do programa MikroC, pode ser usado para gerar um Hexa que será lido
pelo módulo do display, mostrado no display, como na figura abaixo:

4
Serão gerados dois números hexadecimais
um para display do tipo Catodo Comum e outro para
o tipo Anodo Comum, já que o tipo de display
presente utilizado no Kit da Acepic V2.1 é Catodo
Comum, logo, o hexadecimal equivalente ao digito
“Zero” será 0x3F.

Por fim,para a utilização dos displays, é importante saber que:


• Os displays são multiplexados, logo, as linhas de dados dos 4 estão nos mesmos pontos,
nesse caso, o PORTD.
• O acionamento de cada display é dado pelos pinos RE0, RE1, RE2 e RA5, então os
registradores referentes a esses pinos devem ser configurados como saídas.
• Para ativar os displays, as chaves 5, 6, 7 e 8 do DIP DP2 devem ser ligadas, conforme
oesquema elétrico abaixo.
Tabela: Códigos para exibição de dígitos em displays de 7 segmentos

OBS.: -Nosso display é de Katodo Comum com MSb=h(dp)


- Portas envolvidas: A e E para habilitação dos displays e D para segmentos.

5
RD7 RD6 RD5 RD4 RD3 RD2 RD1 RD0
h g f e d c b a

2.1. Contador Simples:


O programa tem como finalidade utilizar um display para realizar a contagem de 0 a 9, sendo
assim, somente o display da contagem será usado e os outros permanecerãodesabilitados, para
isso, apenas uma das chaves 5, 6, 7 e 8 do DIP DP2 deve ser ligada ou apenas um dos pinos RE0,
RE1, RE2 e RA5 deve ser habilitado.
No início do programa são determinados os valores em hexa que serão mostrados no display,
após isso,na função principal são declaradas as funções dos registradores e no loop infinito é
realizada a contagem limitada até 9 pelo comando “if”.

///////////////////////////////////////////////////////////////////////////////////////////////
Unsigned char cont; // variável de contagem

Unsigned char digito[]= { 0x3F, // 0 Define cada segmento


0x06, // 1 dos valores mostrados
0x5B, // 2 no display de LEDs
0x4F, // 3 em Hexadecimal para ficar
0x66, // 4 mais fácil de codificar
0x6D, // 5 cada dígito dos valores
0x7D, // 6 mostrados no display.
0x07, // 7
0x7F, // 8
0x67}; // 9
void main(){ // Função Principal
cont=0;

6
TRISD = 0x00; // Declara PORT’D’ como saída
PORTD = 0x00; // Inicia os bits do PORT’D’ em low
TRISA.RA5 = 0x00; // Declara o pino RA5 do PORT’A’ como saída
PORTA.RA5 = 0x01; // Inicia RA5 em high, ativando o display
while(1){ // Loop Infinito
PORTD = (digito[cont]); // Chama o digito correspondente a [cont]
cont = (cont + 1); // Incrementa em 1 a variável cont
delay_ms(500); // Delay de 0,5 segundo
if(cont==10){ // Se cont for igual a 10...
cont = 0; // Zera cont, e recomeça a contagem
} // end if
} //end while
} //end void main
Tarefa: gerar efeitos de iluminação nos segmentos de um display.
///////////////////////////////////////////////////////////////////////////////////////////////

2.2. Caractere Personalizado:


É possível determinar dígitos personalizados através do utensílio “Seven Segment Editor” do
MikroC, para isso abre-se a ferramenta em questão e após a formação do caractere é gerado um
número hexadecimal que será utilizado no programa, como mostrado abaixo:

O programa consiste em mostrar “IFPA” nos displays durante 4 segundos e depois exibe
letra por letra por 8 segundos e assim sucessivamente, para isso são atribuídas ás variáveis
osvalores dos dígitos em hexa, na função principal são declarados os registradores que serão
utilizados no processo, feito isso, no loop infinito habitam os dois laços “for”. O primeiro laço é
responsável por mostrar as letras nos 4 displays ao mesmo tempo, o segundo laço mostra de forma
sequencial letra por letra em um display por vez e assim ininterruptamente.

///////////////////////////////////////////////////////////////////////////////////////////////
// valor do display de 7 segmentos correspondente às letras:
unsignedchar letra_I = 0x06;
unsignedchar letra_F = 0x71;
unsignedchar letra_P = 0x73;
unsignedchar letra_A = 0x77;

char i; // variável auxiliar de tempo

void main(){ //Função Principal


TRISD = 0x00; //Declara RD7 como entrada e os demais pinos como saídas
PORTD = 0x00; //Coloca todos os pinos de PORTD em low
TRISE = 0x00; //Declara todos os pinos do registrador PORT'E como saídas
PORTE = 0x00; //Coloca todos os pinos de PORT'E em low
TRISA.RA5 = 0x00; //Declara o pino RA5 como saída
PORTA.RA5 = 0x00; //Coloca o pino RA5 em low

7
while (1){ //Loop infinito
for(i=0;i<=199;i++){ //Executa ou mostra “IFPA” através de 200 laços por aprox. 4s
//Veja que a qualquer letra é mostrada de 20ms em 20ms no período de 4s, eliminando o efeito da cintilação.
// Apresenta a IFPA nos displays
PORTD = letra_A; // Apresenta a LETRA A
RA5_bit = 1; // Habilita RA5
delay_ms(5); // Aguarda 5ms
RA5_bit = 0; // Desabilita

PORTD = letra_P; // Apresenta a LETRA P


RE2_bit = 1; // Habilita RE2
delay_ms (5); // Aguarda 5ms
RE2_bit = 0; // Desabilita

PORTD = letra_F; // Apresenta a LETRA F


RE1_bit = 1; // Habilita RE1
delay_ms (5); // Aguarda 5ms
RE1_bit = 0; // Desabilita
PORTD = letra_I; // Apresenta a LETRA I
RE0_bit = 1; // Habilita RE0
delay_ms (5); // Aguarda 5ms
RE0_bit = 0; // Desabilita
} //end for1

for(i=0;i<=3;i++){ //Executa esse laço por aprox. 8s


// Mostra IFPA no display letra por letra de for sequencial
PORTD = letra_I; // Apresenta a LETRA I
RE0_bit = 1; // Habilita RE0
delay_ms (500); // Aguarda 500ms
RE0_bit = 0; // Desabilita

PORTD = letra_F; // Apresenta a LETRA F


RE1_bit = 1; // Habilita RE1
delay_ms (500); // Aguarda 5000ms
RE1_bit = 0; // Desabilita

PORTD = letra_P; // Apresenta a LETRA P


RE2_bit = 1; // Habilita RE2
delay_ms (500); // Aguarda 500ms
RE2_bit = 0; // Desabilita

PORTD = letra_A; // Apresenta a LETRA A


RA5_bit = 1; // Habilita RA5
delay_ms(500); // Aguarda 500ms
RA5_bit = 0; // Desabilita
} //end for2
} //end while
} //end void main
Tarefa: Insira um texto nos display’s e faça-o girar para esquerda ou direita.
///////////////////////////////////////////////////////////////////////////////////////////////

2.3. Contador de 0 a 9999:


O programa utiliza os quatro displays multiplexados disponíveis para realizar uma contagem
de 0 a 9999, um princípio muito útil em diversas situações. No início do programa são declarados,
em hexadecimal, os dígitos que serão mostrados nos displays, logo após, são definidas as funções
dos registradores que coordenarão o procedimento.
No Loop Infinito há uma pequena álgebra com o papel de atribuir cada algarismo da
“variável de contagem” a cada dígito que será mostrado nos displays (Unidade, Dezena, Centena e
Milenar). A seguir, atentando ao fato de que os displays são multiplexados, tem-se um mecanismo

8
com o intuito de habilitar e desabilitar os mesmos (através dos registradores) de uma forma
extremamente rápida, para vencer a perceptibilidade do olho humano, então assim cria-se a
ilusão de que todos os displays estão ligados de forma simultânea.

///////////////////////////////////////////////////////////////////////////////////////////////
unsignedchar digito[]= { 0x3F, // 0 Define cada segmento
0x06, // 1 dos valores mostrados
0x5B, // 2 no display de LEDs
0x4F, // 3 em Hexadecimal para ficar
0x66, // 4 mais fácil de codificar
0x6D, // 5 cada dígito dos valores
0x7D, // 6 mostrados no display.
0x07, // 7
0x7F, // 8
0x67}; // 9
char i; // variável auxiliar
int Cnt = 0; // variável de contagem
int Mil, Cen, Dez, Uni = 0; //variáveis do display
void main(){
TRISD = 0x80; //Declara RD7 como entrada e os demais pinos como saídas
PORTD = 0x00; //Inicia todos os pinos de PORTD em low
TRISE = 0x00; //Declara todos os pinos do registrador PORT'E como saídas
PORTE = 0x00; //Inicia todos os pinos de PORT'E em low
TRISA.RA5 = 0x00; //Declara o pino RA5 como saída
PORTA.RA5 = 0x00; //Inicia o pino RA5 em low
while (1){ // Loop infinito
for (i = 0; i<=1; i++){ /* controla o tempo que cada numero
é mostrado no display (aprox= 40ms) */

Uni = (Cnt%10); // Calcula a unidade


// 1234/10 = 123,4 | Resto = 4. | Uni = 4 |

Dez = (Cnt%100); // Calcula a dezena


// 1234/100 = 12,34 | Resto 34 -> Dez=34
Dez = (Dez/10) - ((Dez%10)/10); // Calcula a dezena
// 34/10 = 3,4 - (0,4) | Dez = 3 |

Cen = (Cnt%1000); // Calcula a centena


// 1234/1000 = 1,234 | Resto 234 -> Cen=234
Cen = (Cen/100)-((Cen%100)/100); // Calcula a centena
// 234/100 = 2,34 - (0,34) | Cen = 2 |

Mil = (Cnt/1000) - ((Mil%1000)/1000); // calcula o milênio


// 1234/1000 = 1,234 - (0,234) | Mil = 1 |

PORTD = (digito[Uni]); // Apresenta Digito[Unidade_do_contador]


RA5_bit = 1; // Habilita RA5
delay_ms(5); // Aguarda 5ms
RA5_bit = 0; // Desabilita

PORTD = (digito[Dez]); // Apresenta Digito[Dezena_do_contador]


RE2_bit = 1; // Habilita RE2
delay_ms(5); // Aguarda 5ms
RE2_bit = 0; // Desabilita

PORTD = (digito[Cen]); // Apresenta Digito[Centena_do_contador]


RE1_bit = 1; // Habilita RE1
delay_ms(5); // Aguarda 5ms
RE1_bit = 0; // Desabilita

PORTD = (digito[Mil]); // Apresenta Digito[Milenar_do_contador]

9
RE0_bit = 1; // Habilita RE0
delay_ms(5); // Aguarda 5ms
RE0_bit = 0; // Desabilita
}// end for
if(Cnt == 10000){ // Se Cnt for igual a 10000...
Cnt = 0; // Zera a variável de contagem, recomeça
delay_ms(1000); // Delay de 1 segundo
PORTD = 0x00; // Zera PORTD
}//end if
Cnt++; //Incrementa a variável de contagem em 1
} // end while
}// end void main
Tarefa: Temporizar em 1min, acionando buzer ou ventoinha, na contagem de 2500.
///////////////////////////////////////////////////////////////////////////////////////////////

3. LCD Alfanumérico:
Os módulos LCD são interfaces de saída muito úteis em sistemas Microprocessados, estes
módulos podem ser Gráficos ou Caractere. Os LCD comuns (tipo Caractere) são especificados em
número de linhas por colunas e no Kit Acepic em questão o LCD é 16x2, logo, tem 2 linhas e 16
colunas.
O LCD utiliza um controlador próprio, permitindo sua interligação com outras placas através de
seus pinos, onde devem ser alimentados e interligados com o barramento de dados e controle do
módulo da placa do usuário. Antes de iniciar o uso do LCD é importante notar que:
• O LCD tem conectado seus pinos de dados diretamente no “PORT D”.
• Os pinos de controle, RS(entrada de dados ou instrução) e Enable nos bits 1 e 0,
respectivamente, do “PORT E”.
• O backlight(gerado pelo conjunto de LEDs posicionados na parte de trás do display) pode ser
acionado ligando-se a chave 1 do DIP DP2, conforme esquema abaixo:

10
Antes de escrever algum programa para o uso do LCD é de suma importância ter conhecimento das
funções e comandos que o MikroC disponibiliza para facilitar a manipulação do Display de Cristal Líquido
(LCD).

Na página anterior foram declaradas as conexões e as direções dos pinos do LCD em relação ao
Micro controlador, após isso deve-se atentar para as seguintes funções e comandos que são cruciais para o
uso do LCD:
Lcd_Init(); // Inicializa o LCD
Lcd_Cmd(_LCD_CLEAR); // Limpa o conteúdo da tela
Lcd_Cmd(_LCD_CURSOR_OFF); // Desliga o cursor
Lcd_Out(2,6,texto); // Escreve o texto na linha 2, a partir da coluna 6

3.1. “Hello World” + LCD.


O programa tem como intuito testar os comandos básicos do LCD e mostrar textos
personalizados na tela.
No início do procedimento são feitas as declarações das conexões dos pinos do PIC com o LCD,
e a seguir tem-se as variáveis de textos que serão mostradas no display, logo após, a função em
que se encontram os comandos responsáveis pelos textos que aparecerão na tela. Em seguida há
um laço for cujo tem como papel mover o texto para a extremidade esquerda da tela.
Posteriormente encontra-se a função principal onde é configurado o registrador ADCON1, e
também são chamadas as funções que faram parte do processo.
E finalmente o Loop Infinito, no qual, há dois Laços “for” responsáveis por mover o conteúdo
presente no LCD para o lado direito e depois para o lado esquerdo, de forma infinita.

//////////////////////////////////////////////////////////////////////////////////////////////
// Módulo de conexões dos pinos do LCD com os do PIC
sbit LCD_RS at RE0_bit;
sbit LCD_EN at RE1_bit;
sbit LCD_D4 at RD4_bit;
sbit LCD_D5 at RD5_bit;
sbit LCD_D6 at RD6_bit;
sbit LCD_D7 at RD7_bit;
sbit LCD_RS_Direction at TRISE0_bit;
sbit LCD_EN_Direction at TRISE1_bit;
sbit LCD_D4_Direction at TRISD4_bit;
sbit LCD_D5_Direction at TRISD5_bit;
sbit LCD_D6_Direction at TRISD6_bit;
sbit LCD_D7_Direction at TRISD7_bit;
// Variáveis de texto
char txt1[] = "Hello World!";
char txt2[] = "^__^";
char txt3[] = "Eletronica";
char txt4[] = "IFPA";
char i; // Variável auxiliar de 'contagem'

void write_LCD() { // Função que escreve no LCD


Lcd_Out(1,3,txt1); //Escreve na linha 1, coluna 3 o conteúdo de txt1
Lcd_Out(2,7,txt2); //Escreve na linha 2, coluna 7: txt2

11
delay_ms(5000); //Aguarda 5s
Lcd_Cmd(_LCD_CLEAR); //Limpa a tela do LCD
delay_ms(250); //delay de 250ms
Lcd_Out(1,4,txt3); //Escreve na linha 1, coluna 4: txt3
Lcd_Out(2,7,txt4); //Escreve na linha 2, coluna 7: txt4
delay_ms(2000); //Aguarda 2s

for(i=0; i<3; i++) { // Repete o laço 3 vezes


Lcd_Cmd(_LCD_SHIFT_RIGHT); // Move o conteúdo da tela para a esquerda
delay_ms(500); // aguarda 0,5s
} // end for
} // end void write LCD

void main() { // Função principal


ADCON1 = 0x0F; //Configura todos as saídas analógicas como Digitais I/O's
Lcd_Init(); // Inicializa LCD
Lcd_Cmd(_LCD_CLEAR); // Limpa display
Lcd_Cmd(_LCD_CURSOR_OFF); // Desliga o Cursor
write_LCD(); // Chama a Função de escrita no LCD

while(1) { // Loop infinito


for(i=0; i<6; i++) { // Move o texto pra esquerda 6x
Lcd_Cmd(_LCD_SHIFT_LEFT); // comando para mover p/ esquerda
delay_ms(500); // aguarda 0,5s
} // end for

for(i=0; i<6; i++) { // Move o texto pra direita 6x


Lcd_Cmd(_LCD_SHIFT_RIGHT); // comando para mover p/ direita
delay_ms(500); // delay (500ms)
} // end for
} // end while
} // end void main
Tarefa: Insira um texto no LCD e faça-o girar para esquerda ou direita.

///////////////////////////////////////////////////////////////////////////////////////////////

3.2. Contador + LCD.


No início do programa são definidas as conexões do LCD com o PIC, após isso, têm-se as
variáveis que serão exibidas no Display, uma de texto, outra do tipo ponteiro (que armazenará os
dígitos), e uma variável responsável pela contagem.
Em seguida encontra-se a função que calcula os algarismos e mostra o valor da contagem no
LCD, abaixo, a função principal onde são configurados os módulos que farão parte do programa e
finalmente o Loop Infinito onde é chamada a função que calcula os valores que serão mostrados
no display, ainda no loop, é feito o incremento na variável de contagem e também o laço “if”
responsável por zerá-la.

///////////////////////////////////////////////////////////////////////////////////////////////
// Conexões do LCD com o PIC
sbit LCD_RS at RE0_bit;
sbit LCD_EN at RE1_bit;
sbit LCD_D4 at RD4_bit;
sbit LCD_D5 at RD5_bit;
sbit LCD_D6 at RD6_bit;
sbit LCD_D7 at RD7_bit;
sbit LCD_RS_Direction at TRISE0_bit;
sbit LCD_EN_Direction at TRISE1_bit;
sbit LCD_D4_Direction at TRISD4_bit;
sbit LCD_D5_Direction at TRISD5_bit;
sbit LCD_D6_Direction at TRISD6_bit;

12
sbit LCD_D7_Direction at TRISD7_bit;

char txt[]="CONTADOR 9999"; // variável de Texto


int Cnt = 0; // Variável de contagem
char *digit = "0000"; // Variável (ponteiro) dos dígitos

void Display_init(){ /* Função que calcula algarismos que serão


Armazenados na variável 'digit' */

digit[3] = (Cnt%10) +48; // Calcula o digito da UNIDADE


digit[2] = (Cnt/10)%10 + 48; // Calcula o digito da DEZENA
digit[1] = (Cnt/100)%10 +48; // Calcula o digito da CENTENA
digit[0] = (Cnt/1000) + 48; // Calcula o digito da MILENIO
Lcd_Out(2,7,digit); // Mostra a variável 'digit' no LCD
} // end void Display_init

void main(){ // Função Principal


ADCON1 = 0x0F; // Configura as portas como Digitais I/O's
Lcd_init(); // Inicializa o LCD
Lcd_cmd(_LCD_CLEAR); // Limpa a tela
Lcd_cmd(_LCD_CURSOR_OFF); // Desliga o cursor
Lcd_out(1,3,txt); // Mostra no LCD o conteúdo de 'txt'

while(1){ // Loop Infinito


Display_init(); // Chama função Display_init
delay_ms(100); // Aguarda 0,1s;
Cnt ++; // incrementa a variável de contagem
if (Cnt >9999){ // Quando Cnt for maior que 9999...
Cnt = 0;} // Zera a variável de contagem
} // end while
} // end void main
Tarefa: Temporizar em 1min, acionando buzer ou ventoinha, na contagem de 2500.
///////////////////////////////////////////////////////////////////////////////////////////////

3.3. Relógio + LCD.


Utilizando os conhecimentos adquiridos ao longo do curso, é possível projetar um relógio
aproveitando as propriedades contadoras do PIC.
Para isso são criadas Três variáveis responsáveis pela contagem dos segundos, minutos e das
horas, feito isso, são declaradas as variáveis que serão mostradas no display, uma de texto e a
outra do tipo ponteiro, que é responsável pelo armazenamento de cada dígito que será calculado.
Para obter os dígitos foi criada a função “digits” que através das variáveis de contagem,
calcula o valor de cada algarismo que será armazenado na variável “digit” que será mostrada no
LCD.
Na função principal são configurados os módulos e registradores que regerão o
procedimento. No Loop Infinito é chamada a função que calcula os dígitos,além disso é
incrementada a variável de contagem de segundos, os laços “if’s” servem para controlar o
incremento ou decremento das variáveis, que são essenciais para o programa trabalhar como um
relógio.

///////////////////////////////////////////////////////////////////////////////////////////////
// Conexões dos pinos do LCD com os pinos do PIC
sbit LCD_RS at RE0_bit;
sbit LCD_EN at RE1_bit;
sbit LCD_D4 at RD4_bit;
sbit LCD_D5 at RD5_bit;
sbit LCD_D6 at RD6_bit;
sbit LCD_D7 at RD7_bit;
sbit LCD_RS_Direction at TRISE0_bit;

13
sbit LCD_EN_Direction at TRISE1_bit;
sbit LCD_D4_Direction at TRISD4_bit;
sbit LCD_D5_Direction at TRISD5_bit;
sbit LCD_D6_Direction at TRISD6_bit;
sbit LCD_D7_Direction at TRISD7_bit;

unsignedshort Cnt_seg = 0; // Contagem de segundos


unsignedshort Cnt_min = 0; // Contagem de minutos
unsignedshort Cnt_hora =0; // Contagem de horas
char txt[] = "IFPA"; // Variável de texto
char *digit = "00:00:00"; // Variável dos dígitos

unsignedshort digits(){ // Função que calcula os dígitos dos:


// Segundos,
digit[7] = (Cnt_seg%10) +48; // Calcula a unidade
digit[6] = (Cnt_seg%100) ; // Calcula a Dezena
digit[6] = ((digit[6]/10) - ((digit[6]%10)/10)) +48; // +48 (TABELA ASCII)

// Minutos,
digit[4] = (Cnt_min%10) +48; // Calcula a unidade
digit[3] = (Cnt_min%100); // Calcula a Dezena
digit[3] = ((digit[3]/10) - ((digit[3]%10)/10)) +48; // +48 (TABELA ASCII)

// Horas.
digit[1] = (Cnt_hora%10) +48; // Calcula a unidade
digit[0] = (Cnt_hora%100); // Calcula a Dezena
digit[0] = ((digit[0]/10) - ((digit[0]%10)/10)) +48; // +48 (TABELA ASCII)
} // end int digits

void main(){ // Função principal


ADCON1 = 0x0F; // Declara as portas AN como DIGITAIS I/O's
Lcd_Init(); // Inicia o LCD
Lcd_Cmd(_LCD_CLEAR); // Limpa a tela do LCD
Lcd_Cmd(_LCD_CURSOR_OFF); // Desabilita o cursor

while(1){ // Loop Infinito


digits(); // Chama a função que calcula os dígitos
Cnt_seg++; // Incrementa em 1 a variável de cnt dos seg

if(Cnt_seg==60){ // Se: segundos = 60...


Cnt_seg = 0; // Zera contagem de segundos
Cnt_min++; // Incrementa em 1 contagem dos minutos
} //end if

if(Cnt_min==60){ // Se: minutos = 60...


Cnt_min = 0; // Zera a contagem de minutos
Cnt_hora++; // Incrementa em 1 contagem das horas
} //end if

if (Cnt_hora==24){ // Se: horas = 24...


Cnt_hora = 0; // Zera a contagem das horas
} // end if

Lcd_Out(1,7, txt); // Mostra o texto no display


Lcd_Out(2,5, digit); // Mostra os dígitos no display
delay_ms(1000); // Delay de 1s
} //end loop infinito
} //end void main
Tarefa: Faça um temporizador de 20min com acionamento de carga(Buzzer ou ventoinha) em 3 periodos do relógio.
///////////////////////////////////////////////////////////////////////////////////////////////

14
4. Conversor A/D.
O conversor analógico-digital é capaz de gerar uma representação digital a partir de uma grandeza
analógica, normalmente um sinal representado por um nível de tensão ou intensidade de corrente
elétrica. O módulo ADC presente no PIC18F4550 permite a conversão para o numero digital de até 10
bits correspondente ao sinal analógico na entrada, o micro controlador apresenta ainda 3
registradores responsáveis pela configuração do módulo ADC, são eles, ADCON1,2 e 3.
Antes da manipulação do Conversor A/D é importante lembrar que:
• Os potenciômetros podem ser conectados aos canais analógicos AN0 e AN1 através das
chaves 1 e 2, respectivamente, do DIP DP1.
• O sensor de temperatura pode ser conectado ao canal analógico AN2 acionando-se a chave
3 do DIP DP1.
• A utilização das portas analógicas deve ser configurada por software.
• A seguir tem-se o esquema dos periféricos relacionados ao ADC.

4.1. Conversor A/D + LEDS.


O programa a seguir utiliza o módulo dos LED’s junto com ADC e tem como objetivo acender os
LED’s referentes à tensão no potenciômetro, para isso são criadas duas variáveis, uma para
receber o valor de leitura do conversor AD e outra que será usada numa pequena álgebra para
acender os LED’s.

15
Na Função principal são configurados os registradores do módulo ADC, que são de crucial
importância no processo, também são configurados os PORT’s D e A, nos quais estão presentes os
LED’s e a entrada analógica, respectivamente.
No Loop Infinito a variável ‘leitura’ recebe o valor lido do módulo ADC, logo após, divide esse
valor por 127 e atribui o resultado da divisão à variável ‘led_on’. E então os Led’s referentes ao
valor da divisão serão acionados através dos laços if’s que tiverem sua condição atendida. Logo
quanto maior o valor da tensão no potenciômetro, mais LED’s serão ativados.

///////////////////////////////////////////////////////////////////////////////////////////////
//Define atalhos para os pinos:
#define D0 RD0_bit
#define D1 RD1_bit
#define D2 RD2_bit
#define D3 RD3_bit
#define D4 RD4_bit
#define D5 RD5_bit
#define D6 RD6_bit
#define D7 RD7_bit

int leitura = 0; // Variável de Leitura do ADC 0 a 1023 = 1024 estados


int led_on = 0; // Variável Matemática

void main() { // Função Principal


//Configura o ADCON0 e Seleciona a entrada AN0
CHS3_bit = 0;
CHS2_bit = 0;
CHS1_bit = 0;
CHS0_bit = 0;

ADON_bit = 1; // ativa o módulo conversor A/D

// Configura ADCON1 e Declara apenas a entrada AN0 como analógica


PCFG3_bit = 1;
PCFG2_bit = 1;
PCFG1_bit = 1;
PCFG0_bit = 0;

TRISD = 0x00; // Seta o PORTD como saída exceto o bit RD7


PORTD = 0x00; // inicia o PORT’D em Low

TRISA = 0x01; // Declara apenas o bit RA0 como entrada e o resto como saída
PORTA = 0x00; // inicia PORT’A em Low

while(1){
leitura = ADC_read(0); /* Atribui a variável 'leitura' à
Função ADC_read(que lê a porta 0)*/

// 1023/8 = 127
led_on = leitura/127; // Led_on recebe o valor da divisão de 'leitura/127'

if(led_on>=0)PORTD = 0x00; // coloca todo o PORTD em low


//a cada 127 bits, acende +1 led
if(led_on>=1) D0 = 1;

if(led_on>=2) D1 = 1;

16
if(led_on>=3) D2 = 1;

if(led_on>=4) D3 = 1;

if(led_on>=5) D4 = 1;

if(led_on>=6) D5 = 1;

if(led_on>=7) D6 = 1;

if(led_on>=8) D7 = 1;

delay_ms(200); // intervalo de tempo do loop infinito


} //end while
} // end void main
///////////////////////////////////////////////////////////////////////////////////////////////
4.2. Conversor A/D + Display de 7 segmentos.
Utilizando os conhecimentos adquiridos nos módulos ADC e Display de 7 segmentos é possível
relacioná-los em apenas um programa.
O algoritmo a seguir tem o papel de mostrar no display de 7 segmentos o número digital
equivalente a entrada analógica. Para isso, primeiramente são declarados os valores, em
hexadecimal, dos dígitos que serão mostrados nos displays, e abaixo, são criadas as variáveis que
serão usadas durante o programa.
Na função principal são configurados os registradores responsáveis pelos displays e também pelo
conversor AD, logo após, é selecionada a porta analógica que será lida durante o processo.
No início do Loop Infinito há uma pequena álgebra para atribuir cada digito ao seu respectivo
display, vale lembrar que essa matemática já foi explicada anteriormente. Na última linha do Loop o
valor lido na porta analógica AN0 é convertido para digital, através da função ‘ADC_read();’, e
depois é atribuído a variável ‘Cnt’, lembrando que a variável ‘Cnt’ é manipulada nos cálculos para
mostrar os dígitos nos displays.

///////////////////////////////////////////////////////////////////////////////////////////////
unsignedchar digito[]= { 0x3F, // 0 Define cada segmento
0x06, // 1 dos valores mostrados
0x5B, // 2 no display de LEDs
0x4F, // 3 em Hexadecimal para ficar
0x66, // 4 mais fácil de codificar
0x6D, // 5 cada dígito dos valores
0x7D, // 6 mostrados no display.
0x07, // 7
0x7F, // 8
0x67}; // 9

int Cnt; // variável de contagem


int Mil, Cen, Dez, Uni = 0; //variáveis do display =
//Milênio, Centena, Dezena, Unidade.

void main(){ //--Função Principal

TRISD = 0x80; //Declara RD7 como entrada e os demais pinos como saídas
PORTD = 0x00; //Coloca todos os pinos de PORTD em low
TRISE = 0x00; //Declara todos os pinos do registrador PORT'E como saídas
PORTE = 0x00; //Coloca todos os pinos de PORT'E em low
TRISA.RA5 = 0; //Declara o pino RA5 como saída
PORTA.RA5 = 0; //Coloca o pino RA5 em low

// Seleciona a entrada AN0


CHS3_bit = 0;
CHS2_bit = 0;

17
CHS1_bit = 0;
CHS0_bit = 0;

ADON_bit = 1; // Liga o módulo conversor A/D

// Declara apenas a entrada AN0 como analógica


PCFG3_bit = 1;
PCFG2_bit = 1;
PCFG1_bit = 1;
PCFG0_bit = 0;

Cnt = ADC_read(0); /* Atribui à variável 'Cnt' o valor da


Função ADC_read(que lê a porta 0) */

while (1){ // Loop infinito


Uni = (Cnt%10); // Calcula a unidade
Dez = (Cnt%100); // Calcula a dezena
Dez = (Dez/10) - ((Dez%10)/10); // Calcula a dezena
Cen = (Cnt%1000); // Calcula a centena
Cen = (Cen/100)-((Cen%100)/100); // Calcula a centena
Mil = (Cnt/1000) - ((Mil%1000)/1000); // calcula o milênio

PORTD = (digito[Uni]); // Apresenta Digito[Unidade_do_contador]


RA5_bit = 1; // Habilita RA5
delay_ms(5); // Aguarda 5ms
RA5_bit = 0; // Desabilita

PORTD = (digito[Dez]); // Apresenta Digito[Dezena_do_contador]


RE2_bit = 1; // Habilita RE2
delay_ms(5); // Aguarda 5ms
RE2_bit = 0; // Desabilita

PORTD = (digito[Cen]); // Apresenta Digito[Centena_do_contador]


RE1_bit = 1; // Habilita RE1
delay_ms(5); // Aguarda 5ms
RE1_bit = 0; // Desabilita

PORTD = (digito[Mil]); // Apresenta Digito[Milenar_do_contador]


RE0_bit = 1; // Habilita RE0
delay_ms(5); // Aguarda 5ms
RE0_bit = 0; // Desabilita

Cnt = ADC_read(0); // Atribui o valor de ADC_read à variável 'Cnt'


}// end while
}// end void main
///////////////////////////////////////////////////////////////////////////////////////////////

4.3. Conversor A/D + LCD.


Após o entendimento do programa anterior a assimilação do código a seguir se torna muito
mais simples, pois os dois programas são muito semelhantes já que apresentam a mesma
finalidade, porem em diferentes displays.
Para mostrar o estado digital no LCD, correspondente a entrada analógica, primeiramente são
definidos as conexões dos pinos do PIC com os do display, abaixo, são criadas duas variáveis, uma
para armazenar a leitura do ADC e outra, do tipo ponteiro, para armazenar os dígitos que serão
mostrados no LCD.
Na função ‘Display’ é calculado, e mostrado, o valor de cada algarismo que preenche a variável
‘digit’, da mesma forma explicada no programa anterior, porém nesse caso, nota-se a soma +48,
que serve para chamar o dígito adequado na tabela ASCII.

18
Na função principal é inicializado o display LCD e também é configurado o registrador PORT’A,
onde se encontra a porta analógica que faz parte do processo. No Loop Infinito o valor lido pelo
módulo ADC é armazenado na variável ‘Cnt’ e ainda é chamada a Display para calcular os
algarismos e mostrá-los no LCD.

///////////////////////////////////////////////////////////////////////////////////////////////
// Módulo de conexões do LCD com o PIC
sbit LCD_RS at RE0_bit;
sbit LCD_EN at RE1_bit;
sbit LCD_D4 at RD4_bit;
sbit LCD_D5 at RD5_bit;
sbit LCD_D6 at RD6_bit;
sbit LCD_D7 at RD7_bit;
sbit LCD_RS_Direction at TRISE0_bit;
sbit LCD_EN_Direction at TRISE1_bit;
sbit LCD_D4_Direction at TRISD4_bit;
sbit LCD_D5_Direction at TRISD5_bit;
sbit LCD_D6_Direction at TRISD6_bit;
sbit LCD_D7_Direction at TRISD7_bit;
//end módulo de conexões
unsignedchar Cnt; // variável de armazenamento da leitura do ADC
char *digit = "0000";

void Display(){ // Função que calcula e mostra os dígitos no display


digit[3] = (Cnt%10) +48; // Digito da unidade
digit[2] = (Cnt/10)%10 + 48; // Digito da dezena
digit[1] = (Cnt/100)%10 +48; // Digito da centena
digit[0] = (Cnt/1000) + 48; // Digito do milênio

Lcd_Out(2,7,digit); // Mostra no Display o conteúdo da variável digit


} // end Display

void main(){ //-- Função principal


Lcd_Init(); // Inicializa LCD
Lcd_Cmd(_LCD_CLEAR); // Limpa display
Lcd_Cmd(_LCD_CURSOR_OFF); // Desliga o cursor
TRISA = 0x01; // Declara apenas RA0 como entrada e o resto como saída
PORTA = 0x00; // Coloca PORTA em low
// Seleciona a entrada AN0
CHS3_bit = 0;
CHS2_bit = 0;
CHS1_bit = 0;
CHS0_bit = 0;
ADON_bit = 1; // Liga o módulo conversor A/D
// Declara apenas a entrada AN0 como analógica
PCFG3_bit = 1;
PCFG2_bit = 1;
PCFG1_bit = 1;
PCFG0_bit = 0;

while(1){
Cnt = ADC_read(0); /* Atribui a variável 'Cnt' à
Função ADC_read(que lê a porta 0)*/
Lcd_Out(1,4,"ADC 10bits");
Display(); // Chama a função Display
}//end while
}// end void main
///////////////////////////////////////////////////////////////////////////////////////////////

19
4.4. Termômetro (ADC + LCD).
Utilizando o módulo ADC, Display LCD e o sensor de temperatura (LM35) presentes no Kit de
desenvolvimento AcepicPro, é possível projetar um Termômetro, para isso é importante lembrar
que o LM35 se encontra na porta AN2.

No início do programa são definidos as conexões do LCD com o PIC, a seguir, são criadas as
variáveis do tipo “unsigned char” que armazenam os algarismos e “unsigned long” que armazenam
as grandezas analógicas.
O programa apresenta três funções auxiliares, a primeira (CustomChar) é gerada pelo próprio
MikroC para mostrar no LCD caracteres especiais, nesse caso, a caractere referente à graus. A
segunda função (media_temp) é responsável por calcular a média de 100 medidas de temperatura
no intervalo de 10 milissegundos, para que assim o resultado seja mais exato.
A terceira função auxiliar (Celsius) é responsável por converter os valores lidos pelo módulo ADC
para a escala Celsius de temperatura. Após as funções auxiliares, tem-se a Função Principal onde
são configurados os registradores referentes ao módulo ADC, após isso, é inicializado o LCD.
No Loop infinito é chamada a função Celsius os valores calculados pela função são mostrados no
display.

///////////////////////////////////////////////////////////////////////////////////////////////
//Comunicação entre o PIC e o LCD
sbit LCD_RS at RE0_bit;
sbit LCD_EN at RE1_bit;
sbit LCD_D4 at RD4_bit;
sbit LCD_D5 at RD5_bit;
sbit LCD_D6 at RD6_bit;
sbit LCD_D7 at RD7_bit;
sbit LCD_RS_Direction at TRISE0_bit;
sbit LCD_EN_Direction at TRISE1_bit;
sbit LCD_D4_Direction at TRISD4_bit;
sbit LCD_D5_Direction at TRISD5_bit;
sbit LCD_D6_Direction at TRISD6_bit;
sbit LCD_D7_Direction at TRISD7_bit;

//Variáveis Globais
unsignedlong store, t_Celsius;//variáveis que armazenam as grandezas analógicas

//Dígitos para exibir no display:


unsignedchar centena, dezena, unidade, dec1, dec2;

20
constchar character[] = {6,9,6,0,0,0,0,0}; //caractere especial de "graus"

void CustomChar(char pos_row, char pos_char){ /*função gerada pelo mikroC para
Imprimir caracteres especiais*/
char i;
Lcd_Cmd(64);
for (i = 0; i<=7; i++) Lcd_Chr_CP(character[i]);
Lcd_Cmd(_LCD_RETURN_HOME);
Lcd_Chr(pos_row, pos_char, 0);
}//end CustomChar

long media_temp(){ // Calcula a média de 100 medidas


unsignedchar i;
unsignedlong temp_store = 0;
for(i=0; i<100; i++)
{
temp_store += ADC_Read(3); /*temp_store = temp_store + ADC_Read(3)
faz o somatório das 100 iterações*/
}//end for
delay_ms(10);
return(temp_store/100); //retorna a média das iterações
}//end media temp

void Celsius(){
store = media_temp(); //atribui a média de 100 medidas à variável store
t_Celsius = (store*5*100)/1023; //converte o valor para escala Celsius

/*as 4 linhas seguintes separam os dígitos do valor em dezena, unidade


e mais duas casas decimais*/
dezena = t_Celsius/10;
unidade = t_Celsius % 10;
dec1 = (((store*5*100)%1023)*10)/1023;
dec2 = (((((store*5*100)%1023)*10)%1023)*10)/1023;
delay_ms(250); //taxa de atualização do display e das medidas
} //end void Celsiuse

void main(){ //-Função principal


ADCON0 = 0x0D; // Seleciona a Porta AN3 // ADON_bit = 1

ADCON1 = 0x0B; // Configura AN0, AN1, AN2, AN3 como portas ANALÓGICAS

TRISA.RA3 = 1; // PINO RA3[AN2] = entrada -> LM35

Lcd_Init(); //Inicializa o display


Lcd_Cmd(_Lcd_Cursor_Off); //Apaga o cursor
Lcd_Cmd(_LCD_CLEAR); //Limpa o display

Lcd_Out(1,3,"TEMPERATURA"); /*Escreve o texto uma vez no display,


Linha 1, coluna 3*/

while(1){ // Loop infinito

21
Celsius(); // Chama a função

Lcd_Chr(2,6,dezena+48); // Mostra o digito da dezena


Lcd_Chr_Cp(unidade+48); // Mostra o digito da unidade
Lcd_Chr_Cp('.'); // Mostra um ponto
Lcd_Chr_Cp(dec1+48); // Mostra o 1º digito decimal
Lcd_Chr_Cp(dec2+48); // Mostra o 2º digito decimal
CustomChar(2,11); // Mostra o caractere º
Lcd_Out(2,12,"C"); //escreve "C" de Celsius no display
} //end while
} //end main
///////////////////////////////////////////////////////////////////////////////////////////////

5. EUSART.
EUSART (Enhanced Universal Synchronous Assynchronous Receiver-Transmitter) é um módulo que
possibilita a transmissão e recepção serial de dados originalmente disponíveis na forma paralela.
Através do protocolo RS-232 é possível comunicar-se com outros dispositivos, por exemplo, com
outros Micro Controladores e até mesmo com o computador. As I/O’s relacionados à EUSART Tx e Rx
são localizados, respectivamente, nos pinos RC6 e RC7 do PIC18F4550.

5.1. EUSART + LEDS.


O programa em questão utiliza a comunicação serial para controlar as ações no PIC, ou seja, é
possível ativar e desativar os LED’s através do teclado do computador, para isso, primeiramente
deve-se criar a variável para o recebimento de dados via serial.
Na função principal é configurado o registrador PORT’D que é relacionado ao LED’s e o
registrador ADCON1 referente à EUSART, que é inicializada à 9600 bps na parte final da função.
No Loop Infinito é verificado se o PIC recebeu os dados da EUSART e se isto for verdadeiro, a
tecla pressionada ativa/desativa os LED’s e também é mostrada na EUSART qual tecla foi digitada.

///////////////////////////////////////////////////////////////////////////////////////////////

22
char uart_rd; // Variável de armazenamento serial
void main() { //--Função Principal
TRISD = 0x00; // Configura o PORTD como saída
PORTD = 0x00; // Inicia em low
ADCON1 = 0x0F; // Configura as portas analógicas

UART1_Init(9600); // Inicializa o módulo serial à 9600bps


Delay_ms(100);
// Escreve no módulo serial
UART1_Write_Text("Pressione as teclas para ativar os LEDs");
UART1_Write(10); // LF, alimentação de linha
UART1_Write(13); // CR, quebra de linha
while (1){
if (UART1_Data_Ready()) { // Se os dados forem recebidos via serial...
uart_rd = UART1_Read(); // Atribui os dados à variável 'uart_rd'
UART1_Write(uart_rd); // E manda via serial
// Ativa o LED relacionado a tecla pressionada:
switch(uart_rd){
case 'a': RD0_bit = 1; break;
case 's': RD1_bit = 1; break;
case 'd': RD2_bit = 1; break;
case 'f': RD3_bit = 1; break;
case 'g': RD4_bit = 1; break;
case 'h': RD5_bit = 1; break;
case 'j': RD6_bit = 1; break;
case 'k': RD7_bit = 1; break;
case 'z': PORTD = 0; break; // Desativa todos os LEDs
} // end switch
} // end if
} // end while

23
} // end main
///////////////////////////////////////////////////////////////////////////////////////////////

5.2. EUSART + LCD.


É de suma importância relacionar os periféricos do Kit, para projetos mais ricos e aplicáveis,
pensando nisso, o programa a seguir utiliza-se do display LCD para demonstrar a comunicação do
computado com o PIC, através do módulo EUSART.
O objetivo do programa é mostrar no LCD, do Kit, as teclas pressionadas no teclado do
computador, para isso é criada uma variável de armazenamento para a manipulação dos dados
recebidos. Na função principal é configurado o registrador ADCON1 e também são inicializados o
LCD e a UART, no fim da função é enviado um texto para a UART e são feitos os comandos de CR e
LF baseados na tabela ASCII.
No Loop Infinito é feita a verificação do recebimento de dados, e se os dados enviados pela
UART forem “lidos” pelo PIC, ele mostra o conteúdo recebido no display LCD, após isso encontram-
se dois comandos relacionados ao LCD.

///////////////////////////////////////////////////////////////////////////////////////////////
// Módulo de conexões do LCD com o PIC
sbit LCD_RS at RE0_bit;
sbit LCD_EN at RE1_bit;
sbit LCD_D4 at RD4_bit;
sbit LCD_D5 at RD5_bit;
sbit LCD_D6 at RD6_bit;
sbit LCD_D7 at RD7_bit;
sbit LCD_RS_Direction at TRISE0_bit;
sbit LCD_EN_Direction at TRISE1_bit;
sbit LCD_D4_Direction at TRISD4_bit;
sbit LCD_D5_Direction at TRISD5_bit;
sbit LCD_D6_Direction at TRISD6_bit;
sbit LCD_D7_Direction at TRISD7_bit;
//end módulo de conexões

char uart_rd; // Variável de armazenamento

void main(){ // Função principal


ADCON1 = 0x0F; // Coloca todas as entradas analógicas como digitais

Lcd_Init(); // Inicializa LCD


Lcd_Cmd(_LCD_CLEAR); // Limpa display
Lcd_Cmd(_LCD_CURSOR_OFF); // Desliga o cursor

UART1_Init(9600); // Inicializa o módulo seria a 9600 bps


Delay_ms(100);

UART1_Write_Text("O Programa mostra no LCD as teclas pressionadas");


UART1_Write(10); // line feed, alimentação de linha
UART1_Write(13); // car return, quebra de linha

while (1) { // Loop Infinito


if (UART1_Data_Ready()) { // Se os dados forem recebidos...
uart_rd = UART1_Read(); // Lê os dados e atribui à variável uart_rd
Lcd_Chr_Cp(uart_rd); // Mostra no LCD o conteúdo de 'uart_rd'
// Se a tecla enter for pressionada, quebra a linha no LCD
if(uart_rd==13) Lcd_Cmd(_LCD_SECOND_ROW);

// Se "\" for pressionada, limpa a tela do LCD


if(uart_rd==92) Lcd_Cmd(_LCD_CLEAR);
}// end if

24
}// end while
}// void main
///////////////////////////////////////////////////////////////////////////////////////////////

5.3. Controle de temperatura via EUSART.


Por fim é possível relacionar o módulo serial com o Termômetro, que por sua vez já foi
explicado anteriormente, para isso foram criadas as variáveis referentes ao medidor de
temperatura e também a variável referente ao módulo serial, que recebe via EUSART o valor da
tecla pressionada.
Na Função Principal são configurados os registradores que regem o processo e também é
iniciado o modulo serial e são escritos os comandos pertencentes ao controlador de temperatura
na porta serial. No Loop Infinito encontra-se o Laço “If” referente à comunicação serial e também
laços de condições para que o programa funcione corretamente.
No laço Switch são executadas as ações referentes às teclas pressionadas, após a função
principal têm-se as funções auxiliares: “Celsius”, para medir e transformar a temperatura para a
escala em questão; “Average_Temp” com intuito de calcular a média das medições para ler a
temperatura com maior precisão; “Custom Char”, função gerada pelo MikroC referente a
caractere especial mostrada no display LCD.

///////////////////////////////////////////////////////////////////////////////////////////////
// Conexões dos pinos do LCD com os pinos do PIC
sbit LCD_RS at RE0_bit;
sbit LCD_EN at RE1_bit;
sbit LCD_D4 at RD4_bit;
sbit LCD_D5 at RD5_bit;
sbit LCD_D6 at RD6_bit;
sbit LCD_D7 at RD7_bit;
sbit LCD_RS_Direction at TRISE0_bit;
sbit LCD_EN_Direction at TRISE1_bit;
sbit LCD_D4_Direction at TRISD4_bit;
sbit LCD_D5_Direction at TRISD5_bit;
sbit LCD_D6_Direction at TRISD6_bit;
sbit LCD_D7_Direction at TRISD7_bit;

char tecla_uart; // Variável que recebe o valor da tecla pressionada


unsigned bit lig; // variável auxiliar do tipo bit
unsignedlong store, t_Celsius;//variáveis armazenam as grandezas analógicas
unsignedshort dezena, unidade, dec1, dec2; //dígitos para exibir no display
constchar character[] = {6,9,6,0,0,0,0,0}; //caractere especial de "graus"

void Celsius();
void CustomChar(char pos_row, char pos_char);
long average_temp();

#define Res PORTC.RC1


#define Cooler PORTC.RC2
#define disp_t lcd_out(1,1,"TEMP = "); lcd_chr_cp(dezena+48); \
lcd_chr_cp(unidade+48); \
lcd_chr_cp('.'); lcd_chr_cp(dec1+48); \
lcd_chr_cp(dec2+48); CustomChar(1,14); Lcd_Out(1,15,"C");

void main() { //-- Função Principal

ADCON0 = 0x0D; // Seleciona AN3 e habilita o módulo AD


ADCON1 = 0x0B; // Configura a AN3 como porta analógica

TRISC.RC1 = 0x00; //Configura o pino RC1 como saída RESISTOR


TRISC.RC2 = 0x00; //Configura o pino RC2 como saída COOLER

25
PORTC.RC1 = 0; // RESISTOR OFF
PORTC.RC2 = 0; // COOLER OFF

ADON_bit = 1; // Ativa o módulo ADC

Lcd_Init(); // Inicia o LCD


Lcd_Cmd(_LCD_CLEAR); // Limpa a tela do LCD
Lcd_Cmd(_LCD_CURSOR_OFF); // Desabilita o cursor
Lcd_Out(1,1," DISPOSITIVO ");
Lcd_Out(2,1," DESLIGADO ");

UART1_init(9600); // Inicia o módulo serial a 9600bps


delay_ms(100);
UART1_Write_Text("1- LIGAR DISPOSITIVO");
UART1_Write(10); // line feed, alimentação de linha
UART1_Write(13); // car return, quebra de linha

UART1_Write_Text("2- AQUECEDOR");
UART1_Write(10); // line feed, alimentação de linha
UART1_Write(13); // car return, quebra de linha

UART1_Write_Text("3- COOLER");
UART1_Write(10); // line feed, alimentação de linha
UART1_Write(13); // car return, quebra de linha

UART1_Write_Text("0- DESLIGAR TERMOMETRO");


UART1_Write(10); // line feed, alimentação de linha
UART1_Write(13); // car return, quebra de linha
UART1_Write(10); // line feed, alimentação de linha
UART1_Write(13); // car return, quebra de linha
lig=0;

while(1){
Celsius();
if (UART1_Data_Ready()) { // Se os dados forem recebidos...
tecla_uart = UART1_Read(); // Lê os dados e atribui à variável tecla_uart
if(lig==1 || tecla_uart=='1'){ /* Se o botão ligar tiver sido pressionado ou
se o bit lig estiver em high... */
switch(tecla_uart){
// Quando uma tecla for pressionada executa o laço referente à mesma...

case '0':
Lcd_Out(1,1," DISPOSITIVO ");
Lcd_Out(2,1," DESLIGADO ");

UART1_Write_Text("Dispositivo Desligado.");
UART1_Write(10); // line feed, alimentação de linha
UART1_Write(13); // car return, quebra de linha
UART1_Write(10); // line feed, alimentação de linha
UART1_Write(13); // car return, quebra de linha
Res = 0; // Desliga o Resistor de Aquecimento
Cooler = 0; // Desliga o Cooler
lig = 0; // Zera o bit
break;

case '1':
Lcd_Cmd(_LCD_CLEAR); // Limpa a tela
UART1_Write_Text("Termometro Ligado");
UART1_Write(10); // line feed, alimentação de linha
UART1_Write(13); // car return, quebra de linha

26
lig = 1; // Ativa o bit
break;

case '2':
Lcd_Cmd(_LCD_CLEAR); // Limpa a tela
Lcd_Out(2,1," AQUEC. LIGADO ");
Res = 1; // Liga o Resistor de Aquecimento
UART1_Write_Text("Aquecedor Ligado, para desligar pressione 'w'");
UART1_Write(10); // line feed, alimentação de linha
UART1_Write(13); // car return, quebra de linha
break;

case '3':
Lcd_Cmd(_LCD_CLEAR); // Limpa a tela
Cooler = 1; // Liga o Cooler
Lcd_Out(2,1," COOLER LIGADO ");
UART1_Write_Text("Cooler Ligado, para desligar pressione 'q'");
UART1_Write(10); // line feed, alimentação de linha
UART1_Write(13); // car return, quebra de linha
break;

case 'w':
Lcd_Cmd(_LCD_CLEAR); // Limpa a tela
Res = 0; // Desliga o Resistor de Aquecimento
UART1_Write_Text("Aquecedor Desligado.");
UART1_Write(10); // line feed, alimentação de linha
UART1_Write(13); // car return, quebra de linha
break;

case 'q':
Lcd_Cmd(_LCD_CLEAR); // Limpa a tela
Cooler = 0; // Desliga o Cooler
UART1_Write_Text("Cooler Desligado.");
UART1_Write(10); // line feed, alimentação de linha
UART1_Write(13); // car return, quebra de linha
break;

}//end switch
}//end if
}//end if

else { // Se nenhuma tecla foi pressionada...


if(lig==1){ // E se o dispositivo estiver ativado:
disp_t; // Chama a Macro
Celsius(); // Chama a Função
}//end if
}//end else
}//end while
}//end void main

void Celsius(){// --Função que calcula a temperatura


store = average_temp(); //atribui a média de 100 medidas à variável store
t_Celsius = (store*5*100)/1023; //converte o valor para Celsius

/*as 4 linhas seguintes separam os dígitos do valor em dezena, unidade


e mais duas casas decimais*/
dezena = t_Celsius/10;
unidade = t_Celsius % 10;
dec1 = (((store*5*100)%1023)*10)/1023;
dec2 = (((((store*5*100)%1023)*10)%1023)*10)/1023;
delay_ms(100); //taxa de atualização do display e das medidas
}// end Celsius

27
long average_temp(){ // Função que calcula a média das medições
unsignedchar i;
unsignedlong temp_store = 0;

for(i=0; i<100; i++){


// faz o somatório das 100 iterações
temp_store = temp_store + ADC_Read(3);
}//end for
return(temp_store/100); //retorna a média das iterações
}//end average temp

//Função gerada pelo mikroC para mostrar caracteres especiais:


void CustomChar(char pos_row, char pos_char){
char i;
Lcd_Cmd(64);
for (i = 0; i<=7; i++) Lcd_Chr_CP(character[i]);
Lcd_Cmd(_LCD_RETURN_HOME);
Lcd_Chr(pos_row, pos_char, 0);
}//end void CustomChar
///////////////////////////////////////////////////////////////////////////////////////////////

6. PWM (Modulação por Largura de Pulso).


O PIC 18F4550 contém dois módulos PWM que, obviamente, têm duas saídas que se encontram no
PORT’C uma no RC1 outra no RC2.Através do PIC é possível modular a largura do pulso de uma onda,
uma aplicação muito útil na eletrônica digital e analógica. Sobre as conexões módulo PWM no Kit vale
ressaltar:

• Três componentes estão conectados na saída RC2 o LED1, o Buzzer e a Ventoinha.


• A Ventoinha pode ser desativada através da chave 7 do DP2, independente do estado do PIC.
• O Buzzer pode ser desativado através da chave 8 do DP2, independente do estado do PIC.
• O LED pode ser desativada através da chave 8 do DP3, independente do estado do PIC.
• Abaixo nota-se o esquema elétrico dos circuitos em questão:

28
6.1. PWM + LED, Ventoinha e LCD.
Através da modulação da largura de pulso é possível controlar o Duty Cicle das ondas que
mantém os componentes, citados acima, ativados. Para isso configura-se o CCP1 para o módulo
PWM e também o TIMER 2 para auxiliar no processo, lembrando que todas as informações
necessárias para tal então presentes no Datasheet.
O programa a seguir consiste em ajustar o Duty Cicle na saída RC2 através de dois botões, então,
para isso, na função principal são configurados os registradores onde se encontram os botões e
também o registrador referente a saída CCP1, feito isso, são configurados os registradores
responsáveis pelos TIMER0 e TIMER2 que são essências na operação.
O TIMER2 trabalha em conjunto com o módulo PWM e o TIMER0 é utilizado na interrupção
responsável pelo monitoramento dos botões, logo quando TIMER0 chega ao overflow e gerada a
interrupção e se o botão RB0 for pressionado aumenta a largura do pulso, se RB1 for pressionado
diminui-se a largura do pulso.

29
///////////////////////////////////////////////////////////////////////////////////////////////
void interrupt(){ //-- Interrupção
if(T0IF_bit){ // Se houve overflow do TMR0...
T0IF_bit = 0x00; // Reseta a variável que "detecta" o overflow
TMR0L = 0x6C; // Reinicia o Timer0
/*Se RB0 for pressionado... */
if(!RB0_bit) CCPR1L++; // Incrementa o Duty Cicle

/*Se RB1 for pressionado... */


if(!RB1_bit) CCPR1L--; // Decrementa o Duty Cicle
}//end T0IF
}//end interrupt

void main() { //-- Função Principal


ADCON1 = 0x0F; // Configura todos os pinos AN's como DIGITAIS I/O's
TRISC.RC2 = 0x00; // Configura o Pino RC2 como Saída (LED PWM)
TRISB.RB0 = 0x01; // Configura o Pino RB0 como Entrada (BOTÃO+)
TRISB.RB1 = 0x01; // Configura o Pino RB1 como Entrada (BOTÃO-)
T0CON = 0xC6; // Configura registrador timer0 e prescaler 1:128
GIE_bit = 0x01; // Habilita interrupção global
PEIE_bit = 0x01; // Habilita interrupção por periférico
T0IE_bit = 0x01; // Habilita interrupção do timer0
TMR0L = 0x6C; // Inicia o timer0 em 108
PWM1_Init(1000); // Inicializa o PWM a 1KHz
PR2 = 0xFF; // Inicia em 255
T2CON = 0x06; // Timer2 On, Prescaler 1:16
// Ativa o modo PWM
CCP1M2_bit = 1;
CCP1M3_bit = 1;
PWM1_Start();
CCPR1L = 0x00; // Inicia com o Duty Cicle em zero
///////////////////////////////////////////////////////////////////////////////////////////////

6.2. PWM + LCD.


Utilizando os princípios e componentes utilizados no código anterior é possível somar o Display
de Cristal Líquido ao processo,para mostrar o estado do registrador que controla o Duty Cicle, e
assim tornar o programa mais rico e completo.
Para tal basta adicionar as funções do LCD à função principal, e além disso criar um Loop Infinito,
com uma pequena álgebra, já explicada anteriormente, para calcular os dígitos do registrador
CCPR1L e em seguida mostra-los no Display.

///////////////////////////////////////////////////////////////////////////////////////////////
// Módulo de conexões do LCD com o PIC
sbit LCD_RS at RE0_bit;
sbit LCD_EN at RE1_bit;
sbit LCD_D4 at RD4_bit;
sbit LCD_D5 at RD5_bit;
sbit LCD_D6 at RD6_bit;
sbit LCD_D7 at RD7_bit;
sbit LCD_RS_Direction at TRISE0_bit;
sbit LCD_EN_Direction at TRISE1_bit;
sbit LCD_D4_Direction at TRISD4_bit;
sbit LCD_D5_Direction at TRISD5_bit;
sbit LCD_D6_Direction at TRISD6_bit;
sbit LCD_D7_Direction at TRISD7_bit;
//end módulo de conexões
char *digit = "000"; // Variável que mostra no LCD o valor de CCPR1L

30
void interrupt(){ // -- Interrupção
if(T0IF_bit){ // Se ocorreu o overflow do TMR0...
T0IF_bit = 0x00; // Zera a variável de verificação
TMR0L = 0x6C; // Reinicia o TMR0

if(!RB0_bit){ // Se o botão RB0 foi pressionado


CCPR1L++; // Incrementa o Duty Cicle
delay_ms(200); // Delay anti-bounce
}//end if

if(!RB1_bit){ // Se o botão RB1 foi pressionado


CCPR1L--; // Decrementa o Duty Cicle
delay_ms(200); // Delay anti-bounce
}//end if
}//end T0IF
}//end interrupt

void main() { // -- Função Principal


ADCON1 = 0x0F; // Coloca todos os pinos como DIGITAIS I/O's
TRISC.RC2 = 0x00; // Configura RC2 como Saída (LED PWM)
TRISB.RB0 = 0x01; // Configura RB0 como Entrada
TRISB.RB1 = 0x01; // Configura RB1 como Entrada
T0CON = 0xC6; // Configura registrador timer0 e prescaler 1:128
GIE_bit = 0x01; // Habilita interrupção global
PEIE_bit = 0x01; // Habilita interrupção por periférico
T0IE_bit = 0x01; // Habilita interrupção do timer0
TMR0L = 0x6C; // Inicia o timer0 em 108
PR2 = 0xFF; // Inicia em 255
T2CON = 0x06; // Timer2 On, Prescaler 1:16
// Ativa o modo PWM
CCP1M2_bit = 1;
CCP1M3_bit = 1;

CCPR1L = 0x00; // Inicia com o Led desativado


PWM1_Init(1000); // Inicializa o módulo PWM a 1KHz
Lcd_Init(); // Inicializa LCD
Lcd_Cmd(_LCD_CLEAR); // Limpa display
Lcd_Cmd(_LCD_CURSOR_OFF); // Desliga o Cursor
PWM1_Start;

while(1){ // Loop infinito


digit[2] = (CCPR1L%10) +48; // Calcula o digito da UNIDADE
digit[1] = (CCPR1L/10)%10 + 48; // Calcula o digito da DEZENA
digit[0] = (CCPR1L/100)%10 +48; // Calcula o digito da CENTENA

// Mostra no display:
Lcd_Out(1,1," Duty Cicle: ");
Lcd_Out(2,5, digit);
Lcd_Out(2,9,"bits");
}//end while
}//end void main
///////////////////////////////////////////////////////////////////////////////////////////////

6.3. PWM + Buzzer.


Através dos princípios de PWM já estudado é possível relacioná-lo com o Buzzer, com intuito de
apresentar um som, em uma frequência gerada através do módulo PWM, tocando na saída. Sabe-se
que cada nota musical tem uma frequência específica e através disso é possível reproduzir melodias.
No início do programa são definidas as notas e suas frequências e também uma pequena álgebra
para determinar a duração das notas. Na função principal é declarado o pino onde se encontra o
buzzer, na função Sound_Init(). Após isso é criada a função “melodia” que contém o comando

31
Sound_Play(), responsável por gerar as frequências e as durações das notas que serão tocadas, para
reproduzir a melodia desejada.

///////////////////////////////////////////////////////////////////////////////////////////////
// DEFINE AS FREQUÊNCIAS DAS NOTAS
#define E3 164.81
#define F3 174.61
#define Gb3 185.00
#define G3 196.00
#define Ab3 207.65
#define A3 220.00
#define Bb3 233.08
#define B3 246.94
#define C4 261.63
#define Db4 277.18
#define D4 293.66
#define Eb4 311.13
#define E4 329.63
#define F4 349.23
#define Gb4 369.99
#define G4 392.00
#define Ab4 415.30
#define A4 440.00
#define Bb4 466.16
#define B4 493.88

// DURAÇÃO DAS NOTAS:


#define BPM 150 // BPM - Velocidade que as notas são reproduzidas
#define H 2*Q // half 2/4
#define Q 60000/BPM // quarter 1/4
#define E Q/2 // eighth 1/8
#define S Q/4 // sixteenth 1/16
#define W 4*Q // whole 4/4

void melodia();

void main() { // --Função Principal


ADCON1 = 0x0F; // Configura as PORTAS como digitais I/O's
/* Inicia a função do som declarando qual será o pino de saída (RC2)*/
Sound_Init(&PORTC, 2);
melodia(); //Chama a Função 'melodia'
}//end void

void melodia() { // Função que toca os sons nas frequências desejadas


//Sound_Play( Nota, Duração)
//delay_ms(1+Duração)

Sound_Play(A3,Q);
delay_ms(1+Q);
Sound_Play(A3,Q);
delay_ms(1+Q);
Sound_Play(A3,Q);
delay_ms(1+Q);
Sound_Play(F3,E+S);
delay_ms(1+E+S);
Sound_Play(C4,S);
delay_ms(1+S);

Sound_Play(A3,Q);
delay_ms(1+Q);

32
Sound_Play(F3,E+S);
delay_ms(1+E+S);
Sound_Play(C4,S);
delay_ms(1+S);
Sound_Play(A3,H);
delay_ms(1+H);

Sound_Play(E4,Q);
delay_ms(1+Q);
Sound_Play(E4,Q);
delay_ms(1+Q);
Sound_Play(E4,Q);
delay_ms(1+Q);
Sound_Play(F4,E+S);
delay_ms(1+E+S);
Sound_Play(C4,S);
delay_ms(1+S);

Sound_Play(Ab3,Q);
delay_ms(1+Q);
Sound_Play(F3,E+S);
delay_ms(1+E+S);
Sound_Play(C4,S);
delay_ms(1+S);
Sound_Play(A3,H);
delay_ms(1+H);
}//end melodia
///////////////////////////////////////////////////////////////////////////////////////////////

7. Teclado Matricial.
O Teclado matricial é do tipo 4x4 e está conectado ao PORT’B, conforme o esquema abaixo, logo 4
bits do registrador B serão linhas e os outros 4 serão colunas e isso deve ser levado em conta em todos
os programas que utilizam o teclado.

33
Antes de iniciar o módulo referente ao Teclado Matricial é de suma importância entender o
mecanismo das interrupções, pois são fundamentais para o funcionamento do Teclado, já que ele está
disposto na forma de Matriz.

Interrupções:
Uma interrupção é um sinal de um dispositivo que tipicamente resulta em uma troca de contextos,
isto é, o processador para de fazer o que está fazendo para atender o periférico que pediu a
interrupção. Nesse caso a interrupção será acionada pelo Overflow do Timer 0, que é configurado com
um tempo X, para verificarse houve o pressionamento das teclas, então o programa é “interrompido”
para a execução dos comandos presente na função de interrupção, e em seguida o programa retorna
ao ponto que foi obstruído. Abaixo nota-se o diagrama padrão de uma interrupção:

7.1. Teclado + Display 7 segmentos.


O programa consiste em relacionar o módulo do display de 7 segmentos e o teclado, o código
tem como intuito mostrar nos displays quais teclas foram pressionadas. Primeiramente são
definidos os pinos que serão linhas e os pinos que serão colunas, a seguir, são definidos os dígitos
que serão mostrados nos displays, atentando para o fato de que os quatro displays são
multiplexados logo o valor mostrado neles serão os mesmos.
Na função principal são configurados os registradores que fazem parte do processo, os quais já
foram explicados anteriormente, exceto os responsáveis pela interrupção, que basicamente ativam
as interrupções por periféricos e também configura o registrador do TIMER 0para gerar uma
interrupção através do overflow.
Na função de interrupção é feita a varredura das teclas e se alguma tecla for pressionada, atribui
o respectivo valor à variável “tecla” e então no Loop Infinito o valor da variável “tecla” chama o
digito em hexadecimal referente à mesma, para mostrá-lo nos displays.

///////////////////////////////////////////////////////////////////////////////////////////////
//Define os pinos das colunas do Teclado
#define col_1 RB4_bit

34
#define col_2 RB5_bit
#define col_3 RB6_bit
#define col_4 RB7_bit

//Define os pinos das linhas


#define row_A RB0_bit
#define row_B RB1_bit
#define row_C RB2_bit
#define row_D RB3_bit

unsignedchar digito[]= { 0x3F, // 0 Define cada segmento


0x06, // 1 dos valores mostrados
0x5B, // 2 no display de LEDs
0x4F, // 3 em Hexadecimal para ficar
0x66, // 4 mais fácil de codificar
0x6D, // 5 cada dígito dos valores
0x7D, // 6 mostrados no display.
0x07, // 7
0x7F, // 8
0x67}; // 9

unsignedchar tecla = 0x00; //variável do vetor


unsignedchar control = 0x01; //variável de controle

void interrupt(){ //--Função Interrupção


if(T0IF_bit){ //Houve estouro do Timer0?
T0IF_bit = 0x00; //Limpa a flag de overflow
TMR0L = 0x6C; //Reinicia o timer0

if(col_1 && control == 0x01){ //Coluna 1 em nível high? Control igual 1?


control = 0x02;
col_1 = 0x00; //Apenas a coluna 1 em nível baixo
col_2 = 0x01;
col_3 = 0x01;
//Se a tecla for pressionada atribui-se o valor desejado à ela...
if(!row_A) tecla = 1;
elseif(!row_B) tecla = 4;
elseif(!row_C) tecla = 7;
}//end if

elseif(col_2 && control == 0x02){ //Coluna 2 em nível high? Control igual 2?


control = 0x03;
col_1 = 0x01;
col_2 = 0x00; //Apenas a coluna 2 em nível baixo
col_3 = 0x01;

if(!row_A) tecla = 2;
elseif(!row_B) tecla = 5;
elseif(!row_C) tecla = 8;
elseif(!row_D) tecla = 0;
}//end else if

elseif(col_3 && control == 0x03){ //Coluna 3 em nível high? Control igual 3?


control = 0x01;
col_1 = 0x01;
col_2 = 0x01;
col_3 = 0x00; //Apenas a coluna 3 em nível baixo

if(!row_A) tecla = 3;
elseif(!row_B) tecla = 6;
elseif(!row_C) tecla = 9;
}//end else if

35
}//end T0IF_bit
}//end interrupt

void main(){ //-- Função Principal


ADON_bit = 0x00; //Módulo ADC é desligado
ADCON1 = 0x0F; //Coloca todos os pinos como DIGITAIS I/O's
RBPU_bit = 0x00; //Habilita os Resistores de Pull Up
T0CON = 0xC6; //Configura registrador timer0 e prescaler 1:128
GIE_bit = 0x01; //Habilita interrupção global
PEIE_bit = 0x01; //Habilita interrupção por periférico
T0IE_bit = 0x01; //Habilita interrupção do timer0
TMR0L = 0x6C; //Inicia o timer0 em 108
TRISB = 0x0F; //Configura o nibble mais significativo como entrada
PORTB = 0xFF; //PORTB inicia em high

TRISD = 0x00; //Declara o PORTD como saída


PORTD = 0x00; //Inicia em LOW

TRISA.RA5= 0x00; //Declara o bit RA5 como saída


PORTA.RA5= 0x01; //Inicia RA5 em HIGH
TRISE = 0x00; //Declara PORT'E como saída
PORTE = 0xFF; //Inicia PORTE em HIGH

while(1){ //-- Loop Infinito


// Mostra no display o digito chamado pela variável 'tecla'
PORTD = digito[tecla] ;
}//end while
} // end main
///////////////////////////////////////////////////////////////////////////////////////////////

7.2. Teclado + LCD.


Utilizando os mesmos princípios explicados anteriormente é possível relacionar o teclado
matricial com o LCD e assim mostrar no display as teclas que foram digitadas, para isso são
declarados, no início do programa, os pinos das conexões do teclado com o PIC e igualmente para o
LCD, e após isso são criadas as variáveis que serão manipuladas no processo.
Na função Principal são configurados os registradores referentes ao teclado (PORTB), Timer0
(Interrupção), e também é iniciado o módulo LCD. No Loop Infinito é mostrado no LCD o valor da
variável “teclas”.
Na interrupção é feita a verificação do estado dos botões do teclado matricial, e quando um
botão for pressionado é atribuído o valor referente a caractere, encontrado na tabela ASCII, à
variável “teclas”, após isso se tem o delay “anti-bounce” e também o incremento da variável “i”
para que a caractere seja mostrada na próxima coluna do LCD.

///////////////////////////////////////////////////////////////////////////////////////////////
// Módulo de conexões do LCD com o PIC
sbit LCD_RS at RE0_bit;
sbit LCD_EN at RE1_bit;
sbit LCD_D4 at RD4_bit;
sbit LCD_D5 at RD5_bit;
sbit LCD_D6 at RD6_bit;
sbit LCD_D7 at RD7_bit;
sbit LCD_RS_Direction at TRISE0_bit;
sbit LCD_EN_Direction at TRISE1_bit;
sbit LCD_D4_Direction at TRISD4_bit;
sbit LCD_D5_Direction at TRISD5_bit;
sbit LCD_D6_Direction at TRISD6_bit;
sbit LCD_D7_Direction at TRISD7_bit;
//end módulo de conexões

36
//Define os pinos do PIC das colunas do Teclado
#define col_1 RB4_bit
#define col_2 RB5_bit
#define col_3 RB6_bit
#define col_4 RB7_bit

//Define os pinos do PIC das linhas


#define row_A RB0_bit
#define row_B RB1_bit
#define row_C RB2_bit
#define row_D RB3_bit
//end definições do teclado
unsignedchar i = 0; // variável auxiliar
unsignedchar teclas=0; // variável das teclas,
unsigned bit clear; // variável limpa tela
unsignedchar control = 0x01; // variável de controle

void interrupt(){ // Função da interrupção


if(T0IF_bit){ //Houve overflow do Timer0?
T0IF_bit = 0x00; //Limpa a flag
TMR0L = 0x6C; //Reinicia o timer0

if(col_1 && control == 0x01){ //Coluna 1 em nível high? Control igual 1?


control = 0x02; //Apenas a coluna 1 em nível baixo
col_1 = 0x00;
col_2 = 0x01;
col_3 = 0x01;
col_4 = 0x01;
// TECLAS +(ASCII) // ANTI-BOUNCE // incremento de i
if(!row_A) {teclas = 1+48; delay_ms(270); i++; } //DIG 1
elseif(!row_B) {teclas = 4+48; delay_ms(270); i++; } //DIG 4
elseif(!row_C) {teclas = 7+48; delay_ms(270); i++; } //DIG 7
elseif(!row_D) {clear=1; delay_ms(270);} // Coloca Clear em HIGH
} //end if

elseif(col_2 && control == 0x02){ //Col_2 em nível high? Control igual 2?


control = 0x03;
col_1 = 0x01;
col_2 = 0x00; //Apenas a coluna 2 em nível baixo
col_3 = 0x01;
col_4 = 0x01;
if(!row_A) {teclas = 2+48; delay_ms(270); i++;} //DIG 2
elseif(!row_B) {teclas = 5+48; delay_ms(270); i++;} //DIG 5
elseif(!row_C) {teclas = 8+48; delay_ms(270); i++;} //DIG 8
elseif(!row_D) {teclas = 0+48; delay_ms(270); i++;} //DIG 0
} //end else if

elseif(col_3 && control == 0x03){ //Col_3 em nível high? Control igual 3?


control = 0x04;
col_1 = 0x01;
col_2 = 0x01;
col_3 = 0x00; //Apenas a coluna 3 em nível baixo
col_4 = 0x01;
if(!row_A) {teclas = 3+48; delay_ms(270); i++;} //DIG 3
elseif(!row_B) {teclas = 6+48; delay_ms(270); i++;} //DIG 6
elseif(!row_C) {teclas = 9+48; delay_ms(270); i++;} //DIG 9
elseif(!row_D) {teclas = 35; delay_ms(270); i++;} //DIG #
} //end else if

elseif(col_4 && control == 0x04){ //Col_4 em nível high? Control igual 4?


control = 0x01;
col_1 = 0x01;

37
col_2 = 0x01;
col_3 = 0x01;
col_4 = 0x00; //Apenas a coluna 4 em nível baixo

if(!row_D) {teclas = 68; delay_ms(270); i++;} //DIG D


elseif(!row_B) {teclas = 66; delay_ms(270); i++;} //DIG B
elseif(!row_C) {teclas = 67; delay_ms(270); i++;} //DIG C
elseif(!row_A) {teclas = 65; delay_ms(270); i++;} //DIG A
} //end else if
} //end IF TMR0 INTERRUPT
} //end void interrupt

void main(){ // Função principal


ADON_bit = 0x00; //Módulo ADC é desligado
ADCON1 = 0x0F; //Coloca todos os pinos como DIGITAIS I/O's
RBPU_bit = 0x00; //Habilita os Resistores de Pull Up
T0CON = 0xC6; //Configura registrador timer0 e prescaler 1:128
GIE_bit = 0x01; //Habilita interrupção global
PEIE_bit = 0x01; //Habilita interrupção por periférico
T0IE_bit = 0x01; //Habilita interrupção do timer0
TMR0L = 0x6C; //Inicia o timer0 em 108
TRISB = 0x0F; //Seta o nibble mais significativo como saída
PORTB = 0xFF; //PORTB inicia em high
Lcd_Init(); // Inicia o LCD
Lcd_Cmd(_LCD_CLEAR); // Limpa display
Lcd_Cmd(_LCD_CURSOR_OFF); // Desliga o cursor

while(1){ //-Loop infinito


Lcd_Out(1,1, "TECLAS DIGITADAS");

// Quando i for maior que 0 o valor de 'teclas' é mostrado no display...


if (i>0) Lcd_Chr(2,i, teclas);

if(clear==1 || i>16){ // Se clear estiver em HIGH ou i for maior que 16...


Lcd_Cmd(_LCD_CLEAR); // Limpa a tela
delay_ms(200);
i=0; //Zera a variável de contagem
clear=0; //Zera a variável de limpeza
} //end if
} //end while
} //end main
///////////////////////////////////////////////////////////////////////////////////////////////

8. Comunicação I2C.
Para o entendimento dos algoritmos a seguir é necessário o estudo do protocolo de comunicação I2C,
e ainda modo de implementação desse protocolo no PIC18F4550, tendo isso em mente, é possível
desenvolver diversosprojetos utilizando os periféricos anexados aos Kits dos Microcontroladores.
Os algoritmos a seguir, por usarem o mesmo protocolo de comunicação, seguem um padrão nos
dispositivos Mestre e outro padrão nos dispositivos escravos. O padrão de programação nos “Mestres”
consiste em declarar os endereços dos “Escravos” que serão instruídos, após isso duas funções são
arquitetadas, uma para enviar dados para o escravo endereçado e outra para ler dados do mesmo.
Para os dispositivos “Escravos”são declaradas, no mínimo, duas variáveis que são manipuladas na
interrupção, que é gerada pelo “Mestre” com o intuito é de enviar dados, através de uma variável, ou
receber dados, através de outra variável. A partir disso foram desenvolvidos os algoritmos abaixo.

8.1. Botão + Display de 7 Segmentos.


No dispositivo “Mestre” está anexado um botão, presente no Kit de ACEPIC de desenvolvimento, e
quando o botão é pressionado, o Mestre incrementa a variável “cont” e envia um byte com o valor dela

38
para o dispositivo “Escravo”, através do protocolo I2C de comunicação. O Escravo por sua vez, está
programado para receber esse byte e converter de binário para decimal e mostrar o valor convertido nos
displays de 7 segmentos.

Algoritmo para o Mestre.


///////////////////////////////////////////////////////////////////////////////////////////////

const Slave_Addy = 0xA0; // Endereço do Escravo


unsignedshort cont = 0x00; // Variável de contagem

void send(unsignedshort send_data){ // Função para enviar dados


I2C1_Init(100000); // Inicia o módulo I2C a 100Khz
I2C1_Start(); // Inicia a transmissão
I2C1_Wr(Slave_Addy); // Endereço do escravo
I2C1_Wr(send_data); // Envia o byte de dados
I2C1_Stop(); // Finaliza a transmissão
}//end send

unsignedshort read(){ //Função para receber dados


unsignedshort read_data; // variável de leitura
I2C1_Init(100000); // Inicia o módulo I2C a 100Khz
I2C1_Start(); // Inicia a transmissão
I2C1_Wr(Slave_Addy + 1); // Endereço do escravo + 1 (bit de R/W )
read_data = I2C1_Rd(0); // Armazena o byte recebido na variável read_data
delay_ms(100); // delay de 100 milissegundos
I2C1_Stop(); // Finaliza a transmissão
return read_data;
}// end read

void init(){ // Função de inicialização


I2C1_Init(100000); // Inicia o modo I2C à 100Khz
TRISB = 0xFF; // Configura PORTB como entrada
TRISD = 0x00; // Configura PORTD como saída
PORTD = 0x00; // Inicia o PORTD em low
ADCON1 = 0x0F; //Coloca todos os pinos como DIGITAIS I/O's

/* 1000 = I2C Master mode, clock = FOSC/(4 * (SSPADD + 1)) */


SSPM3_bit = 1;
SSPM2_bit = 0;
SSPM1_bit = 0;
SSPM0_bit = 0;
}

void main() { //Função principal


init(); // Chama a função 'init'

while (1){ // Loop Infinito


if (PORTB.RB2 == 0){ // Se o botão for pressionado...
delay_ms(250); // Delay Anti-bounce
cont++; // incrementa a variável cont
PORTD++; // incrementa o PORTD
send (cont); // Envia o valor da variável 'cont' através do I2C
}// end if
}// end while
}// end void main

///////////////////////////////////////////////////////////////////////////////////////////////

Algoritmo para o Escravo


///////////////////////////////////////////////////////////////////////////////////////////////

39
const Addy = 0xA0; // Endereço do escravo no barramento I2C
unsignedchar digito[] = { 0b10111111, // 0 Define cada segmento
0b10000110, // 1 dos valores mostrados
0b11011011, // 2 no display de LEDs
0b11001111, // 3 em binário para ficar
0b11100110, // 4 mais fácil de codificar
0b11101101, // 5 cada dígito dos valores
0b11111101, // 6 mostrados no display.
0b10000111, // 7
0b11111111, // 8
0b11100111}; // 9

unsignedshort Cnt = 0; // variável de contagem


int Mil, Cen, Dez, Uni = 0; //variáveis do display
/*Milênio, Centena, Dezena, Unidade*/

/* Variáveis Globais I2C */


unsignedshort j; // variável auxiliar
unsignedshort rxbuffer; // variável de recebimento de dados
unsignedshort tx_data; // variável de transmissão de dados

void init(){ //Função de Inicialização


ADCON1 = 0x0F; //Seta todas as portas como digitas
TRISB = 0xFF; //Define os pinos do modulo I2C como entrada
SSPADD = Addy; //Define o endereço do slave
SSPCON1 = 0x36; //Configura I2c como slave, 7bits
PIE1.SSPIE = 1; //Habilita interrupção do modulo I2c
INTCON = 0xC0; //Habilita interrupção global e dos periféricos
} // end void init

void interrupt(){ // Função interrupção


if (PIR1.SSPIF == 1){ // I2C Interrupt
PIR1.SSPIF = 0; // Reinicia a flag de interrupção

//Transmite dados para o Mestre


if (SSPSTAT.R_W == 1){ // Leitura
SSPBUF = tx_data; // Armazena em SSPBUF os dados para enviar...
SSPCON1.CKP = 1; // Libera o Clock
j = SSPBUF; // Lê o buffer para limpar a flag
return;
}
if (SSPSTAT.BF == 0){ // checa se ainda há algo no buffer
j = SSPBUF; // esvazia o buffer
return;
}

//Recebe dados do Mestre


if (SSPSTAT.D_A == 1){ // Dados [não endereço]
rxbuffer = SSPBUF; // armazena os dados do buffer em 'rxbuffer'
j = SSPBUF; // Lê o buffer para limpar a flag [address]
return;
}
}
j = SSPBUF; // Lê o buffer para limpar a flag
} // end Interrupt

void main() { // Função Principal


init(); // chama a função
TRISD = 0x80; //Declara RD7 como entrada e os demais pinos como saídas
PORTD = 0x00; //Coloca todos os pinos de PORTD em low

40
TRISE = 0x00; //Declara todos os pinos do registrador PORT'E como saídas
PORTE = 0x00; //Coloca todos os pinos de PORT'E em low
TRISA.RA5 = 0; //Declara o pino RA5 como saída
PORTA.RA5 = 0; //Coloca o pino RA5 em low

while (1){ // Loop infinito


Cnt = rxbuffer; // atribui o byte recebido por I2C à variável 'Cnt'

Uni = (Cnt%10); // Calcula a unidade


// 1234/10 = 123,4 | Resto = 4. | Uni = 4

Dez = (Cnt%100); // Calcula a dezena


// 1234/100 = 12,34 | Resto 34
Dez = (Dez/10) - ((Dez%10)/10); // Calcula a dezena
// 34/10 = 3,4 - (0,4) | Dez = 3

Cen = (Cnt%1000); // Calcula a centena


// 1234/1000 = 1,234 | Resto 234
Cen = (Cen/100)-((Cen%100)/100); // Calcula a centena
// 234/100 = 2,34 - (0,34) | Cen = 2

Mil = (Cnt/1000) - ((Mil%1000)/1000); // calcula o milênio


// 1234/1000 = 1,234 - (0,234) | Mil = 1

PORTD = (digito[Uni]); // Apresenta Digito[Unidade_do_contador]


RA5_bit = 1; // Habilita RA5
delay_ms(5); // Aguarda 5ms
RA5_bit = 0; // Desabilita

PORTD = (digito[Dez]); // Apresenta Digito[Dezena_do_contador]


RE2_bit = 1; // Habilita RE2
delay_ms(5); // Aguarda 5ms
RE2_bit = 0; // Desabilita

PORTD = (digito[Cen]); // Apresenta Digito[Centena_do_contador]


RE1_bit = 1; // Habilita RE1
delay_ms(5); // Aguarda 5ms
RE1_bit = 0; // Desabilita

PORTD = (digito[Mil]); // Apresenta Digito[Milenar_do_contador]


RE0_bit = 1; // Habilita RE0
delay_ms(5); // Aguarda 5ms
RE0_bit = 0; // Desabilita
} // end while
}// end void main
///////////////////////////////////////////////////////////////////////////////////////////////

8.2. Botão + LCD.


Este programa trabalha de forma um tanto quanto similar ao anterior, porém utilizando o
Display de Cristal Líquido (LCD) e nesse caso o Mestre tanto envia dados quanto recebe. O
programa utiliza-se de dois botões, em um o Mestre envia dados para o Escravo, e no outro o
Mestre recebe dados do Escravo.
Quando o botão de envio é pressionado, o Mestre incrementa a variável “contagem” e a
envia para o Escravo, que mostra o valor da mesma em seu LCD, entretanto quando o botão de
recebimento é acionado, o Mestre lê o valor que está sendo mostrado no LCD do Escravo e o
mostra em seu próprio LCD.

Algoritmo para o Mestre

41
///////////////////////////////////////////////////////////////////////////////////////////////
// Conexões dos pinos do LCD com os pinos do PIC
sbit LCD_RS at RE0_bit;
sbit LCD_EN at RE1_bit;
sbit LCD_D4 at RD4_bit;
sbit LCD_D5 at RD5_bit;
sbit LCD_D6 at RD6_bit;
sbit LCD_D7 at RD7_bit;
sbit LCD_RS_Direction at TRISE0_bit;
sbit LCD_EN_Direction at TRISE1_bit;
sbit LCD_D4_Direction at TRISD4_bit;
sbit LCD_D5_Direction at TRISD5_bit;
sbit LCD_D6_Direction at TRISD6_bit;
sbit LCD_D7_Direction at TRISD7_bit;

#define botao_7 PORTB.RB2


#define botao_ast PORTB.RB3

const Slave_Addy = 0xA0; // Endereço do Escravo


unsignedshort contagem = 0x00; // Variável de contagem
unsignedshort Cnt = 0; // Variável de contagem
char *digit = "000"; // Declara os dígitos

void Display_init() // define display_init;


{ // Calcula os dígitos:
digit[2] = (Cnt%10) +48; // digito unidade
digit[1] = (Cnt/10)%10 + 48; // digito da dezena
digit[0] = (Cnt/100)%10 +48; // digito da centena

Lcd_Out(1,7,digit); // mostra a variável digito


Lcd_Out(2,1, "BYTES ENVIADOS"); // mostra no display
}

void send(unsignedshort send_data){ // Função para enviar dados


I2C1_Init(100000); // Inicia o módulo I2C a 100Khz
I2C1_Start(); // Inicia a transmissão
I2C1_Wr(Slave_Addy); // Endereço do escravo
I2C1_Wr(send_data); // Envia o byte de dados
I2C1_Stop(); // Finaliza a transmissão
} // end send

unsignedshort read(){ // Função para receber dados


unsignedshort read_data; // variável de leitura
I2C1_Init(100000); // Inicia o módulo I2C a 100Khz
I2C1_Start(); // Inicia a transmissão
I2C1_Wr(Slave_Addy + 1); // Endereço do escravo + 1 (bit de R/W )
read_data = I2C1_Rd(0); // Armazena o byte recebido na variável read_data
delay_ms(100); // delay de 100 milissegundos
I2C1_Stop(); // Finaliza a transmissão
return read_data;
}// end read
void main() { //Função principal
RBPU_bit = 0;
TRISB.RB0 = 1; // SCL como entrada
TRISB.RB1 = 1; // SDA como entrada
TRISB.RB2 = 1; // RB2 como entrada (botão 7)
PORTB.RB2 = 0; // inicia em low
TRISB.RB3 = 1; // RB3 como entrada (botão *)
PORTB.RB3 = 0; // inicia em low
TRISB.RB4 = 0; // RB4 como saída (coluna matricial)
PORTB.RB4 = 0; // inicia em low
ADCON1 = 0x0F; //Coloca todos os pinos como DIGITAIS I/O's

42
/* 1000 = I2C Master mode, clock = FOSC/(4 * (SSPADD + 1)) */
SSPM3_bit = 1;
SSPM2_bit = 0;
SSPM1_bit = 0;
SSPM0_bit = 0;

Lcd_Init(); // Inicia o LCD


Lcd_Cmd(_LCD_CLEAR); // Limpa a tela do LCD
Lcd_Cmd(_LCD_CURSOR_OFF); // Desabilita o cursor
Lcd_Out(1,1, "PRESSIONE 7 P/ ");
Lcd_Out(2,1, "INICIAR A COMUN.");

while (1){ // Loop Infinito

if (botao_7 == 0){ // Se o botão for pressionado


delay_ms(300); // anti bouncing
Lcd_Cmd(_LCD_CLEAR); // Limpa a tela do LCD
delay_ms(50);
send (contagem); // envia o valor da variável contagem
contagem = contagem++; // incrementa em 1
Lcd_Out(1,1, "TRANSMITINDO...");
Lcd_Out(2,5, "SLAVE A0");
delay_ms(100);
}// end if

elseif (botao_ast==0){
delay_ms(300); // anti bouncing
Lcd_Cmd(_LCD_CLEAR); // Limpa a tela do LCD
Cnt = read(); // lê o byte do escravo SPPBUF
delay_ms(50);
Display_Init(); // chama a função que mostra os dados no LCD
}// end else if
}//end while
}// end void main
///////////////////////////////////////////////////////////////////////////////////////////////

Algoritmo para o Escravo


///////////////////////////////////////////////////////////////////////////////////////////////

// Conexões dos pinos do LCD com os pinos do PIC


sbit LCD_RS at RE0_bit;
sbit LCD_EN at RE1_bit;
sbit LCD_D4 at RD4_bit;
sbit LCD_D5 at RD5_bit;
sbit LCD_D6 at RD6_bit;
sbit LCD_D7 at RD7_bit;
sbit LCD_RS_Direction at TRISE0_bit;
sbit LCD_EN_Direction at TRISE1_bit;
sbit LCD_D4_Direction at TRISD4_bit;
sbit LCD_D5_Direction at TRISD5_bit;
sbit LCD_D6_Direction at TRISD6_bit;
sbit LCD_D7_Direction at TRISD7_bit;

const Addy = 0xA0; // Define o endereço do Slave


unsignedshort j; // variável auxiliar
unsignedshort rxbuffer; // variável de recebimento de dados
unsignedshort tx_data; // variável de transmissão de dados

unsignedshort Cnt = 0; // Variável de contagem

43
char *digit = "000"; // declara os dígitos

void init(){ //Função de Inicialização


ADCON1 = 0x0F; //Seta todas as portas 'analógicas' como DIGITAIS
TRISB = 0xFF; //Define os pinos do modulo I2C como entrada
SSPADD = Addy; //Define o endereço do slave
SSPCON1 = 0x36; //configura I2c como slave, 7bits
PIE1.SSPIE = 1; //habilita interrupção do modulo I2c
INTCON = 0xC0; //habilita interrupção global e dos periféricos
SSPCON2.GCEN = 1;
}

void Display_init() // define display_init;


{ // Calcula os dígitos:
digit[2] = (Cnt%10) +48; // digito unidade
digit[1] = (Cnt/10)%10 + 48; // digito da dezena
digit[0] = (Cnt/100)%10 +48; // digito da centena

Lcd_Out(1,7,digit); // mostra a variável digito


Lcd_Out(2,1, "BYTES RECEBIDOS.");
}// end display_init

void interrupt(){ // Função interrupção


if (PIR1.SSPIF == 1){ // I2C Interrupt
PIR1.SSPIF = 0; // Reinicia a flag de interrupção

//Transmite dados para o Mestre


if (SSPSTAT.R_W == 1){ // Leitura
SSPBUF = tx_data; // Armazena em SSPBUF os dados para enviar...
SSPCON1.CKP = 1; // Libera o Clock
j = SSPBUF; // Lê o buffer para limpar a flag
return;
} //end if
if (SSPSTAT.BF == 0){ // checa se ainda há algo no buffer
j = SSPBUF; // esvazia o buffer
return;
} // end if

//Recebe dados do Mestre


if (SSPSTAT.D_A == 1){ // Verificação de 'dados'
rxbuffer = SSPBUF; // armazena os dados do buffer em 'rxbuffer'
j = SSPBUF; // Lê o buffer para limpar a flag [address]
return;
} //end if
} // end interrupção I2C
j = SSPBUF; // Lê o buffer para limpar a flag
} // end void interrupt

void main() { // Função Principal


init(); // chama a função init
Lcd_Init(); // Inicia o LCD
Lcd_Cmd(_LCD_CLEAR); // Limpa a tela do LCD
Lcd_Cmd(_LCD_CURSOR_OFF); // Desabilita o cursor

while(1){ // Loop infinito


Cnt = rxbuffer; // atribui o byte recebido à variável 'Cnt'
tx_data = Cnt; // atribui Cnt à tx_data
Display_init(); // Chama a função para mostrar no display
} // end while
} // end void
///////////////////////////////////////////////////////////////////////////////////////////////

44
8.3. PIC + RTC
Para tornar possível o entendimento dos próximos algoritmos, é necessário o estudo do
módulo RTC, presente no Kit ACEPIC de desenvolvimento, feito isso, nota-se que os programas a
seguir são relativamente simples, o primeiro com intuito de escrever os valores nos registradores
do RTC e o segundo com a função de ler e mostrar no LCD os valores armazenados nos
registradores do RTC, ambas ações são realizadas através do protocolo I2C.

Algoritmo para Escrita


///////////////////////////////////////////////////////////////////////////////////////////////

void main() { // Função Principal


ADCON1 = 0x0F; // Coloca todos os pinos como digitais

I2C1_Init(100000); // Inicia o I2C com a frequência de 100Khz

I2C1_Start();
I2C1_Wr(0xD0); // Endereço DS1307 + 0 = ESCRITA
I2C1_Wr(0); // Escreve no REGISTRADOR 0
I2C1_Wr(0); // Escreve 0 = (Habilita a Contagem + 0 seg)
I2C1_Stop(); // Finaliza a transmissão

// ESCRITA I2C
I2C1_Start(); // Inicia a transmissão
I2C1_Wr(0xD0); // Endereço do slave (DS1307=1101000) + 0
I2C1_Wr(0x01); // Escreve no REG 01 (MINUTOS)
I2C1_Wr(Dec2Bcd(15)); // Envia 34 = (34min)
I2C1_Stop();

I2C1_Start();
I2C1_Wr(0xD0); // Endereço do slave (DS1307=1101000) + 0
I2C1_Wr(0x02); // Escreve no REG 02 (HORAS)
I2C1_Wr(Dec2Bcd(9)); // Envia 10 = (10hrs)
I2C1_Stop();

I2C1_Start();
I2C1_Wr(0xD0); // Endereço do slave (DS1307=1101000) + 0
I2C1_Wr(0x03); // Escreve no REG 03 (DIA DA SEMANA)
I2C1_Wr(Dec2Bcd(2)); // Envia 6 = (sexta feira)
I2C1_Stop();

I2C1_Start();
I2C1_Wr(0xD0); // Endereço do slave (DS1307=1101000) + 0
I2C1_Wr(0x04); // Escreve no REG 04 (DATA)
I2C1_Wr(Dec2Bcd(21)); // Envia 22 = DIA 22
I2C1_Stop();

I2C1_Start();
I2C1_Wr(0xD0); // Endereço do slave (DS1307=1101000) + 0
I2C1_Wr(0x05); // Escreve no REG 05 (MÊS)
I2C1_Wr(Dec2Bcd(3)); // Envia 1 = Janeiro
I2C1_Stop();

I2C1_Start();
I2C1_Wr(0xD0); // Endereço do slave (DS1307=1101000) + 0
I2C1_Wr(0x06); // Escreve no REG 05 (ANO)
I2C1_Wr(Dec2Bcd(16)); // Escreve 16 = 2016
I2C1_Stop();

45
delay_ms(100); // Delay de 100 Milissegundos

} //end void main


///////////////////////////////////////////////////////////////////////////////////////////////

Algoritmo para Leitura


///////////////////////////////////////////////////////////////////////////////////////////////
//Módulo de conexões do LCD
sbit LCD_RS at RE0_bit;
sbit LCD_EN at RE1_bit;
sbit LCD_D4 at RD4_bit;
sbit LCD_D5 at RD5_bit;
sbit LCD_D6 at RD6_bit;
sbit LCD_D7 at RD7_bit;

sbit LCD_RS_Direction at TRISE0_bit;


sbit LCD_EN_Direction at TRISE1_bit;
sbit LCD_D4_Direction at TRISD4_bit;
sbit LCD_D5_Direction at TRISD5_bit;
sbit LCD_D6_Direction at TRISD6_bit;
sbit LCD_D7_Direction at TRISD7_bit;

// Variavéis de armazenamento dos valores recebidos do DS1307


unsignedshort seg,mint,hora,dia,data1,mes,ano,dia_bcd;

// Função para converter BCD para string e mostrar no LCD


void BcdToStr_LCD(unsignedshort x, unsignedshort y, unsignedshort var){
unsignedshort var1, var2;
var1 = (var >>4) + 0x30;
Lcd_Chr(x,y,var1);
var2 = (var &0x0F) + 0x30;
Lcd_Chr_CP(var2);
}// end void BcdToStr

void main() { // Função Principal


ADCON1 = 0x0F; // Coloca todos os pinos como digitais

Lcd_Init(); // Inicia o LCD


Lcd_Cmd(_LCD_CLEAR); // Limpa a tela do LCD
Lcd_Cmd(_LCD_CURSOR_OFF); // Desabilita o cursor

I2C1_Init(100000); // Inicia o I2C com a frequência de 100Khz

I2C1_Start();
I2C1_Wr(0xD0); // Endereço DS1307 + 0 = ESCRITA
I2C1_Wr(0); // Escreve no REGISTRADOR 0
I2C1_Wr(0); // Escreve 0 = Habilita a Contagem + 0 seg
I2C1_Stop(); // Finaliza a transmissão

while(1){ // Loop Infinito


//LEITURA I2C
I2C1_Start();
I2C1_Wr(11010000); // Endereço do slave (DS1307=1101000) + 0
I2C1_Wr(0x00); // Endereço da Leitura (SEG)
I2C1_Repeated_Start(); // Reinicia
I2C1_Wr(0xD1); // Endereço do slave (DS1307=1101000) + 1

//Lê os registradoes e armazena os valores nas variáveis


seg = I2C1_Rd(1); // Lê o REG 0...
mint = I2C1_Rd(1); // Lê o REG 1...
hora = I2C1_Rd(1);

46
dia_bcd = I2C1_Rd(1);
data1 = I2C1_Rd(1);
mes = I2C1_Rd(1);
ano = I2C1_Rd(0); // Lê o REG 6.
I2C1_Stop(); // Finaliza a leitura

// Etapa de Conversão e Escrita no lcd:


// Converte de BCD para STRING e mostra no LCD
BcdToStr_LCD(1,5, hora); // Mostra a Hora no LCD...
Lcd_Chr_CP(':');
BcdToStr_LCD(1,8, mint); // Minutos
Lcd_Chr_CP(':');
BcdToStr_LCD(1,11, seg); // Segundos

BcdToStr_LCD(2,2, data1); // Mostra a Data no LCD


Lcd_Chr_CP('/');
BcdToStr_LCD(2,5, mes); // Mês
Lcd_Chr_CP('/');
BcdToStr_LCD(2,8, ano); // Ano

// DIA DA SEMANA
dia = Bcd2Dec(dia_bcd); //Converte de bcd para decimal
// Escreve no LCD o dia referente ao valor da variável 'dia':
switch(dia)
{
case1: Lcd_Out(2,13,"DOM"); break;
case2: Lcd_Out(2,13,"SEG"); break;
case3: Lcd_Out(2,13,"TER"); break;
case4: Lcd_Out(2,13,"QUA"); break;
case5: Lcd_Out(2,13,"QUI"); break;
case6: Lcd_Out(2,13,"SEX"); break;
case7: Lcd_Out(2,13,"SAB"); break;
} //end switch
delay_ms(50);
} //end while
} //end void main
///////////////////////////////////////////////////////////////////////////////////////////////

8.4. Relógio Ajustável.


Após o entendimento dos programas anteriores é possível desenvolver um Relógio
ajustável, utilizando o LCD, para mostrar a hora, o RTC para realizar a contagem e os botões para o
ajuste das variáveis Horas e Minutos, que estão presentes nos registradores do RTC, todas as linhas
de instruções do algoritmo abaixo, estão comentadas para facilitar o funcionamento do mesmo.
///////////////////////////////////////////////////////////////////////////////////////////////
// Módulo de conexões do LCD com o PIC
sbit LCD_RS at RE0_bit;
sbit LCD_EN at RE1_bit;
sbit LCD_D4 at RD4_bit;
sbit LCD_D5 at RD5_bit;
sbit LCD_D6 at RD6_bit;
sbit LCD_D7 at RD7_bit;
sbit LCD_RS_Direction at TRISE0_bit;
sbit LCD_EN_Direction at TRISE1_bit;
sbit LCD_D4_Direction at TRISD4_bit;
sbit LCD_D5_Direction at TRISD5_bit;
sbit LCD_D6_Direction at TRISD6_bit;
sbit LCD_D7_Direction at TRISD7_bit;
//end módulo de conexões

47
//Define os pinos das colunas do Teclado
#define col_1 RB6_bit
#define col_2 RB7_bit
//Define os pinos das linhas
#define row_A RB2_bit
#define row_B RB3_bit
//end definições do teclado

// Variavéis de armazenamento dos valores recebidos do DS1307


unsignedshort seg,mint,hora,dia,data1,mes,ano;
unsignedchar control = 0x01; // variável de controle

// Função para converter BCD para string e mostrar no LCD


void BcdToStr_LCD(unsignedshort x, unsignedshort y, unsignedshort var){
unsignedshort var1, var2;
var1 = (var >>4) + 0x30;
Lcd_Chr(x,y,var1);
var2 = (var &0x0F) + 0x30;
Lcd_Chr_CP(var2);
}// end void BcdToStr

void LE_I2C(){
//LEITURA I2C
I2C1_Start();
I2C1_Wr(11010000); // Endereço do slave (DS1307=1101000) + 0
I2C1_Wr(0x00); // Endereço da Leitura (SEG)
I2C1_Repeated_Start(); // Reinicia
I2C1_Wr(0xD1); // Endereço do slave (DS1307=1101000) + 1

//Lê os registradores e armazena os valores nas variáveis


seg = I2C1_Rd(1); // Lê o REG 0...
mint = I2C1_Rd(1); // Lê o REG 1...
hora = I2C1_Rd(1);
dia = I2C1_Rd(1);
data1 = I2C1_Rd(1);
mes = I2C1_Rd(1);
ano = I2C1_Rd(0); // Lê o REG 6.
I2C1_Stop(); // Finaliza a leitura
}//end void le_I2C

void inc_but (unsignedshort var){


if (var==hora){
hora = Bcd2Dec(hora); // converte de BCD para decimal
hora = hora + 1; // incrementa a variável hora em 1
if(hora == 24) hora = 0;
// ESCRITA I2C
I2C1_Start(); // Inicia a transmissão
I2C1_Wr(0xD0); // Endereço do slave (DS1307=1101000) + 0
I2C1_Wr(0x02); // Escreve no REG 02 (HORAS)
I2C1_Wr(Dec2Bcd(hora)); // Envia o valor de hora inc. e convertido p/ BCD
I2C1_Stop(); // Finaliza a transmissão
} //end if
elseif (var==mint){
mint = Bcd2Dec(mint); // converte de BCD para decimal
mint = mint + 1; // DECREMENTA a variável minutos em 1
if(mint == 60) mint = 0;

// ESCRITA I2C
I2C1_Start(); // Inicia a transmissão
I2C1_Wr(0xD0); // Endereço do slave (DS1307=1101000) + 0
I2C1_Wr(0x01); // Escreve no REG 01 (MINUTOS)

48
I2C1_Wr(Dec2Bcd(mint)); // Envia o valor de Mint decrementado
I2C1_Stop();
} // end else if
}//end void inc_but

void dec_but (unsignedshort var){


if (var==hora){
hora = Bcd2Dec(hora); // converte de BCD para decimal
hora = hora - 1;
if (hora <1)hora = 23;
// ESCRITA I2C
I2C1_Start(); // Inicia a transmissão
I2C1_Wr(0xD0); // Endereço do slave (DS1307=1101000) + 0
I2C1_Wr(0x02); // Escreve no REG 02 (HORAS)
I2C1_Wr(Dec2Bcd(hora)); // Envia o valor de hora inc. e convertido p/ BCD
I2C1_Stop(); // Finaliza a transmissão
} //end if
elseif (var==mint){
mint = Bcd2Dec(mint); // converte de BCD para decimal
mint = mint - 1; // DECREMENTA a variável minutos em 1

if(mint <1) mint = 59; // Se 'mint' for 0 'mint' será 59

// ESCRITA I2C
I2C1_Start(); // Inicia a transmissão
I2C1_Wr(0xD0); // Endereço do slave (DS1307=1101000) + 0
I2C1_Wr(0x01); // Escreve no REG 01 (MINUTOS)
I2C1_Wr(Dec2Bcd(mint)); // Envia o valor de Mint decrementado
I2C1_Stop(); // Finaliza a transmissão

} // end else if
}//end void dec_but

void main(){ // Função principal


ADON_bit = 0x00; // Módulo ADC é desligado
ADCON1 = 0x0F; //Coloca todos os pinos como DIGITAIS I/O's
RBPU_bit = 0x00; //Habilita os Resistores de Pull Up

// Seta as Colunas como saída e inicia em high:


TRISB.RB6 = 0x00;
TRISB.RB7 = 0x00;
PORTB.RB6 = 0x01;
PORTB.RB7 = 0x01;

//Seta as linhas como entradas e inicia em high:


TRISB.RB2 = 0x01;
TRISB.RB3 = 0x01;
PORTB.RB2 = 0x01;
PORTB.RB3 = 0x01;

TRISC.RC0 = 0;
PORTC.RC0 = 0;
Lcd_Init(); // Inicializa LCD
Lcd_Cmd(_LCD_CLEAR); // Limpa display
Lcd_Cmd(_LCD_CURSOR_OFF); // Desliga o cursor

I2C1_init(100000); // Inicia o módulo I2C


I2C1_Start();
I2C1_Wr(0xD0); // Endereço DS1307 + 0 = ESCRITA
I2C1_Wr(0); // Escreve no REGISTRADOR 0

49
I2C1_Wr(0); // Escreve 0 = Habilita a Contagem + 0 seg
I2C1_Stop(); // Finaliza a transmissão

while(1) //Loop infinito


{
LE_I2C(); // Chama a função que lê os dados do RTC através do I2C

// Converte de BCD para STRING e mostra no LCD


BcdToStr_LCD(1,5, hora); // Mostra a Hora no LCD...
Lcd_Chr_CP(':');
BcdToStr_LCD(1,8, mint); // Minutos
Lcd_Chr_CP(':');
BcdToStr_LCD(1,11, seg); // Segundos

BcdToStr_LCD(2,2, data1); // Mostra a Data no LCD


Lcd_Chr_CP('/');
BcdToStr_LCD(2,5, mes); // Mês
Lcd_Chr_CP('/');
BcdToStr_LCD(2,8, ano); // Ano

// DIA DA SEMANA
dia = Bcd2Dec(dia); //Converte de bcd para decimal

// Escreve no LCD o dia referente ao valor da variável 'dia':


switch(dia)
{
case1: Lcd_Out(2,13,"DOM"); break;
case2: Lcd_Out(2,13,"SEG"); break;
case3: Lcd_Out(2,13,"TER"); break;
case4: Lcd_Out(2,13,"QUA"); break;
case5: Lcd_Out(2,13,"QUI"); break;
case6: Lcd_Out(2,13,"SEX"); break;
case7: Lcd_Out(2,13,"SAB"); break;
} //end switch

if(col_1 && control == 0x01) {


control = 0x02;
col_1 = 0x00; //Apenas a coluna 1 em nível baixo
col_2 = 0x01;
if(!row_A){ // INCREMENTA HORA
delay_ms(200); // delay Anti-bounce (DIG 9)
inc_but(hora);
PORTC.RC0 = 1;
}
elseif(!row_B) { // DECREMENTA HORA
delay_ms(200); // delay Anti-bounce (DIG #)
dec_but(hora);
PORTC.RC0 = 0;
}
}

elseif(col_2 && control == 0x02){


control = 0x01;
col_1 = 0x01; //Apenas a coluna 2 em nível baixo
col_2 = 0x00;
if(!row_A){ // INCREMENTA 'MINT'
delay_ms(200); // Delay Anti-bounce (DIG 'C')
inc_but(mint);
}
elseif(!row_B) { // DECREMENTA 'MINT'
delay_ms(200); // Delay Anti-bounce (DIG 'D')
dec_but(mint);

50
} //DIG 'D'
} // end if botoes
delay_ms(50); // delay de ciclo
} // end while
} // end main
///////////////////////////////////////////////////////////////////////////////////////////////

51
Considerações Finais

Apesar de ser considerada antiga, C ainda é uma das linguagens de programação mais
utilizadas no mundo pois trata-se de uma linguagem leve, porém eficiente e poderosa, devido a isso
é amplamente utilizada em Microcontroladores PIC, com intuito de trazer maior simplicidade,
rapidez e eficiência aos algoritmos.

Os Microcontroladores, por sua vez, são dispositivos extremamente populares, estando


presentes em equipamentos eletrônicos e sistemas de controle industriais e residenciais. Por se
tratar de um componente programável ebem versátil, pode ser empregado em aplicações das mais
diversas, sendo assim, extremamente útil no campo da eletrônica digital, assim como na área do
mercado de trabalho voltado à tecnologia.

E após o desenvolvimento de vários programas utilizando a linguagem C, voltada para os


Microcontroladores, a experiência obtida, foi colossal, entretanto para tal aprendizado o uso do
Laboratório de Sistemas de Micro processados foi essencial, pois os softwares e hardwares
presentes nele possibilitaram a simulação e a análise de todos os projetos desenvolvidos.

52

Você também pode gostar