Curso Online de Microcontroladores Pic 1° SEMANA
-
Upload
franklin-nascimento-viana -
Category
Documents
-
view
54 -
download
3
Transcript of Curso Online de Microcontroladores Pic 1° SEMANA
Curso Online de Microcontroladores Pic
Aluno: Kaylens Lee Jhonson Lira de Souza
O curso para microcontroladores PIC em linguagem C tem como base o compilador MikroC da Mikroelektronika (www.mikroe.com) e nosso kit de desenvolvimento ACEPIC40 (pode ser adquirido em nossa loja virtual, www.acepiccamp.com.br)....
26 janeiro - 1 fevereiro
UNIDADE 1
Introdução Recurso
O compilador MikroC Recurso
Criando um projeto Recurso
Nesta primeira parte serão explicados sobre tipos e modificadores de tipos de variáveis, variáveis, operadores e também alguns comandos básicos e bastantes utilizados na programação em linguagem C.
Introdução à linguagem C Recurso
Introdução à linguagem C - Continuação Recurso
Notação Numérica Recurso
Criaremos agora o nosso primeiro projeto, onde o código será escrito, compilado e gravado no microcontrolador.Podemos gravar o microcontrolador seguindo as instruções do arquivo "Instalação da ACEPIC 40.pdf", que se encontra no CD do curso.
Primeiro projeto Recurso
Compilando e gravando o projeto Recurso
Projeto de fixação Recurso
Responda as questões referentes ao assunto da primeira semana seguindo o link abaixoEste questionário é importante para que sua média seja formada e também é um feedback para que você avalie sua participação no cursoVocê pode acompanhar sua nota e as correções clicando no link "Nota" no menu "Administração" ao lado direito.
Questões - Semana 1 Tarefa
Linguagem C
É uma linguagem de programação de propósito geral, estruturada, imperativa, procedural de alto e baixo nível, criada em 1972 por Dennis Ritchie no AT&T Bell Labs, para desenvolver o sistema operacional UNIX (que foi originalmente escrito em Assembly). Desde então, espalhou-se por muitos outros sistemas e tornou-se uma das linguagens de programação mais usadas influenciando muitas outras linguagens, especialmente o C++, que foi
desenvolvida como uma extensão para C.
Programação de Microcontroladores em linguagem C
Atualmente, a maioria dos microcontroladores existentes nos mercado, contam com compiladores em C para o desenvolvimento de software, pois a linguagem C permite a construção de programas e aplicações muito mais complexas do que o Assembly.
O compilador C tem a capacidade de “traduzir” com alto grau de inteligência e velocidade o código em C para o código de máquina, portanto podemos dizer que a linguagem C possui grande eficiência.
Essa eficiência da linguagem C faz com que o programador preocupe-se mais com a programação em si e o compilador assume responsabilidades como localização da memória, operações matemáticas e lógicas, verificação de bancos de memórias e outros..
O Compilador MikroC
O compilador MikroC foi desenvolvido pela Mikroelektronika e é com ele que vamos desenvolver nossos projetos. Segue no CD do curso uma cópia de demonstração do compilador MikroC e este também pode ser baixado no site: www.mikroe.com.
Todos os exemplos do curso poderão ser feitos na cópia de demonstração, sendo que esta é liberada para programas com tamanho até 2K.
Abaixo está a tela inicial do compilador.
Quando da instalação do compilador, na primeira vez que abrir o programa, a tela deverá estar como a da figura acima.
A Interface do compilador
Nós temos na interface do compilador MikroC o editor de código (Code Editor). É aqui que o código do projeto será escrito:
É possível modificarmos a cor do fundo do editor de código clicando no menu
em “Tools -> Options” ou clicando na figura , localizado acima do editor de código mais à direita.
Escolha a opção “Colors” dentro da chave “Editor” e altere a máscara em “Scheme”, conforme a figura abaixo:
Do lado esquerdo ao editor de código temos o Code Explorer:
Logo abaixo está o Project Setup com as informações do microcontrolador utilizado e algumas configurações para “Debug”:
Abaixo de tudo, temos a Janela de Mensagens (Message Window). Nesta janela serão mostrados o status de compilação, e informações de erro e alertas para o nosso código:
OBS.: Outras janelas e ferramentas, veremos no decorrer do curso.
Criando um novo projeto
Iniciaremos um projeto somente para verificarmos o funcionamento do compilador MikroC.
Clique no Menu em “Project” e escola a opção “New Project”;
Aparecerá a tela abaixo para inserção dos dados do projeto:
Siga os passos abaixo:
-Em “Project Name”, dê o nome do projeto, que neste caso pode ser: Inicio;
-Em “Project Path”, escolha o caminho da pasta onde serão armazenados os arquivos do projeto;
-“Description” não é obrigatório o preenchimento;
-Em “Device”, escolha o microcontrolador a ser utilizado em nosso projeto, no caso, pode ser o P16F877A;
-Em “Clock”, deixe o default mesmo, ou seja, 008.000000 (8MHz);
-Em “Device Flags”, deixe o default também, por enquanto não faremos nenhuma alteração;
-Digite, no Editor de código, apenas o bloco principal, conforme abaixo:
void main()
{
}
-Após digitar o código acima, vamos compilar o projeto clicando no menu “Project” e escolhendo a opção “Build”:
Podemos também compilar a projeto com as teclas “Ctrl + F9”.
-Se tudo estiver correto, será mostrado o resultado da compilação na Janela de Mensagens, conforme abaixo:
Introdução à Linguagem C
Tipos de dados
Os tipos de dados suportados pela linguagem C e utilizados no compilador MikroC são identificados pela palavras reservadas*: char, int, float, double e void.
Tipo de dado Tamanho Intervalochar 8 bits 0 a 255int 16 bits 0 a 65536
float e double 32 bits 3.4 x 10-38 a 3.4 x 1038
void 0 0
O tipo char representa caracteres ASCII de 8 bits, sendo que cada variável do tipo char pode representar somente um caracter ASCII.
O tipo int representa números inteiro de 16 bits.
O tipo float e double representam números fracionários de 32 bits, sendo que este tipo de dado deve ser evitado tendo em vista o seu tamanho.
O tipo void é normalmente utilizado em funções para declarar que ela não deve retornar nenhum valor.
*palavra reservada = palavra utilizada pelo compilador e que não deve ser utilizada pelo usuário.
Modificadores de Tipo
Além dos tipos de dados acima, podemos utilizar comandos especiais para modificá-los e são chamados de modificadores de tipo, sendo eles: signed, unsigned, short e long.
O modificador de tipo signed pode ser utilizado para representar um número positivo ou negativo, assim um tipo de dados signed int pode representar valores de -32768 a 32767.
O modificador unsigned define um tipo de dado sem sinal, ou seja, somente a parte positiva de uma variável, então o tipo de dados unsigned int representa valores de 0 à 65536.
O modificador short é utilizada para definir um valor menor do que o tipo modificado, ou seja, ao declaramos uma variável com o tipo short, a variável tem seu tamanho reduzido para 8 bits.
O modificador long é o contrário, ou seja, amplia o tamanho de uma variável, então uma variável declarada com o tipo de dado long int passa a ter o tamanho de 32 bits.
Temos abaixo, uma tabela com todos os tipos de dados e seus modificadores disponíveis para o compilador MikroC:
Tipo Tamanho Valores
signed char 8 bits -128 à 127
(unsigned) char 8 bits 0 à 255
(signed) short (int) 8 bits -128 à 127
unsigned short (int) 8 bits 0 à 255
(signed) int 16 bits -32768 à 32767
unsigned int 16 bits 0 à 65535
(signed) long (int) 32 bits -2147483648 à 2147483647
unsigned long (int) 32 bits 0 à 4294967295
float 32 bits (+-) 1,17549435082e-38 à (+-) 6,80564774407e38
double 32 bits (+-) 1,17549435082e-38 à (+-) 6,80564774407e38
long double 32 bits (+-) 1,17549435082e-38 à (+-) 6,80564774407e38
void 0 0
* Palavras entre parênteses são opcionais.
Variáveis
As variáveis são uma representação simbólica onde são armazenados dados do programa ou dados externos como uma tecla pressionada ou uma tensão lida, etc. As variáveis podem conter letras e números, sempre começando com
letras e não devem ter nome de palavras reservadas pelo compilador como, por exemplo, for, do, int, etc.
Declaração de Variáveis
Declarar uma variável é simplesmente informar ao compilador que uma variável chamada “X” é do tipo “Y” e é declarada da seguinte forma:
<tipo> + <nome da variável>;
Podemos também declarar e inicializar uma variável da seguinte forma:
<tipo> + <nome da variável> = <valor da variável>;
Exemplos: unsigned int x = 12345;
int conta;
short x1;
Variáveis Globais -> São declaradas no início de nosso código e que podem ser acessadas em qualquer ponto do programa:
Ex.:
int conta;
unsigned int c;
void main()
{
conta = 10;
c = c + 1;
while (1);
}
Variáveis Locais -> São declaradas dentro de uma função e somente existe durante a execução da função. Elas são descartadas depois de executada a função:
void main()
{
int conta
conta = conta++;
while(1);
}
Operadores
Temos, na linguagem C, vários operadores e podemos verificá-los abaixo:
Operadores de Atribuição
São utilizados para atribuir valores às variáveis:
Operador Descrição Exemplo= Associa um valor à variável a = 2
Aritméticos:
Operador Descrição Exemplo
+ Soma dos argumentos a + b
- Subtração dos argumentos a - b
* Multiplicação dos argumentos a * b
/ Divisão dos argumentos a / b
% Resto da divisão (só pode ser utilizado com valores inteiros)
a % b
++ Soma 1 ao argumento a++
-- Subtrai 1 ao argumento a--
Relacionais:
São utilizados para comparação entre argumentos e retornam uma resposta verdadeira ou falsa. Como em linguagem C não existe uma variável booleana para um resultado verdadeiro ou falso, todo valor igual a 0 será considerado falso e todo valor diferente de 0 (qualquer valor) será considerado verdadeiro.
Operador Descrição Exemplo
== Compara se igual a a == 5
!= Compara se diferente de a != 5
> Compara se maior que a > 5
< Compara se menor que a < 5
>= Compara se maior ou igual a a >= 5
<= Compara se menor ou igual a a <= 5
Operadores lógicos bit-a-bit:
Operador Descrição
& E (AND)
| OU (OR)
^ OU EXCLUSIVO (XOR)
~ Complemento (NOT)
>> Deslocamento à direita
<< Deslocamento à esquerda
Operadores lógicos relacionais:
Operador Descrição
&& Comparação lógica E (AND)
|| Comparação lógica OU (OR)
! Comparação lógica Complemento (NOT)
Introdução à Linguagem C
Declarações de controle:
Comando if (se):
O comando IF é uma comando de decisão e é utilizado para avaliar uma determinada condição e determinar se ela é verdadeira, caso seja, executa o bloco contido dentro desta condição. Sua forma geral é:
if (exp) comando;
Se o resultado da condição referente a expressão (exp) for verdadeiro, o comando será executado, caso contrário, o programa segue sem executar o comando.
if (exp)
{
comando1;
comando2;
}
Para o caso acima, a mesma explicação anterior se encaixa, sendo que agora, se a condição da expressão for verdadeira, serão executados comando1 e comando2.
Exemplos:
if (conta>50) conta = 0;
Neste caso, se a variável conta atingir um valor maior que 50, o comando conta = 0 será executado e a variável conta será zerada.
if (conta>50)
{
conta = 0;
conta1++;
}
Neste caso, se a variável conta atingir um valor maior que 50, os comandos conta = 0 e conta1++ serão executados e assim a variável conta será zerada e a variável conta1 será incrementada em 1, respectivamente.
Comando IF-ELSE:
Neste caso, a condição IF é utilizada da mesma forma anterior, sendo que agora, se a condição da expressão for falsa a condição ELSE será executada, ou seja, neste caso existe a possibilidade de escolha de uma entre duas opções. Sua forma é:
if (exp) comando1;
else comando2;
Caso a expressão seja verdadeira, o comando1 será executado, caso seja falsa, o comando2 será executado.
Exemplo:
if (conta>0)
{
conta = 0;
conta1++;
}
else conta++;
Para o exemplo, se o valor da variável conta for maior que 0, então os comandos conta = 0 e conta1++ serão executados, porém caso o valor da variável conta seja 0 ou menor que 0, então o comando conta++ será executado.
Comando SWITCH-CASE:
Quando existem muitos valores para testar de uma só variável, o comando IF pode ficar meio confuso ou sem muita eficiência, para isso podemos utilizar o SWITCH-CASE. Segue sua forma:
switch(variável)
{
case valor1: comando1;
....
break;
case valor2: comando2;
....
break;
....
....
default: comandoN;
....
....
}
Neste caso, a variável será testada e se o valor dela for igual a valor1, o comando1 será executado, se for igual ao valor2, o comando2 será executado e assim por diante, agora se o valor for diferente de qualquer caso (case), o comandoN será executado.
Exemplo:
switch(conta)
{
case 10 : conta1++;
break;
case 15: conta2++;
break;
case 20: {
conta1++;
conta2++;
}
break;
default: conta3++;
}
Neste caso, se o valor da variável conta for igual a 10, a variável conta1 será incrementado, se o valor da variável conta for igual a 15, o valor da variável conta2 será incrementado, caso o valor de conta seja igual a 20, tanto os valores de conta1 quanto de conta2 serão incrementados, para todos outros valores diferentes para a variável conta, o valor de conta3 será incrementado.
Note que após cada comando, temos a cláusula break, cuja função é encerrar o teste da variável tendo em vista já ter sido satisfeita a condição, assim, por exemplo:
switch(conta)
{
case 10 : conta1++;
break;
.
.
.
Se a variável conta tem seu valor igual a 10, o comando de incremento da variável conta1 será executado, ou seja, a condição já foi atendida e não é preciso testar mais vezes a variável conta. Então, a cláusula break, encerra os teste feitos por case e, assim, o programa continua na próxima instrução após a estrutura switch.
Laço FOR:
Este é um dos comandos de laço (loop ou repetição) disponíveis na linguagem C, a sua forma é:
for(inicialização;condição(término);incremento) comando:
ou,
for(inicialização;condição(término);incremento)
{
comando1;
comando2;
}
onde:
inicialização: essa seção conterá uma inicialização para a variável;
condição: responsável por contar a condição de finalização do laço;
incremento: aqui pode conter uma ou mais variáveis para incremento da variável.
Exemplo:
int conta;
int a = 0;
for (conta=0;conta<10;conta++) a = conta;
Neste exemplo, a variável conta será iniciada com o valor 0, a expressão a = conta será executada e após isso a variável conta será incrementada novamente. Essa repetição ou laço se encerrará quando a condição conta < 10 for satisfeita, ou seja quando a variável conta for igual a 9.
Laço WHILE:
Neste laço, os comandos serão repetidos enquanto a expressão for verdadeira, sua forma é:
while (exp)
{
comando;
}
Exemplo:
int x;
x = 0;
while(x<10) x++;
A programa ficará no laço de repetição whil, enquanto a variável x for menor que 10 e o programa só continuará quando o valor de x for maior ou igual a 10.
Laço DO-WHILE:
Este laço é uma variação do comando WHILE, sendo que neste caso o comando será executado antes de testar se a condição é verdadeira. Sua forma é:
do
{
comando;
}
while(exp);
O comando será executado pelo menos uma vez antes de verificar a condição da expressão.
Exemplo:
int x;
int y;
do
{
x++;
} while(y!=1);
Introdução à Linguagem C
Notação numérica
Em linguagem C, podemos usar as 4 formas de representação numérica, decimal, binária, hexadecimal e octal, sendo as mais comuns somente as 3 primeiras.
Notação decimal: a representação desta notação é direta, ou seja, como estamos acostumados a escrever:
Ex.: PORTB = 10;
Notação binária: esta representação vem precedida de “0b” ou “0B”, indicando a notação:
Ex.: PORTB = 0b00000010;
ou
PORTB = 0B00000010;
Notação Hexadecimal: esta representação vem precedida de “0x” ou “0X”, ex:
Ex.: PORTB = 0x0A;
ou
PORTB = 0X0A;
Primeiro Projeto
Primeiramente, sugiro que se abra uma pasta no seu HD com o nome CURSO_PIC, para armazenarmos os nossos projetos criados durante o curso. Dentro da pasta CURSO_PIC, podemos criar outras pastas referentes ao projetos.
Vamos abrir um novo projeto, conforme explicado anteriormente, com as definições abaixo
Definindo o projeto:
-Project Name : PiscaLed
-Project Path : C:\CURSO_PIC\PISCA LED\
-Description : ....
-Device : P16F877A
-Clock : 008.000000
-Device Flags : Clique no botão Default no lado direito dos “Device Flags”
Os bits de configuração (fuses) podem ser configurados, selecionando ou não os mesmos em “Device Flags” e quando utilizamos a opção “Default” os fuses serão configurados da seguinte forma:
_LVP_OFF;
_WDT_OFF
_HS_OSC
Circuito:
O circuito que utilizaremos neste primeiro exemplo está logo abaixo:
Escrevendo o código:
Em linguagem C, os programas podem ter uma ou mais funções, isso faz com que tenhamos uma estrutura modular, ou seja, tenhamos blocos que podem ser acessados em qualquer parte do programa facilitando a visualização e o entendimento do programa.
Todo programa C tem uma função principal (main) e a partir dela nosso programa será inicializado.
Estrutura básica de um programa em C utilizando o compilador mikroC:
void main()
{ //Inicia a função.
//Este é o bloco principal do programa e é o único que a linguagem C precisa para funcionar.
} //Finaliza a função.
Este, como vimos anteriormente, é o menor programa escrito em linguagem C no compilador MikroC. Note a função principal void main() e logo após, abrimos a inicialização da função com uma chave, logo após vem o corpo do programa (onde ele será escrito) e em seguida fechamos o programa com outra chave.
Note também as duas barras (//) antes das explicações. Essas barras iniciam um comentário e tudo que vier após estas barras não será ‘entendido’ pelo programa, ou seja, não será compilado.
Podemos também fazer comentários de outra maneira, veja a seguir:
/*Temos aqui uma outra forma de comentar num programa. Esta maneira é indicada quando precisamos escrever algum comentário grande como este ou maior*/
Neste caso, o comentário tem seu início com ‘/*’ e é finalizado com ‘*/’.
Código do Projeto:
void main()
{ //Inicia a função main (principal)
PORTD = 0;
TRISD = 0; //Configura todos os pinos da porta D como saída.
while(1) //Começo do loop (infinito)
{
PORTD = ~PORTD; //Troca situação dos pinos na porta D.
delay_ms(1000); //Tempo de atraso de 1 segundo.
}
} //Finaliza a função main
Compilando o Projeto
Depois de escrevermos o código, devemos compilar o projeto para que possamos gravar no microcontrolador. A compilação se dá pressionando-se “ctrl + F9” ou clicando no menu Project e escolher a opção Build, conforme abaixo.
Estando o projeto sem erros, as informações abaixo devem ser mostradas na parte de baixo do programa.
Gravando o código
Utilizaremos, para gravar o programa no microcontrolador, o programa WinPic800 como segue a figura abaixo:
Siga os procedimentos de instalação e gravação do microcontrolador no WinPic800 conforme manual “Instalação da ACEPIC 40.pdf” que segue no CD do curso.
Projeto de Fixação
Projetos de fixação:
-Este projeto faz piscar o led conectado ao pino 0 da porta D
void main()
{
TRISD.F0 = 0; //Configura o pino 0 da porta D como saída.
while (1)
{
PORTD.F0 = 0; //Coloca pino 0 da porta D em nível lógico 0
delay_ms(500); //Tempo de atraso de 500ms
PORTD.F0 = 1; //Coloca pino 1 da porta D em nível lógico 1
delay_ms(500); //Tempo de atraso de 500ms
}
}
obs.: Os registradores especiais, TRISD, PORTD, PORTA, TRISA, INTCON, TMR0 e outros são tratados, pelo compilador, como variáveis, portanto, podemos atribuir valor e tratá-los exatamente como uma variável qualquer.
UNIDADE 2
Nesta segunda semana, veremos como fazer a leitura de uma tecla pressionada através da leitura direta do bit e da função do próprio compilador, trataremos do acionamento de bits individualmente e também das funções e protótipos de funções da linguagem C. Verificaremos também a utilização do Timer 0 como contador e temporizador, assim como faremos alguns projetos para entendimento dos assuntos da semana.
Leitura das teclas Recurso
Controle individual de bits Recurso
Função Button Recurso
Funções em Linguagem C Recurso
Abaixo veremos a utilização do timer 0 como contador e temporizador.Seguem também exemplos de utilização e nas póximas aulas veremos os outros dois timers 1 e 2 para o PIC 16F877A.
Contador/Temporizador Timer 0 Recurso
Cálculo / Contando um tempo de 1 segundo Recurso
Timer 0 com sinal externo Recurso
Responda as questões referentes aos assuntos da segunda semana seguindo o link abaixoEste questionário é importante para que sua média seja formada e também é um feedback para que você avalie sua participação no cursoVocê pode acompanhar sua nota e as correções clicando no link "Nota" no menu "Administração" ao lado direito.
Questões - Semana 2 Tarefa
Acionamento de Botões Projeto -Fazer um programa que, se o usuário pressionar um dos botões “1”,
“4”, “7” e “*”, um dos Led’s D1, D2, D3 e D4,respectivamente, deverá acender.
Definindo o projeto: -Project Name : Botao -Project Path : \ CURSO_PIC\Botoes\ -Description : .... -Device : P16F877A -Clock : 008.000000 -Device Flags: Default
Escrevendo o programa Note que os botões tem suas ligações em toda PORTA B e que os
botões “1”, “2”, “3” e “A” tem uma de sua conexões no PINO RB0, sendo assim, quando pressionarmos um desses botões, o LED D1 acenderá e se pressionarmos um dos botões “4”, “5”, “6” ou “B”, o LED D2 acenderá e assim também para o restante dos botões.
Como sabemos, os microcontroladores PIC tem resistores de PULL UP internos conectados na porta B (verificar Datasheet) e para o nosso programa deveremos habilitar estes resistores. A habilitação dos resistores de PULL UP se dá através do registrador OPTION_REG, através do Bit 7 (RBPU) e este deve estar em 0 para que os resistores sejam habilitados.
Ligação dos resistores de Pull Up interna
void main() { OPTION_REG.F7 = 0; //Habilita resistores de PULL UP da porta B TRISB = 0X0F; //Configura os 4 bits menos significativos
como entrada //... e os 4 bits mais significativos como saída
para a porta B TRISD = 0X00; //Configura toda a porta D como saída PORTB = 0X00; //Limpa porta B PORTD = 0X00; //Limpa portaD -> apaga os Led’s while(1) { if (PORTB.F0 == 0) //Se o bit 0 da porta B for igual a zero PORTD.F1 = 1; //Aciona o bit 1 da porta D -> acende o LED 1 if (PORTB.F1 == 0) //Se o bit 1 da porta B for igual a zero PORTD.F2 = 1; //Aciona o bit 2 da porta D -> acende o LED 2 if (PORTB.F2 == 0) //Se o bit 2 da porta B for igual a zero PORTD.F2 = 1; //Aciona o bit 2 da porta D -> acende o LED 3 if (PORTB.F3 == 0) //Se o bit 3 da porta B for igual a zero PORTD.F3 = 1; //Aciona o bit 3 da porta D -> acende o LED 4 } }
Controle de Bits de registradores - Acionamento de um único bit
No nosso programa, precisamos acionar somente o bit 7 (RBPU) do registrador OPTION_REG, então utilizamos a seguinte forma:
OPTION_REG.F7 = 0; Isso faz com que somente o bit 7 do registrador OPTION_REG seja
afetado, ou seja, somente ele passará a ter o valor 0 e o restante dos bits permanecem em seu estado anterior que, neste caso, estão em “default”. Podemos também atribuir das seguintes maneiras: OPTION_REG = 0, OPTION_REG = 0x00 ou ainda OPTION_REG = 0b00000000.
Utilizamos este modo de acionamento também para verificação dos botões e para o acionamento dos LED’s, conforme abaixo:
if (PORTB.F0 == 0) Nesta condição, verificaremos somente se o bit 0 da porta B está em
nível lógico 0. Logo após temos: PORTD.F0 = 1; Com este comando, somente o LED conectado ao pino RD0, ou seja,
bit 0 da porta D será acionado. Podemos também atribuir das seguintes
maneiras: PORTD = 1, PORTD = 0x01 ou ainda, PORTD = 0b00000001.
Este modo de atribuição de bits é interessante quando necessitamos modificar somente um bit sem nos preocupar com os outros. Por exemplo: na porta, os LED’s 1, 2, 3 e 4, estão acionados, portanto PORTD = 15 ou PORTD = 0x0F ou ainda PORTD = 0b00001111. Queremos acionar o LED 8 que está conectado no pino RD7 e mantermos os outros como estão... então podemos simplesmente atribuir PORTD.F7 = 1.
Como foi dito anteriormente, todos os registradores são tratados como variáveis e tem seu tipo como unsigned short, ou seja, têm tamanho de 8 bits. Sendo assim, podemos acionar os bits da mesma maneira para outras variáveis do tipo unsigned short.
Ex.1: unsigned short cmd = 0; //Aqui declaramos a variável cmd e
atribuímos a ela o valor zero. cmd.F0 = 1; //Aqui, somente o bit 0 desta variável terá seu valor em 1,
então a variável passa a ter //...o valor de 1, ou seja, cmd = 1. Podemos também
atribuir assim: cmd = 0b00000001; Ex.2: unsigned short cmd = 0; //Aqui declaramos a variável cmd e
atribuímos a ela o valor zero. cmd.F1 = 1 //Aqui, somente o bit1 desta variável terá seu valor em 1,
então a variável passa a ter //...o valor de 2, ou seja, cmd = 2. Podemos também
atribuir assim: cmd = 0b00000010;
Função Button O compilador MikroC disponibiliza uma biblioteca para verificação do
acionamento de botões e esta pode ser acessada através do comando: Button(&PORTx, pino, tempo, estado de ativação) Onde: &PORTx = Porta onde está conectado o botão, Exemplo: PORTB; Pino = Pino da porta onde está conectado o botão, exemplo pino 0 da
porta B; Tempo = Período de debounce em milisegundos; Estado de ativação = Pode ser 0 ou 1 e determina se o botão é
acionado pelo nível lógico 1 ou nível lógico 0. Sintaxe da função:
unsigned short Button(unsigned short *port, unsigned short pin, unsigned short time, unsigned short active_state);
Exemplo de uso: if (Button(&PORTB,0,20,1)); //verifica se o pino 0 da porta B está em
nível lógico 1, sendo que //...estando em 1, faz um delay de 20ms
(debounce)
Exemplo utilizando a biblioteca Button no projeto no projeto Acionamento de Botões.
-Project Name : Button_test -Project Path : \ CURSO_PIC\Botoes\ -Description : .... -Device : P16F877A -Clock : 008.000000 -Device Flags: Default void main() { OPTION_REG.F7 = 0; //Habilita resistores de PULL UP da
porta B TRISB = 0X0F; //Configura os 4 bits menos
significativos como entrada //... e os 4 bits mais significativos
como saída para a porta B TRISD = 0X00; //Configura toda a porta D como
saída PORTB = 0X00; //Limpa porta B PORTD = 0X00; //Limpa portaD -> apaga os Led’s while(1) { if (Button(&PORTB,0,20,0) //Se o bit 0 da porta B for igual a
zero PORTD.F1 = 1; //Aciona o bit 1 da porta D -> acende
o LED 1 if (Button(&PORTB,1,20,0) //Se o bit 1 da porta B for igual a
zero PORTD.F2 = 1; //Aciona o bit 2 da porta D -> acende
o LED 2 if (Button(&PORTB,2,20,0) //Se o bit 2 da porta B for igual a
zero PORTD.F2 = 1; //Aciona o bit 2 da porta D -> acende
o LED 3 if (Button(&PORTB,3,20,0) //Se o bit 3 da porta B for igual a
zero PORTD.F3 = 1; //Aciona o bit 3 da porta D -> acende
o LED 4 } } Note que no nosso caso, quando o botão for acionado, ele levará o pino
de entrada para o nível lógico 0, portanto, na biblioteca Button, o estado de ativação do botão será 0.
Obs.: Podemos atibuir um outro nome a uma constante ou a um registrador através da diretiva define conforme segue abaixo:
Definições do Projeto acima: -Project Name : Button_def
-Project Path : \ CURSO_PIC\Botoes\ -Description : .... -Device : P16F877A -Clock : 008.000000 -Device Flags: Default #define LED1 PORTD.F0 //Atribui o nome LED1 ao bit 0 da porta D #define LED2 PORTD.F1 //Atribui o nome LED2 ao bit 1 da porta D #define LED3 PORTD.F3 //Atribui o nome LED3 ao bit 2 da porta D #define LED4 PORTD.F4 //Atribui o nome LED4 ao bit 3 da porta D void main() { OPTION_REG.F7 = 0; //Habilita resistores de PULL UP da porta B TRISB = 0X0F; //Configura os 4 bits menos significativos
como entrada //... e os 4 bits mais significativos como
saída para a porta B TRISD = 0X00; //Configura toda a porta D como saída PORTB = 0X00; //Limpa porta B PORTD = 0X00; //Limpa portaD -> apaga os Led’s while(1) { if (Button(&PORTB,0,20,0) //Se o bit 0 da porta B for igual a zero LED1 = 1; //Aciona o bit 1 da porta D -> acende
o LED 1 if (Button(&PORTB,1,20,0) //Se o bit 1 da porta B for igual a
zero LED2 = 1; //Aciona o bit 2 da porta D ->
acende o LED 2 if (Button(&PORTB,2,20,0) //Se o bit 2 da porta B for igual a
zero LED3 = 1; //Aciona o bit 2 da porta D ->
acende o LED 3 if (Button(&PORTB,3,20,0) //Se o bit 3 da porta B for igual a
zero LED4 = 1; //Aciona o bit 3 da porta D ->
acende o LED 4 } }
Funções As funções em linguagem C são muito parecidas com as sub-rotinas da
linguagem assembly. O seu uso permite executar uma sequência de comandos sempre que necessitarmos, sem a necessidade de repetí-los.
O formato geral de uma função é: {tipo} nome_da_função ({parâmetros}) { Comando_1; Comando_2; ....
} Onde: tipo: Especifica o tipo de dado que a função retornará para onde ela foi
chamada. nome_da_função: Identifica a função, ou seja, como ela será chamada
pelo programa. Este nome não pode ter o mesmo nome utilizado por funções próprias do MikroC.
parâmetros: Utilizados para enviar valores para a função de modo que estes sejam utilizados pela função para cálculos, atribuições, controle, etc.
Esses valores consistem em tipos de variáveis separados por vírgulas e são opcionais, portanto, podemos escrever funções sem qualquer parâmetro.
Mesmo sem a existência dos valores, os parênteses devem ser utilizados.
Exemplo:
short soma (short a, short b)
{
return a + b;
}
void main ()
{
short c;
c = soma (2,3);
while(1);
}
Neste programa, definimos uma função soma que retornará o resultado
da operação dos parâmetros “a” + o parâmetro “b”. Note que o a função é do tipo short, isso então especifica que a função retornará um valor do tipo short de 8 bits.
No corpo da função, encontramos somente o código return a + b, onde o comando “return” é utilizado para retornar o valor da operação dos parâmetros “a + b”.
No bloco principal, encontramos a chamada da função através de c = soma (2,3), onde a variável “c” receberá o valor do retorno da função (note que a variável também foi declarada com o mesmo tipo da
função) e os parâmetros são os valores “2” e “3”, separados pela vírgula e dentro dos parênteses.
Então neste caso, a função soma recebe os valores “2” e “3” e estes serão atribuídos às variáveis “a” e “b”, respectivamente e a função retornará o valor da soma entre eles, portanto o valor “5” que será repassado para a variável “c” no corpo principal do programa.
Protótipo de função Um programa em linguagem C pode ficar muito grande e muito complexo e, às
vezes, uma função pode ser chamada antes desta ter sido definida. Neste caso, o compilador gera um erro, veja o exemplo abaixo:
void main ()
{
short c;
c = soma (2,3);
while(1);
}
short soma (short a, short b)
{
return a + b;
}
Veja que a função soma está definida após a função principal (main).
Neste caso, como a função soma foi definida após a sua chamada, o compilador retornará um erro dizendo que o identificador soma não foi declarado, veja a mensagens de erro na próxima figura:
Podemos solucionar este tipo de problema se declararmos previamente a função, ou seja, informaremos ao compilador que existe uma função
com aquele “nome” que estamos “chamando”. Isto é conhecido como prototipagem de função.
O protótipo de função deve ser declarado obedecendo os mesmos tipos e parâmetros da função seguido do ponto e vírgula (;) e, normalmente, são declarados logo no início do programa.
Exemplo:
short soma(short a, short b);
void main ()
{
short c;
c = soma (2,3);
while(1);
}
short soma (short a, short b)
{
return a + b;
}
Contador/Temporizador Timer 0 O Timer 0 é um contador/ temporizado binário e pode ser utilizado para
contagem de eventos externos por meio do pino RA4/T0CKI ou como temporização quando a entrada do clock é tida do clock interno.
O Timer 0 possui um prescaler de até 256 e tem 8 bits, ou seja, se configurarmos o prescaler para 1, então haverá um overflow (estouro da contagem) a cada 256 contagens (0 à 255). Para a configuração do Timer 0, utilizam-se os registros especiais OPTION_REG e INTCON.
Registrador OPTION_REG – Endereço: 81h E 181hBit Bit 7 Bit 6 Bit 5 Bit 4 Bit 3 Bit 2 Bit 1 Bit 0
Nome /RBPU INTEDG T0CS T0SE PSA PS2 PS1 PS0
Obs.: Somente os pinos marcados com fundo cinza serão utilizados para a configuração do Timer 0.
Configuração dos bits de controle do Timer 0: T0CS = 1: Incremento a cada transição no pino RA4/T0CKI. T0CS = 0: Incremento a cada ciclo de máquina (Fosc/4). T0SE = 1: Incremento na borda de descida. T0SE = 0: Incremento na borda de subida. PSA = 1: Prescale aplicado ao WDT. PSA = 0: Prescale aplicado ao TMR0. PS2, PS1 e PS0 - Configuração do Prescale.
PS2 PS1 PS0 TMR0 WDT0 0 0 1:2 1:10 0 1 1:4 1:20 1 0 1:8 1:40 1 1 1:16 1:81 0 0 1:32 1:161 0 1 1:64 1:321 1 0 1:128 1:641 1 1 1:256 1:128
Interrupção do Timer 0 Para se utilizar a interrupção do Timer 0 (TMR0), devemos setar
(colocar em nível lógico 1) os bits do registrador INTCON, conforme a tabela abaixo.
O Registrador INTCON é o responsável pelo controle das interrupções no PIC.
Registrador INTCON – Endereço: 0Bh, 8Bh, 10Bh E 18BhBit Bit 7 Bit 6 Bit 5 Bit 4 Bit 3 Bit 2 Bit 1 Bit 0
Nome GIE PEIE T0IE INTE RBIE T0IF INTF RBIF
GIE = 1: Habilita a interrupção global. GIE = 0: Desabilita a interrupção global. T0IE = 1: Habilita a interrupção do TIMER 0. T0IE = 0: Desabilita a interrupção do TIMER 0. T0IF – Flag de sinalização da ocorrência de overflow no TIMER0, este
Flag deverá ser limpo (levado à 0) quando do tratamento da interrupção.
T0IF = 1: Houve overflow no Timer 0. T0IF = 0: Não houve overflow no Timer 0.
Trataremos da interrupção do Timer 0 mais adiante no curso.
Contanto um tempo com o Timer 0 Primeiramente, precisamos entender a função do prescale para que
possamos iniciar a contagem de um tempo com o Timer 0. O Prescale é um divisor programável utilizado para reduzir a freqüência
do clock no Timer 0, então, como dito antes, se programarmos o prescale para 1:1, estamos dizendo que o Timer 0 terá seu valor incrementado a cada ciclo de máquina, ou seja, se tivermos um ciclo de máquina de 1us, o Timer 0 terá um incremento a cada 1us. Programando o prescale para 1:2, o incremento do Timer 0 se dará a cada 2 ciclos de máquina, ou seja a cada 2us e assim por diante até o prescale de 1:256, onde o Timer 0 incrementará somente com 256 ciclos de máquina, em 256us.
O valor da contagem do Timer 0 é acessado através do registrador TMR0 e, como dito anteriormente, este registrador tem 8 bits, então ele pode contar de 0 à 255, portanto, mais uma contagem (256), ele retorna para 0, fazendo com que o bit TOIF (bit 2 do registrador INTCON) seja ativado sinalizando o trasbordo da contagem (overflow).
Contanto um tempo de 100us Primeiramente verifica-se que este tempo se encaixa na divisão do
prescaler 1:1, onde o tempo total com este prescaler é de 256us, então o valor que precisamos para iniciar o Timer 0 será:
256 – 100 = 156. O Valor 156 deverá ser escrito no registrador TMR0 e devemos
selecionar o prescale de 1:1 ativando a contagem pelo WatchDog. Para o exemplo utilizaremos o circuito abaixo:
Definições do Projeto: -Project Name : Timer0 -Project Path : \ CURSO_PIC\Timer0\ -Description : .... -Device : P16F877A -Clock : 008.000000 -Device Flags: Default Façamos, então um programa onde a contagem do tempo será de
100us: void main() { TRISD = 0; //Direciona toda a porta D como saída PORTD = 0; //Limpa a porta D apagando os LED's OPTION_REG = 0X80; /*Configura o Registrador OPTION_REG RBPU = 1 -> PULL UP's porta B
desligados INTEDG = 0 Interrupção na borda de
descida do sinal ...no pino RB0 (não utilizado neste
exemplo). T0CS = 0 -> Incremento pelo ciclo de
máquina ...0,5us para o cristal de 8MHz. T0SE = 0 -> Incremento na orda de
subida. PSA = 0 -> Prescale aplicado ao Timer 0
PS2, PS1 e PS0 = 0 -> Prescale = 1:2*/ TMR0 = 156; //Valor Inicial para o timer 0 while(1) { if (INTCON.T0IF == 1) //Se ocorrer um overflow no timer 0 { INTCON.T0IF = 0; //Limpa o Flag sinalizador de overflow TMR0 = 156; //Retoma o valor inicial para o Timer 0 PORTD = ~PORTD; //Inverte os Bits da porta D, acendendo
ou apagando //...os LED's } } } Ao gravarmos este programa no microcontrolador, verificaremos que os
LED’s permanecerão todos acesos, mas na realidade, todos estão piscando numa freqüência muito alta 10KHz (100us) para o olho humano e que para verificarmos o acionamento ou não dos LED’s será necessários verificarmos com um osciloscópio conectado a qualquer um dos pinos da porta D.
Calculando o tempo Total com o Timer 0 Podemos calcular o tempo total para a contagem do Timer 0 seguindo a
seguinte fórmula: Tempo = 256 x prescaler x ( 1 / (FOSC / 4))
Então, se tivermos um cristal de 8MHz, teremos então em ( 1 / (FOSC /
4)) = 0,5 us e se formos utilizar o prescaler de 1:2 aplicado ao TMR0, teremos:
Tempo = 256 x 2 x 0,5u; Tempo = 256 us.
Para um prescaler de 256, teremos:
Tempo = 256 x 256 x 0,5u; Tempo = 32768 us ou 32,768ms
Contando um tempo de 1 segundo Como sabemos, 1 segundo é o equivalente a 1000ms e o tempo
máximo que conseguimos chegar com o Timer 0 é de 32,768ms. Sendo assim, precisamos então dividir estes 1000ms para que possamos adequar à realidade do nosso PIC.
Bem, a divisão de 1000ms por 32,768ms resulta em 30,5175.... Este resultado, por ser um valor “quebrado” não nos ajudará muito, então vamos dividir o tempo de 1000ms por 25ms. O resultado agora será 40. ...Guardaremos este resultado para ser usado posteriormente.
Então, precisamos que o overflow do Timer 0 ocorra a cada 25ms... Verificamos também que este tempo se “encaixa” no prescaler 1:256. Basta-nos encontrar o valor de início para o Timer 0 a fim de que o overflow ocorra conforme a nossa necessidade.
Podemos lançar mão da fórmula abaixo para definirmos este valor de início para o Timer 0:
Atribuindo os valores na fórmula temos:
TMR0 = 256 – (25 ms / (0,5us x 256)) TMR0 = 60,69
O resultado é um valor fracionário, mas podemos aproximá-lo para 61.
Então o valor a ser atribuído ao registrador TMR0 a fim de iniciá-lo será 61.
De posse dos dados acima, podemos iniciar a escrita do código para programação do microcontrolador. O circuito será o mesmo utilizado no exemplo anterior.
Definições do Projeto: -Project Name : Timer0_1s -Project Path : \ CURSO_PIC\Timer0_1s\ -Description : .... -Device : P16F877A -Clock : 008.000000 -Device Flags: Default void main() { short conta = 0; //Variável de controle para a contagem TRISD = 0; //Direciona toda a porta D como saída PORTD = 0; //Limpa a porta D apagando os LED's OPTION_REG = 0X87; /*Configura o Registrador OPTION_REG RBPU = 1 -> PULL UP's porta B
desligados INTEDG = 0 Interrupção na borda de
descida do sinal ...no pino RB0 (não utilizado neste
exemplo). T0CS = 0 -> Incremento pelo ciclo de
máquina ...0,5us para o cristal de 8MHz. T0SE = 0 -> Incremento na orda de
subida.
PSA = 0 -> Prescale aplicado ao Timer 0 PS2, PS1 e PS0 = 1 -> Prescale = 1:256*/ TMR0 = 61; //Valor Inicial para o timer 0 while(1) { if (INTCON.T0IF == 1) //Se ocorrer um overflow no timer 0 { INTCON.T0IF = 0; //Limpa o Flag sinalizador de
overflow TMR0 = 61; //Retoma o valor inicial para o
Timer 0 if (conta >= 40) //Se a variável conta for maior ou
igual a 40 { PORTD = ~PORTD; //Inverte os Bits da porta D,
acendendo ou apagando os LED's conta = 0; //...e zera novamente a variável
para a próxima contagem } conta++; //incrementa a variável conta++ } } } Um dos erros bastante comuns é a falta das chaves que abrem e
fecham as declarações de controle, conforme abaixo: while(1) { //Abre o laço while if (INTCON.T0IF == 1) //Se ocorrer um overflow no timer 0 { //Abre o primeiro comando if INTCON.T0IF = 0; //Limpa o Flag sinalizador de overflow TMR0 = 61; //Retoma o valor inicial para o Timer 0 if (conta >= 40) //Se a variável conta for maior ou igual a
40 { //Abre o segundo comando if PORTD = ~PORTD; //Inverte os Bits da porta D, acendendo ou
apagando os LED's conta = 0; //...e zera novamente a variável para a
próxima contagem } //Fecha o segundo comando if conta++; //incrementa a variável conta++ } //Fecha o primeiro comando if } //Fecha o laço while Perceba também que temos, para o exemplo acima um comando if
dentro de outro comando if. Isso é bastante comum na linguagem C. if (INTCON.T0IF == 1) //Se ocorrer um overflow no timer 0
{ //Abre o primeiro comando if INTCON.T0IF = 0; //Limpa o Flag sinalizador de overflow TMR0 = 61; //Retoma o valor inicial para o Timer 0 if (conta >= 40) //Se a variável conta for maior ou igual
a 40 { //Abre o segundo comando
if PORTD = ~PORTD; //Inverte os Bits da porta D, acendendo
ou apagando os LED's conta = 0; //...e zera novamente a variável para a
próxima contagem } //Fecha o segundo comando if conta++; //incrementa a variável conta++ } //Fecha o primeiro comando if Veja também onde foi utilizado o valor 40 que havíamos guardado
anteriormente. 40 é o número de vezes que ocorrerá o estouro da contagem do Timer 0 para que possamos apagar ou acender os LED’s.
Então temos que haverá um estouro da contagem do timer 0 a cada 25ms e multiplicando este valor por 40, temos 1000ms ou ainda 1 segundo de atraso que será o tempo de permanência dos LED’s apagados ou acesos.
Note também que logo quando condição if (conta >= 40) é satisfeita, depois de inverter o sinal dos LED’s, temos o comando conta = 0 que faz com que a variável conta volte a sua condição inicial e recomece a contagem para a próxima condição de temporização.
Utilizando o Timer 0 com um sinal externo Para utilizarmos a contagem do Timer 0 por um sinal externo, devemos
configurar o registrador OPTION_REG de modo que o incremento do contador Timer 0 deverá ser pela transição do sinal aplicado ao pino RA4/T0CKI e para o nosso circuito, o incremento se dará na borda de descida do sinal, tendo em vista que o sinal aplicado ao pino RA4 está conectado diretamente aos 5V.
Sendo assim, os bits T0CS e T0SE serão colocados em 1.
-Project Name : T0_Ext -Project Path : \ CURSO_PIC\Timer0_Ext\ -Description : .... -Device : P16F877A -Clock : 008.000000 -Device Flags: Default void main() { TRISD = 0; //Direciona toda a porta D como
saída PORTD = 0; //Limpa a porta D apagando os
LED's OPTION_REG = 0b10110000; /*Configura o Registrador
OPTION_REG RBPU = 1 -> PULL UP's porta B
desligados INTEDG = 0 Interrupção na borda
de descida do sinal ...no pino RB0 (não utilizado neste
exemplo). T0CS = 1 -> Incremento na
transição do pino RA4/T0CKI T0SE = 1 -> Incremento na borda
de subida. PSA = 0 -> Prescale aplicado ao
Timer 0 PS2, PS1 e PS0 = 0 -> Prescale =
1:2*/
TMR0 = 250; //Valor Inicial para o timer 0 while(1) { if (INTCON.T0IF == 1) //Se ocorrer um overflow no timer 0 { INTCON.T0IF = 0; //Limpa o Flag sinalizador de
overflow TMR0 = 250; //Retoma o valor inicial para o
Timer 0 PORTD = ~PORTD; //Inverte os Bits da porta D,
acendendo ou apagando //...os LED's } } } Funcionamento do projeto: Se pressionarmos uma vez o botão conectado ao pino RA4/T0CKI,
veremos que nada acontece, portanto, se pressionarmos 12 vezes este botão, os LED’s acenderão e depois mais 12 vezes, os LED’s apagarão e assim sucessivamente.
Analisando o código temos que definimos o prescale com 1:2, portanto a cada 2 transições no pino RA4/T0CKI acontecerá 1 incremento no contador do timer 0 e também definimos que o timer 0 (registrador TMR0) irá contar à partir de 250.
Então 256 – 250 = 6 e, sendo 1 incremento à cada2 transições, temos 6 X 2 = 12.