Apostila Mosaico Pic MÓDULO4
-
Upload
josuemaxibestcombr -
Category
Documents
-
view
364 -
download
31
Transcript of Apostila Mosaico Pic MÓDULO4
Família High End
PIC 18 e MPLAB C18
Curso Módulo 4
Rinaldo Câmara Gonçalves
Grupo Mosaico www.mosaico-eng.com.br
Revisão 1
Curso Módulo 4 –Família 18F e MpLab C18 1
Índice
SUMÁRIO
CAPÍTULO 1 - INTRODUÇÃO......................................................................................................................... 7 NOSSO OBJETIVO............................................................................................................................................. 7 PRÉ – REQUISITOS ........................................................................................................................................... 7 DIDÁTICA DO LIVRO .......................................................................................................................................... 7
CAPÍTULO 2 - INSTALANDO O COMPILADOR MPLAB C18 ...................................................................... 8 INSTALAÇÃO DO COMPILADOR C18.................................................................................................................... 8 INTRODUÇÃO AO MPLAB................................................................................................................................ 10 CRIANDO UM PROJETO ................................................................................................................................... 10 HABILITANDO O GRAVADOR ............................................................................................................................. 16
CAPÍTULO 3 - ARQUITETURA DA LINHA HIGH END................................................................................ 17 O PIC 18FXXXX – HARDWARE ..................................................................................................................... 17 ARQUITETURA INTERNA DO PIC 18F4520 ....................................................................................................... 18 CONFIGURAÇÕES DO OSCILADOR.................................................................................................................... 19
Opções de Oscilador................................................................................................................................ 19 Fonte de Clock e Chaveamento do Oscilador ......................................................................................... 19 Oscilador Primário.................................................................................................................................... 19 Oscilador Secundário ............................................................................................................................... 19 Bloco Oscilador Interno ............................................................................................................................ 20 Registrador de Controle do Oscilador ...................................................................................................... 20 Oscilador a Cristal e Ressonador Cerâmico ............................................................................................ 20 Operação com oscilador externo ............................................................................................................. 21 Operação com oscilador do tipo RC externo ........................................................................................... 21 Multiplicador de Freqüência PLL.............................................................................................................. 21 Configuração do Tipo de Oscilador a Cristal ou Ressonador.................................................................. 22 Configuração do Tipo de Oscilador manualmente................................................................................... 22 Configuração do Oscilador por linha de Comando .................................................................................. 23
GERENCIAMENTO DE ENERGIA ........................................................................................................................ 24 Selecionando o modo power Managed.................................................................................................... 24 As fontes de clock .................................................................................................................................... 25 Seleção do modo Power managed .......................................................................................................... 25
RUN MODE .................................................................................................................................................... 25 Modo PRI_RUN........................................................................................................................................ 25 Modo SEC_RUN ...................................................................................................................................... 25 Modo RC_RUN......................................................................................................................................... 25 Modo Sleep .............................................................................................................................................. 25
MODO IDLE .................................................................................................................................................... 26 MODOS DE RESET .......................................................................................................................................... 26
Registrador RCON ................................................................................................................................... 26 MEMÓRIA....................................................................................................................................................... 28
Memória de Programa - EEPROM Flash................................................................................................. 28 CAPÍTULO 4 - CAPITULO 4 – OPERAÇÃO COM A MEMÓRIA RAM........................................................ 29
CRIANDO VARIÁVEIS....................................................................................................................................... 29 Mapa da Memória RAM ........................................................................................................................... 29
BANCOS DA MEMÓRIA RAM........................................................................................................................... 30 TIPOS DE VARIÁVEIS....................................................................................................................................... 30
Tipos e formato das Variáveis.................................................................................................................. 30 Criando Variáveis e Inicializando ............................................................................................................. 31 Classificação do tipo de variável .............................................................................................................. 32 Variável Global e Local ............................................................................................................................ 33 Constantes ............................................................................................................................................... 34
CLASSES DE ARMAZENAMENTO ....................................................................................................................... 35 Overlay ..................................................................................................................................................... 35 Auto .......................................................................................................................................................... 35 Static......................................................................................................................................................... 35 Extern ....................................................................................................................................................... 36
2
Register .................................................................................................................................................... 36 Typedef..................................................................................................................................................... 36
QUALIFICADOR DE ARMAZENAMENTO............................................................................................................... 36 const ......................................................................................................................................................... 36 volatile ...................................................................................................................................................... 36
QUALIFICADORES NEAR E FAR ......................................................................................................................... 36 near/far – Memória de Dados................................................................................................................... 37 near/far – Memória de Programa ............................................................................................................. 37
CAPÍTULO 5 - OPERAÇÕES LÓGICAS E ARITMÉTICAS ......................................................................... 38 HARDWARE DE MULTIPLICAÇÃO DE 8X8........................................................................................................... 38 OPERADORES ................................................................................................................................................ 38 OPERADORES ARITMÉTICOS ........................................................................................................................... 38
Atribuição.................................................................................................................................................. 38 Soma ........................................................................................................................................................ 39 Multiplicação............................................................................................................................................. 39 Divisão...................................................................................................................................................... 39 Resto da Divisão ...................................................................................................................................... 39 Incremento e Decremento........................................................................................................................ 40 Pré-incremento ......................................................................................................................................... 40 Pós-incremento ........................................................................................................................................ 40 Pré-decremento........................................................................................................................................ 40 Pós-decremento ....................................................................................................................................... 41
OPERADORES RELACIONAIS ........................................................................................................................... 41 OPERADORES LÓGICOS.................................................................................................................................. 42
Operador Lógico Booleano ...................................................................................................................... 42 Operador Lógico bit a bit .......................................................................................................................... 42
EXPRESSÕES ................................................................................................................................................. 42 Abreviando Expressões ........................................................................................................................... 42 Operador virgula “,” .................................................................................................................................. 43 Precedência dos operadores ................................................................................................................... 43 Modelador (Cast)..................................................................................................................................... 44 Comando .................................................................................................................................................. 44
CAPÍTULO 6 - COMANDOS DA LINGUAGEM C......................................................................................... 45 COMANDOS.................................................................................................................................................... 45 CONTROLE DO FLUXO DE PROCESSAMENTO.................................................................................................... 45
Comando IF – simplificado....................................................................................................................... 45 Comando IF – ELSE Simplificado............................................................................................................ 46 Comando IF e IF – ELSE Composto........................................................................................................ 46 Comando SWITCH................................................................................................................................... 50
COMANDOS PARA CONTROLE DE REPETIÇÃO (LOOP) ....................................................................................... 53 Comando WHILE...................................................................................................................................... 53 Comando Do While .................................................................................................................................. 54 Comando For............................................................................................................................................ 55 Cláusulas para controle dos comandos de laço ...................................................................................... 56 Comando goto .......................................................................................................................................... 57
CAPÍTULO 7 - FUNÇÕES ............................................................................................................................. 59 FUNÇÕES....................................................................................................................................................... 59
Criando uma Função................................................................................................................................ 60 Retorno de Função................................................................................................................................... 60 Parâmetros de uma Função..................................................................................................................... 60 Passagem por valor.................................................................................................................................. 61 Passagem por referência ......................................................................................................................... 61 Trabalhando com tipo void em Funções .................................................................................................. 61 Chamada de função externa .................................................................................................................... 62 Recursividade........................................................................................................................................... 63
CAPÍTULO 8 - MANIPULAÇÃO DOS PINOS DE I/O ................................................................................... 64 CONFIGURANDO OS PINOS DE I/O.................................................................................................................... 64 DESCRIÇÃO DOS PINOS .................................................................................................................................. 65
Pinos de alimentação, Oscilador e Master Clear ..................................................................................... 66 ESTUDO DO PORTA ...................................................................................................................................... 68
Curso Módulo 4 –Família 18F e MpLab C18 3
Descrição dos pinos de I/O do PORTA.................................................................................................... 68 Registradores para configuração do PORTA........................................................................................... 68
ESTUDO DO PORTB ...................................................................................................................................... 69 Descrição dos pinos de I/O do PORTB.................................................................................................... 69 Registradores para configuração do PORTB........................................................................................... 70
ESTUDO DO PORTC ...................................................................................................................................... 71 Descrição dos pinos de I/O do PORTC ................................................................................................... 71 Registradores para configuração do PORTC .......................................................................................... 71
ESTUDO DO PORTD ...................................................................................................................................... 72 Descrição dos pinos de I/O do PORTD ................................................................................................... 72 Registradores para configuração do PORTD .......................................................................................... 72
ESTUDO DO PORTE ...................................................................................................................................... 73 Descrição dos pinos de I/O do PORTE.................................................................................................... 73 Registradores para configuração do PORTE........................................................................................... 73
ACESSO AOS SFR’S DE CONFIGURAÇÃO DOS PINOS DE I/O .............................................................................. 73 Função para manipulação do PORTB ..................................................................................................... 74 Descrição das Funções do PORTB ......................................................................................................... 74
CAPÍTULO 9 - TRATAMENTO DE INTERRUPÇÃO .................................................................................... 77 O que é Interrupção? ............................................................................................................................... 77 Hardware da Lógica de Interrupção......................................................................................................... 78 Habilitando uma Interrupção .................................................................................................................... 78 Definindo o vetor de interrupção .............................................................................................................. 78 Tratando uma interrupção ........................................................................................................................ 79
CAPÍTULO 10 - ESTUDO DOS TIMERS....................................................................................................... 80 TIMER 0 ......................................................................................................................................................... 80
A modalidade de 16 Bits .......................................................................................................................... 80 O Prescaler............................................................................................................................................... 80 Interrupção do Timer0 .............................................................................................................................. 80 Diagrama do Timer 0 no modo 8 Bit ........................................................................................................ 80 Diagrama do Timer 0 no modo 16 Bit ...................................................................................................... 80 O registrador T0CON ............................................................................................................................... 81 Registradores para configuração do TIMER0.......................................................................................... 81 T0CON: Registrador de Controle ............................................................................................................. 81 Funções para manipulação do Timer 0.................................................................................................... 82
TIMER 1 ......................................................................................................................................................... 82 Diagrama do Timer 1................................................................................................................................ 82 Modo de 16 bits para Leitura ou Escrita .................................................................................................. 82 Diagrama do Timer 1 modo de escrita e leitura de 16 Bit........................................................................ 83 Oscilador do Timer1 ................................................................................................................................. 83 Usando o TIMER1 como uma fonte de clock........................................................................................... 83 Timer1 em Low Power ............................................................................................................................. 84 Interrupção do Timer1 .............................................................................................................................. 84 Reset do Timer1....................................................................................................................................... 84 Registradores para configuração do TIMER1.......................................................................................... 84 T1CON: Registrador de Controle ............................................................................................................. 84 Funções para manipulação do Timer 1.................................................................................................... 85
TIMER 2 ......................................................................................................................................................... 85 Diagrama do Timer 2................................................................................................................................ 85 Operação do Timer2 ................................................................................................................................ 85 Interrupção do Timer2 .............................................................................................................................. 86 Timer como Base de Tempo .................................................................................................................... 86 Registradores para configuração do TIMER2.......................................................................................... 86 T2CON: Registrador de Controle ............................................................................................................. 86
TIMER 3 ......................................................................................................................................................... 87 Diagrama do Timer 3................................................................................................................................ 87 Modo de 16 bits para Leitura ou Escrita .................................................................................................. 87 Diagrama do Timer 3 modo de escrita e leitura de 16 Bit........................................................................ 88 Oscilador do Timer3 ................................................................................................................................. 88 Interrupção do Timer3 .............................................................................................................................. 88 Reset do Timer3....................................................................................................................................... 88 Registradores para configuração do TIMER3.......................................................................................... 89 T1CON: Registrador de Controle ............................................................................................................. 89
4
Funções para manipulação do Timer 3.................................................................................................... 89 CAPÍTULO 11 - MÓDULO CCP .................................................................................................................... 90
INTRODUÇÃO.................................................................................................................................................. 90 TEORIA E RECURSOS DO PIC .......................................................................................................................... 90
Modo Capture........................................................................................................................................... 90 Modo Compare......................................................................................................................................... 92 Modo PWM............................................................................................................................................... 93
CAPÍTULO 12 - MÓDULO CONVERSOR AD .............................................................................................. 97 TEORIA .......................................................................................................................................................... 97 RECURSOS DO PIC ........................................................................................................................................ 98
CAPÍTULO 13 - MÓDULO MSSP................................................................................................................ 104 INTRODUÇÃO................................................................................................................................................ 104 TEORIA E RECURSOS DO PIC PARA SPI......................................................................................................... 104 TEORIA PARA I2C.......................................................................................................................................... 109
Condição de Start................................................................................................................................... 110 Condição de Stop................................................................................................................................... 110 Condição de Re-Start ............................................................................................................................. 111 Condição de Acknowledge (ACK) .......................................................................................................... 111 Transmissão de endereço...................................................................................................................... 111 Transmissão de dados ........................................................................................................................... 111 Pausas.................................................................................................................................................... 112 Diagramas de tempo .............................................................................................................................. 112
RECURSOS DO PIC PARA I2C........................................................................................................................ 115 Modo Slave............................................................................................................................................. 115 Modo Master........................................................................................................................................... 120
CAPÍTULO 14 - MÓDULO USART.............................................................................................................. 125 INTRODUÇÃO................................................................................................................................................ 125 TEORIA ........................................................................................................................................................ 125
Modo Assíncrono.................................................................................................................................... 125 Modo síncrono........................................................................................................................................ 126
RECURSOS DO PIC ...................................................................................................................................... 127 Modo Assíncrono.................................................................................................................................... 130 Modo Síncrono ....................................................................................................................................... 131
CAPÍTULO 15 - VARIÁVEIS ORGANIZADAS............................................................................................ 133 INTRODUÇÃO................................................................................................................................................ 133 MATRIZ ........................................................................................................................................................ 133
Criando um Vetor ................................................................................................................................... 133 Criando um vetor na memória RAM....................................................................................................... 133 Iniciando um Vetor.................................................................................................................................. 133 Criando um vetor na memória de programa .......................................................................................... 133 Leitura e Escrita de um vetor ................................................................................................................. 134 Criando uma matriz multidimensional .................................................................................................... 135 Strings .................................................................................................................................................... 136
ESTRUTURA DE DADOS ................................................................................................................................. 136 Criando uma struct de variáveis............................................................................................................. 136 Acessando elementos de uma estrutura................................................................................................ 137 Escrevendo nos elementos de uma estrutura........................................................................................ 138 Leitura de um elemento de estrutura ..................................................................................................... 138 Operação entre estruturas ..................................................................................................................... 138 Inicialização de uma estrutura................................................................................................................ 139 Matrizes de estruturas............................................................................................................................ 139 Estrutura de Bits ..................................................................................................................................... 139
UNIÕES........................................................................................................................................................ 141 Acessando os elementos de uma union ................................................................................................ 141 Escrevendo em um elemento de uma union.......................................................................................... 142 Leitura de um elemento de uma union................................................................................................... 142
VARIÁVEIS ENUMERADAS.............................................................................................................................. 142 Operações com variáveis enumeradas.................................................................................................. 143
PONTEIROS.................................................................................................................................................. 143
Curso Módulo 4 –Família 18F e MpLab C18 5
Definindo um ponteiro ............................................................................................................................ 143 Carregando um endereço no ponteiro ................................................................................................... 143 Operações com ponteiros ...................................................................................................................... 144
CAPÍTULO 16 - MCMASTER – DESENVOLVIMENTO DE SISTEMAS COM MICROCONTROLADORES PIC................................................................................................................................................................. 146
INTRODUÇÃO................................................................................................................................................ 146 VISÃO MACRO DO SISTEMA........................................................................................................................... 146 MÓDULOS PADRÃO....................................................................................................................................... 147
Microcontrolador..................................................................................................................................... 147 LCD alfanumérico................................................................................................................................... 147 Displays de leds com 7 segmentos........................................................................................................ 147 Leds........................................................................................................................................................ 148 Teclado matricial .................................................................................................................................... 148 Buzzer .................................................................................................................................................... 149 Memória E2PROM externa .................................................................................................................... 149 Relógio de tempo real (RTC) ................................................................................................................. 149 Comunicação serial RS-232................................................................................................................... 149 Conversão analógica / digital (A/D)........................................................................................................ 150
PERIFÉRICOS ADICIONAIS ............................................................................................................................. 152 Placa de experiências EXP01................................................................................................................ 152
GRAVADOR .................................................................................................................................................. 152 CAPÍTULO 17 - EXPERIÊNCIAS ................................................................................................................ 153
EXPERIÊNCIA 1 - LEITURA DE UMA TECLA E ACIONAMENTO DE UM LED............................................................. 153 OBJETIVO ................................................................................................................................................ 153 DESCRIÇÃO ............................................................................................................................................. 153 ESQUEMA ELÉTRICO ................................................................................................................................ 154 FLUXOGRAMA........................................................................................................................................... 155 CÓDIGO ................................................................................................................................................... 156 EXERCÍCIOS PROPOSTOS ......................................................................................................................... 158
EXPERIÊNCIA 2 – PISCA - PISCA ................................................................................................................... 159 OBJETIVO ................................................................................................................................................ 159 DESCRIÇÃO ............................................................................................................................................. 159 ESQUEMA ELÉTRICO ................................................................................................................................ 160 FLUXOGRAMA........................................................................................................................................... 161 CÓDIGO ................................................................................................................................................... 163 EXERCÍCIOS PROPOSTOS ......................................................................................................................... 166
EXPERIÊNCIA 3 - DIMMER ............................................................................................................................. 167 OBJETIVO ................................................................................................................................................ 167 DESCRIÇÃO ............................................................................................................................................. 167 ESQUEMA ELÉTRICO ................................................................................................................................ 168 FLUXOGRAMA........................................................................................................................................... 169 CÓDIGO ................................................................................................................................................... 172 DICAS E COMENTÁRIOS ............................................................................................................................ 176 EXERCÍCIOS PROPOSTOS ......................................................................................................................... 176
EXPERIÊNCIA 4 – BOTÕES, LEDS E BUZZER .................................................................................................. 177 OBJETIVO ................................................................................................................................................ 177 DESCRIÇÃO ............................................................................................................................................. 177 ESQUEMA ELÉTRICO ................................................................................................................................ 178 FLUXOGRAMA........................................................................................................................................... 179 CÓDIGO ................................................................................................................................................... 183 DICAS E COMENTÁRIOS ............................................................................................................................ 188 EXERCÍCIOS PROPOSTOS ......................................................................................................................... 188
EXPERIÊNCIA 5 – VARREDURA DE DISPLAYS E UTILIZAÇÃO DO TIMER 1 ......................................................... 189 OBJETIVO ................................................................................................................................................ 189 DESCRIÇÃO ............................................................................................................................................. 189 ESQUEMA ELÉTRICO ................................................................................................................................ 191 FLUXOGRAMA........................................................................................................................................... 192 CÓDIGO ................................................................................................................................................... 197 DICAS E COMENTÁRIOS ............................................................................................................................ 204 EXERCÍCIOS PROPOSTOS ......................................................................................................................... 204
EXPERIÊNCIA 6 – DISPLAY DE CRISTAL LÍQUIDO LCD ..................................................................................... 205 OBJETIVO ................................................................................................................................................ 205
6
DESCRIÇÃO ............................................................................................................................................. 205 ESQUEMA ELÉTRICO ................................................................................................................................ 206 FLUXOGRAMA........................................................................................................................................... 207 CÓDIGO ................................................................................................................................................... 212 DICAS E COMENTÁRIOS ............................................................................................................................ 219 EXERCÍCIOS PROPOSTOS ......................................................................................................................... 219
EXPERIÊNCIA 7 – CONVERSOR A/D .............................................................................................................. 220 OBJETIVO ................................................................................................................................................ 220 DESCRIÇÃO ............................................................................................................................................. 220 ESQUEMA ELÉTRICO ................................................................................................................................ 221 FLUXOGRAMA........................................................................................................................................... 222 CÓDIGO ................................................................................................................................................... 225 DICAS E COMENTÁRIOS ............................................................................................................................ 229 EXERCÍCIOS PROPOSTOS ......................................................................................................................... 229
EXPERIÊNCIA 8 – MODULO PWM.................................................................................................................. 230 OBJETIVO ................................................................................................................................................ 230 DESCRIÇÃO ............................................................................................................................................. 230 ESQUEMA ELÉTRICO ................................................................................................................................ 232 FLUXOGRAMA........................................................................................................................................... 233 CÓDIGO ................................................................................................................................................... 236 DICAS E COMENTÁRIOS ............................................................................................................................ 243 EXERCÍCIOS PROPOSTOS ......................................................................................................................... 243
EXPERIÊNCIA 9 – MASTER I2C ...................................................................................................................... 244 OBJETIVO ................................................................................................................................................ 244 DESCRIÇÃO ............................................................................................................................................. 244 ESQUEMA ELÉTRICO ................................................................................................................................ 246 FLUXOGRAMA........................................................................................................................................... 247 CÓDIGO ................................................................................................................................................... 252 DICAS E COMENTÁRIOS ............................................................................................................................ 259 EXERCÍCIOS PROPOSTOS ......................................................................................................................... 259
EXPERIÊNCIA 10 – COMUNICAÇÃO SERIAL RS232 VIA USART ...................................................................... 260 OBJETIVO ................................................................................................................................................ 260 DESCRIÇÃO ............................................................................................................................................. 260 ESQUEMA ELÉTRICO ................................................................................................................................ 261 FLUXOGRAMA........................................................................................................................................... 262 CÓDIGO ................................................................................................................................................... 264 DICAS E COMENTÁRIOS ............................................................................................................................ 269 EXERCÍCIOS PROPOSTOS ......................................................................................................................... 269
EXPERIÊNCIA 11 – TECLADO MATRICIAL 4X4 ................................................................................................ 270 OBJETIVO ................................................................................................................................................ 270 DESCRIÇÃO ............................................................................................................................................. 270 ESQUEMA ELÉTRICO ................................................................................................................................ 271 FLUXOGRAMA........................................................................................................................................... 272 CÓDIGO ................................................................................................................................................... 276 DICAS E COMENTÁRIOS ............................................................................................................................ 282 EXERCÍCIOS PROPOSTOS ......................................................................................................................... 282
EXPERIÊNCIA 12 – RELÓGIO DE TEMPO REAL (RTC)...................................................................................... 283 OBJETIVO ................................................................................................................................................ 283 DESCRIÇÃO ............................................................................................................................................. 283 ESQUEMA ELÉTRICO ................................................................................................................................ 284 FLUXOGRAMA........................................................................................................................................... 285 CÓDIGO ................................................................................................................................................... 289 DICAS E COMENTÁRIOS ............................................................................................................................ 294 EXERCÍCIOS PROPOSTOS ......................................................................................................................... 294
Curso Módulo 4 –Família 18F e MpLab C18 7
Capítulo 1 - Introdução
Nosso Objetivo
Esta obra tem como finalidade introduzir o leitor no mundo da linha High End da Microchip, num novo conceito de hardware e método de programação.
Para este treinamento adotamos a linguagem C por ser de alto nível, e pensando na velocidade de desenvolvimento de novos projetos com este conceito. Hoje quando pensamos em ganhar tempo em desenvolvimento de sistemas microcontrolados é difícil não pensar na linguagem C.
No estudo do hardware você conhecerá as melhorias nos periféricos já conhecidos da família 16 e muito mais.
Para completar os nossos estudos vamos aprender a programar em linguagem C usando o compilador MPLAB C18 da Microchip.
A Microchip disponibiliza uma versão demo válida por 60 dias, renovável, o que facilita o acesso e o aprendizado do leitor.
A escolha de um curso abordando a linguagem C se dá devido à arquitetura dos PIC 18, desenvolvidos para contemplar está linguagem de programação.
Pré – requisitos
Como todo treinamento, este também necessita de alguns pré-requisitos, que são, conhecer a linha 16 da Microchip, hardware e software (as 35 instruções), e ter noções de linguagem C ou Pascal. Além de um conhecimento mínimo de Eletrônica.
Didática do Livro
Visando facilitar a vida do leitor à ordem dos capítulos, estão de forma cronológica até o capítulo 8, deste ponto em diante os assuntos são independentes ficando a cargo do leitor a escolha do capítulo desejado.
Padrões textuais Para facilitar o entendimento deste material, alguns padrões textuais foram adotados: Itálico Termo adotado em língua estrangeira que ainda não foi popularmente adotado no Brasil. Negrito Nome do registrador, bit ou pino; Registrador<bit> Nome do registrador e nome do(s) bit(s) interno(s);
8
Capítulo 2 - Instalando o compilador MPLAB C18
Instalação do compilador C18
A família 18 é linha de alta performance de microcontroladores da Microchip. Sua arquitetura foi especialmente desenvolvida para programação em alto nível.
Neste nosso estudo de microcontroladores da família 18, utilizaremos o compilador C18 da Microchip para programação em linguagem C que nos permitirá utilizar as instruções avançadas desta família.
Escolhemos este compilador por ter sido desenvolvido pela Microchip e por possuir uma versão demo válida por 60 dias, o que permitirá que hobistas, técnicos e engenheiros tenham um acesso facilitado para estudo do microcontrolador e da programação em linguagem C.
Vejamos agora, como instalar o compilador C18 passo a passo.
• Primeiro Passo
Você pode fazer o download do compilador C18 diretamente do site da Microchip no endereço www.microchip.com\download\c18 .
Após ter feito o download execute o programa, MPLAB-c18-v2.30.01-demo-win32.exe. O arquivo estará na pasta selecionado por você para salvar o download.
Figura 2.1 – Pasta contendo o arquivo de instalação.
• Segundo Passo
Pressione o botão NEXT, para prosseguirmos com a instalação.
Figura 2.2 – Tela inicial de instalação.
Curso Módulo 4 –Família 18F e MpLab C18 9
• Terceiro Passo
Neste passo você pode escolher o nome do diretório que será instalado o compilador C18 ou manter o nome sugerido mcc18.
Figura 2.3 – Tela de escolha do diretório onde vai ser instalado o MPLAB C18.
• Quarto Passo
Fim da instalação dos arquivos no diretório mcc18.
Figura 2.4 – Tela do andamento de instalação do MPLAB C18.
• Quinto Passo
Pressione a tecla Finish para concluir a instalação.
Figura 2.5 – Tela de conclusão da instalação do MPLAB C18.
10
• Sexto Passo
Após a instalação será necessário reiniciar o seu computador para concluir a instalação, pressione a tecla OK.
Figura 2.6 – Tela onde o programa de instalação pede ao usuário reiniciar o computador.
Introdução ao MPLAB
Criando um Projeto
Após a instalação do compilador C, iremos criar um projeto no MPLAB e selecionaremos o compilador C18 para compilar nossos programas.
Clique na opção Project e depois em Project Wizard.
Figura 2.7 – Menu Project.
Esta é a tela de boas vindas. Pressione o botão Avançar, para continuarmos o processo.
Figura 2.8 – Tela inicial do Assistente de Criação do Projeto.
Curso Módulo 4 –Família 18F e MpLab C18 11
Nesta tela você deve selecionar o modelo de PIC da família 18 que você deseja utilizar. Observe que no canto superior esquerdo de cada tela temos a indicação do passo em que estamos para
criação de um projeto. Este é o passo um.
Figura 2.9 – Tela de seleção do microcontrolador.
No passo dois você irá selecionar, o compilador, pressione a seta da opção Active Toolsuite e selecione a opção Microchip C18 Toolsuite. Em alguns casos no campo Toolsuite Contents, um dos caminhos pode estar corrompido, quando isto ocorrer veremos um X vermelho antes do nome da ferramenta.
Para resolver este problema selecione a opção com o X vermelho e pressione o botão Browse, vá até o diretório em que o arquivo está instalado e selecione o mesmo, pronto isto irá resolver o problema.
Pressione a tecla Avançar e vá para o passo seguinte.
Figura 2.10 – Tela seleção da ferramenta de trabalho.
No passo três iremos dar um nome para o nosso projeto e selecionar o diretório para o nosso projeto. Dependendo da versão do MPLAB o nível da subpasta pode ser um problema, não podemos ultrapassar
62 caracteres. Portanto quanto mais próximo da raiz a sua pasta estiver melhor. Veja o exemplo abaixo.
12
Figura 2.11 – Tela de Criação do Projeto.
No passo quatro, vamos selecionar o(s) arquivo(s) que devem ser compilados no nosso projeto. Você deve selecionar os arquivos e pressionar o botão ADD>>, depois pressione o botão Avançar para
irmos ao próximo passo.
Figura 2.12 – Tela de seleção dos arquivos a serem usados no projeto.
Curso Módulo 4 –Família 18F e MpLab C18 13
Na última tela, podemos ver o modelo de microcontrolador, o compilador selecionado e o diretório de nosso projeto. Para finalizar pressione o botão Concluir.
Figura 2.13 – Tela de conclusão do Assistente de Criação do Projeto.
Agora o nosso trabalho está quase concluído, como estamos criando um projeto para trabalhar com o compilador C18, devemos incluir em nosso projeto um arquivo de linker, que passará informações importantes ao compilador sobre a área de memória que podemos utilizar.
Para cada microcontrolador existem dois arquivos de linker um deles é para utilizarmos o ICD2 da Microchip para gravar ou depurar e o outro para ser utilizado para qualquer outro tipo de gravador.
Alguns modelos de microcontrolador possuem um terceiro arquivo de linker para que possamos utilizar as instruções de modo expandido, essas instruções economizam código na programação em Linguagem C.
Figura 2.14 – Tela com o projeto aberto.
14
Observe que o arquivo de Linker está no diretório mcc18, este é o diretório criado na instalação do compilador C18.
Figura 2.15 – Tela de inclusão dos arquivos de linker.
Para concluir a criação do projeto vamos novamente selecionar a opção Project e depois Build Options Project. Neste passo vamos indicar o caminho dos arquivos de linker e do projeto.
Figura 2.16 – Configurando o Build Options.
Curso Módulo 4 –Família 18F e MpLab C18 15
Nesta janela você irá pressionar o botão Browse, e selecione o caminho de cada diretório.
Figura 2.17 – Selecionando os diretórios.
Agora vamos definir o tipo padrão de variáveis que serão criadas por nós em nossos desenvolvimentos.
Veja que definimos o padrão STATIC.
Figura 2.18 – Definindo o tipo padrão de variáveis.
Depois de todas estas configurações já podemos compilar nosso programa e ver os resultados.
16
Figura 2.19 – Compilando o programa.
Habilitando o gravador
Para que possamos gravar o nosso código compilado devemos agora habilitar o gravador de nossa preferência.
Você pode utilizar: Opção 1 para PICSTART Plus, McFlash e McPlus, estes são gravadores de PIC. Opção 2 para MPLAB ICD2, estes pode ser usado como gravador e ou depurador. Opção 3 para MPLAB PM3, este é um gravador para produção. Opção 4 para PRO MATE II, este é um gravador para produção. Opção 5 para Pickit 1, estes é um kit de treinamento da Microchip.
Figura 2.20 – Habilitando o gravador.
Curso Módulo 4 –Família 18F e MpLab C18 17
Capítulo 3 - Arquitetura da Linha High End
O PIC 18FXXXX – Hardware
Para o nosso estudo da família 18, escolhemos o modelo PIC 18F4520 por ser uma versão atualizada do
modelo PIC 18F452 e por ser compatível pino a pino com o PIC 16F877A, modelo da família 16. Desta forma nossos projetos baseados neste modelo poderão ser facilmente migrados para a família 18. A partir de agora veremos como a família High End da Microchip poderá nos ajudar em
desenvolvimentos mais complexos e que exigem uma capacidade de memória maior. Vamos estudar a sua arquitetura, os seus periféricos e a programação em linguagem C com o
compilador MPLAB C18. A linha High End teve a sua arquitetura e o set de instruções otimizado para programação em linguagem
C, por este motivo escolhemos o compilador MPLAB C18, como linguagem de programação para nossos estudos.
Como filosofia da Microchip ela manteve a compatibilidade entre as outras famílias para que possamos migrar facilmente, das famílias 16 e 17 para a família 18.
Uma das evoluções desta família se encontra no acesso a memória de programa, que agora é feita de forma linear não existindo a necessidade de seleção de página.
Quando falamos em seleção de página lembramos que a memória RAM possui seleção de banco, continuamos com a seleção de banco, mas a grande sacada da Microchip foi de aumentar a capacidade desta memória, agora os bancos são de 256 bytes. Para você ter uma dimensão do que estamos falando, um PIC16F877A possui 367bytes divididos em 4 bancos.
Outra vantagem é o ACESS BANK, ele possibilita livre acesso aos 128 bytes de registradores de função especial, (SFR, banco 15) e 128 bytes de registrador de uso geral, (GPR, banco 0).
Vamos ver agora o que um microcontrolador de alta performance pode nos oferecer. Como todo modelo de microcontrolador este também foi lançado aos pares veja a tabela abaixo.
DISPOSITIVO FLASH (BYTES)
INSTRUÇÕES DE UMA WORD RAM (BYTES) DATA EEPROM
(BYTES) NÚMERO DE
PINOS PIC18F2420 16K 8192 768 256 28 PIC18F2520 32K 16384 1536 256 28 PIC18F4420 16K 8192 768 256 40 PIC18F4520 32K 16384 1536 256 40
Estes microcontroladores são apenas uma amostra da linha 18 da Microchip, existem outros modelos
com interface CAN, USB 2.0 e modelos específicos para controle de motor e uso geral. Com um line card da Microchip é possível escolher o modelo adequado ao seu projeto. E os recursos não param por ai veja mais: • Capacidade de processamento de 10MIPS. • Fonte de clock interna de 32KHz e 8MHz do tipo RC. • PLL interno para multiplicar a freqüência de clock. • Prioridade no tratamento de interrupção é possível escolher entre alta ou baixa prioridade. • Hardware de multiplicação 8X8 bits executado em 1 ciclo de máquina. • Alta capacidade de corrente nos pinos de I/O, 25mA por pino. • Três fontes de interrupção externa. • Uma interrupção de mudança de estado, quatro fontes. • Timer 0 de 8 ou 16 bits configurável. • Timer 1 e 3 de 16 bits. • Timer 2 e 4 de 8 bits. • Módulo CCP. • Módulo MSSP (SPI e I2C). • Enhanced USART. • Módulo PSP. • ADC de 10bits. • PLVD. • BOR. • WDT.
18
Arquitetura interna do PIC 18F4520
Figura 3.1 – Arquitetura do PIC 18F4520
Curso Módulo 4 –Família 18F e MpLab C18 19
Configurações do Oscilador
Uma das inovações deste microcontrolador está nas opções de oscilador, temos a possibilidade de utilizar um oscilador interno de 31KHz e 8MHz.
O tipo de Oscilador é definido através dos configuration bits no momento de gravar o microcontrolador, através do registrador 1H, podemos escolher uma das 10 opções de oscilador como pode ser visto abaixo:
Tabela 3.1 - Opções de Oscilador.
1 LP Low Power Cristal 2 XT Cristal/Ressonador 3 HS High-Speed Cristal/Ressonador 4 HSPLL High-Speed Cristal/Ressonador com o PLL habilitado 5 RC Oscilador externo com Resistor e Capacitor, RA6 é saída de clock, Fosc\4. 6 RCIO Oscilador externo com Resistor e Capacitor, RA6 pino de I/O. 7 INTIO1 Oscilador RC interno, RA6 saída de clock Fosc\4 e RA7 pino de I/O. 8 INTIO2 Oscilador RC interno, RA6 e RA7 pino de I/O. 9 EC RA7 é uma entrada de clock externa e RA6 saída de clock Fosc\4 10 ECIO RA7 é uma entrada de clock externa e RA6 pino de I/O
Fonte de Clock e Chaveamento do Oscilador O sistema de clock permite utilizar o oscilador interno de 31kHz ou 8MHz, ou o Timer1 ou um cristal ou
ressonador para geração de clock do microcontrolador. Este sistema possui essencialmente três fontes de clock, que são: Oscilador Primário, Oscilador
Secundário, Bloco de Oscilador Interno.
LP, XT, HS, RC, EC
HSPLL, INTOSC/PLL
Controle deClock
4 x PLL
111
011
000
101
010
100
001
110
CONFIG1H<Fosc3>CONFIG1H<Fosc2>CONFIG1H<Fosc1>CONFIG1H<Fosc0>
OSCON<SCS1>OSCON<SCS0>
POS
TSCALE
R
1
0
8MHz4MHz2MHz1MHz
500kHz250kHz125kHz
31,25kHz
OSCTUNE<INTSRC>
INTRC31kHz
INTRC8MHz
BlocoOscilador
Interno 8MHzINTRC
31kHz(INTRC)
OSCON<IRCF0>OSCON<IRCF1>OSCON<IRCF2>
PARA OS PERIFÉRICOS
WDT, PWRT, FSCM etwo Speed Startup
OSCTUNE<PLLEN>FONTE DE CLOCK PARA
OUTROS MÓDULOS
T1OSC
OSCILADOR INTERNO
SLEEP
T1OSCENHABILITA OSCILADOR
OSC2
OSC1
T1OSI
T1OSO
IDLEN
CPU
Figura 3.2 – Fonte de Clock.
Oscilador Primário O oscilador primário e formado pelo oscilador externo Que pode ser um cristal ou ressonador (LP, XT,
HS e HSPLL), pelo oscilador interno do tipo RC ou pelo sistema de clock externo (EC).
Oscilador Secundário O módulo secundário é formado pelo Timer1.
20
Bloco Oscilador Interno A terceira fonte de clock faz parte do power managed mode, sistema para baixo consumo. Este sistema
pode ser utilizado na falha do oscilador primário, garantindo a continuidade de funcionamento do sistema. Sendo possível trabalhar com 31KHz ou 8MHz. Trabalhando em 8MHz podemos utilizar o postscale e
selecionar freqüências mais baixas, diminuindo desta forma o consumo do microcontrolador.
Registrador de Controle do Oscilador O registrador OSCCON é responsável pela seleção das fontes de clock do sistema, primário, secundário
e bloco oscilador interno. Através dos bits <SCS1:SCS0> podemos selecionar a fonte de clock. Com os bits <IRCF2:IRCF0>,
selecionamos o postscale do oscilador interno RC, que pode ser ajustado de 31.25kHz a 8MHz, desde que o RC esteja configurado para 8MHz.
Os bits <OSTS:IOFS> e T1CON <T1RUN>, indicam qual é a fonte de clock do microcontrolador. O bit IDLEN determina se o microcontrolador está no modo sleep ou no modo IDLE.
OSCCON: Controle do oscilador
Oscilador a Cristal e Ressonador Cerâmico No modo oscilador temos as opções: LP, XT, HS e HSPLL, o cristal ou ressonador são conectados nos
pinos OSC1 e OSC2. A interligação do cristal pode ser vista na figura 3.3.
Curso Módulo 4 –Família 18F e MpLab C18 21
SLEEP
OSC2
OSC1
C2
C1
18FXXXXSistema Oscilador
Figura 3.3 – Esquema de ligação do cirstal ou ressonador.
Operação com oscilador externo Quando trabalhamos com oscilador externo nos modos EC e ECIO podemos conectar um circuito
externo de oscilador no pino OSC1. No modo EC teremos no pino OSC2 a freqüência de clock dividida por quatro, este sinal pode ser
utilizado por outros circuitos externos. O esquema de ligação pode ser visto na figura 3.4a. No modo ECIO o pino OSC2 pode ser utilizado como pino de I/O nós teremos o pino RA6 disponível.
Veja na figura 3.4b o esquema de ligação.
RA6
OSC1/CLKIN
18FXXXXSistema de Oscilador Externo
para outros dispositivos
Pino de I/O
Entrada doClock Externo
OSC2/CLKOUT
OSC1/CLKIN
18FXXXXSistema de Oscilador Externo
para outros dispositivos
Fosc/4
Entrada doClock Externo
Figura 3.4a Figura 3.4b
Operação com oscilador do tipo RC externo Este é o modo de menor custo, o circuito oscilador é formado apenas por um resistor e um capacitor. Um cuidado com este modo é que a freqüência pode variar de acordo com a temperatura e a variação
na tensão de alimentação, já que o sistema depende da carga e descarga de um capacitor. Este modo possui duas formas de configuração, OSC2/RA6 como saída de clock (Fosc/4) ou como pino
de I/O. Na figura 3.5a temos o modo RCIO e na figura 3.5b temos o modo RC.
OSC2/CLOCKOUT
OSC1/CLKIN
18FXXXXSistema de Oscilador Externo
Fosc/4
Rext
Cext
VDD
RA6
OSC1/CLKIN
18FXXXXSistema de Oscilador Externo
Pino de I/O
Rext
Cext
VDD
Figura 3.5a. Figura 3.5b
Multiplicador de Freqüência PLL Um dos recursos adicionais é o circuito de PLL que nos permite multiplicar a freqüência do oscilador
interno em 4 vezes, utilizando um cristal externo de menor valor. Desta forma poderemos trabalhar com um cristal de menor valor, o que diminui o problema com EMI, já
que iremos multiplicar a freqüência de clock internamente. Para que possamos habilitar o PLL devemos, selecionar o modo HSPLL, o que quer dizer que teremos
que trabalhar com um cristal cuja freqüência seja maior do que 4MHz. No modo oscilador interno deveremos selecionar o RC para 8MHz a fim de podermos utilizar o PLL com o circuito RC interno.
22
A seleção e habilitação do PLL podem ser feitas através dos fuses e do registrador OSCTUNE, veja o registrador e para maiores detalhes, veja o apêndice A. OSCTUNE: Registrador de ajuste do oscilador
Configuração do Tipo de Oscilador a Cristal ou Ressonador Para que possamos configurar um dos tipos de oscilador citados podemos, ou inserir uma linha de
comando em nosso programa ou realizar a configuração manualmente.
Configuração do Tipo de Oscilador manualmente Para selecionar o tipo de oscilador, devemos selecionar o microcontrolador desejado, ele pode ser
definido na etapa de criação do projeto ou como veremos na figura 3.6a e 3.6b. Na barra de ferramenta vamos selecionar a opção Configure Select Device.
Figura 3.6a – Seleção do microcontrolador.
Na janela que foi aberta vamos selecionar o microcontrolador desejado, para que possamos seguir para
a próxima etapa, configurar o tipo de oscilador.
Curso Módulo 4 –Família 18F e MpLab C18 23
Figura 3.6b – Seleção do microcontrolador.
Após a seleção do microcontrolador vamos até a opção Configure Configuration Bits, nesta janela veremos as possibilidades de configuração de oscilador.
Figura 3.7 – Menu Configure, Configuration Bits.
Nesta janela podemos selecionar qualquer uma das opções de oscilador desejado.
Figura 3.8 – Seleção do tipo de oscilador.
Configuração do Oscilador por linha de Comando Em nosso código vamos incluir uma linha de comando que fará a configuração dos fuses do
microcontrolador. Esta linha de comando de configuração deve vir logo após a linha de include do arquivo de header do microcontrolador escolhido.
O comando #pragma config, é utilizado para que possamos entrar com a palavra de configuração dos fuses, que é gravada na memória de programa.
As demais palavras de configuração do oscilador podem ser vistas na tabela 3.2, a seguir temos um exemplo de palavra de configuração dos fuses.
Exemplo: #pragma config OSC = HSPLL No arquivo de include do microcontrolador escolhido temos as opções de configuração do oscilador,
veja as opções:
24
Tabela 3.2 – Palavras de configuração para os tipos de osciladores
Palavra de Configuração Tipo de Oscilador Faixa de Freqüência _OSC_LP_1H LP 0 a 200kHz _OSC_XT_1H XT 200kHz a 4MHz _OSC_HS_1H HS 4MHz a 25MHz
_OSC_RC_1H RC externo/RA6 clock out(Fosc/4) 0 a 16MHz
_OSC_RCIO6_1H RC externo/RA6 I/O 0 a 16MHz _OSC_EC_1H Clock Externo 0 a 25MHz
_OSC_ECIO6_1H Clock Externo/RA6 I/O 0 a 25MHz _OSC_HSPLL_1H HS multiplicado pelo PLL Até 10MHz
_OSC_INTIO67_1H RC Interno/ RA6 e RA7 I/O 31kHz a 8MHz
_OSC_INTIO7_1H RC Interno/RA6 clock out (Fosc/4) 31kHz a 8MHz
_FCMEN_OFF_1H Disabled Desabilita verificação de falha no oscilador principal
_FCMEN_ON_1H Enable Habilita verificação de falha no oscilador principal
_IESO_OFF_1H Disabled Desabilita a comutação entre oscilador interno e externo
_IESO_ON_1H Enable Habilita a comutação entre oscilador interno e externo
Gerenciamento de Energia
Visando melhorar a inicialização dos microcontroladores a Microchip, desenvolveu para a família 18, três categorias de gerenciamento de energia, são elas:
• Run modes • Idle modes • Sleep modes
Estas modalidades fornecem uma variedade de opções para diminuição do consumo de energia, para as
aplicações onde os recursos podem ser limitados (sistemas alimentados por pilhas e baterias). O modo Run e o modo Idle podem usar um dos três blocos interno de geradores de clock (primário,
secundário ou bloco oscilador interno); a modalidade Sleep obviamente não fonte de clock. As modalidades de power managed podem incluir diversas possibilidades de salvar do dispositivo e
mate-lo funcionando. Uma delas é a característica de chavear a fonte de clock, oferecida em outros dispositivos PIC18, permitindo que o controlador use o oscilador Timer1 no lugar do oscilador primário.
É incluída também a modalidade Sleep, comum nos microcontroladores da Microchip, onde o oscilador interno é desligado interrompendo todas as fontes de clock do dispositivo.
Selecionando o modo Power Managed Para selecionar o modo de gerenciamento de energia é necessário tomar duas decisões: • Se processador central está recebendo sinal de clock ou não. • E qual fonte de clock será selecionada.
O processador central controla o bit de OSCCON<IDLEN> que controla o clock da CPU, quando os bits
OSCCON<SCS1:SCS0> selecionam a fonte de clock. Os módulos individuais, os bits de controle, as fontes de clock e os módulos afetados são vistos na
tabela abaixo.
Curso Módulo 4 –Família 18F e MpLab C18 25
Tabela 3.3 – Modo de gerenciamento e módulos afetados
As fontes de clock Os bits OSCCON<SCS1:SCS0> permitem a seleção de uma de três fontes de clock, são: • Fonte de clock primário, definido pelos bit de configuração CONFIG1H<FOSC3:FOSC0> • Fonte de clock secundário (oscilador do Timer1) • Bloco oscilador interno (para modo RC)
Seleção do modo Power Managed Para selecionar um dos modos devemos configurar o registrador OSCCON. Os bits <SCS1:SCS0> selecionam a fonte de clock e qual o modo de trabalho Run ou Idle. Mudar estes bits causa uma comutação imediata para a nova fonte de clock, supondo que está
funcionando.
Run Mode
No modo Run, o sinal de clock é fornecido ao microcontrolador e para os periféricos.
Modo PRI_RUN O modo PRI_RUN é o modo normal de trabalho do microcontrolador. Este também é o modo padrão de inicialização do microcontrolador. Neste modo o bit OSCCON<OSTS> é colocado em um, já o bit OSCCON<IOFS> pode ser colocado em
um se o bloco oscilador interno for a fonte de clock primária.
Modo SEC_RUN O modo SEC_RUN é o modo compatível com a característica de chaveamento do sinal de clock
oferecida em outros dispositivos PIC18. Nesta modalidade, o processador central e os periféricos recebem o sinal de clock do Timer1.
Isto dá ao usuário a opção de um consumo de potência mais baixa ou ainda usar uma fonte de clock de maior precisão.
O modo SEC_RUN é configurado pelos bits <SCS1:SCS0>. O oscilador do Timer1 já deve estar funcionar antes de entrar no modo SEC_RUN.
Modo RC_RUN No modo RC_RUN, o processador central e os periféricos recebem o sinal de clock do bloco do oscilador
interno, usando o multiplex do INTOSC. Neste modo, o sinal de clock primário é desligado. Ao usar o bloco oscilador interno (INTRC), este modo permite um menor consumo de energia.
Modo Sleep Quando o dispositivo entra no modo Sleep o bit IDLEN é zerado. Entrar no modo sleep, desliga o oscilador interrompendo o processamento. Se o WDT for selecionado, a
fonte de INTRC continuará operando. Se o oscilador Timer1 for permitido, também continuara funcionando. Quando um evento acordar o microcontrolador do modo sleep (por interrupção, por reset ou por WDT), o
dispositivo iniciará o processamento até que a fonte de clock selecionada esteja funcionando
26
Modo Idle
O modo Idle permite que somete os periféricos trabalhem, quando uma instrução Sleep é executada. Selecionar o modo Idle permite que os usuários controlem mais o consumo de energia. Se o bit de
IDLEN estiver em um, quando uma instrução Sleep for executada, os periféricos continuam a operar através da fonte de clock selecionada pelos bits <SCS1:SCS0>.
Como o microcontrolador não está operando, as únicas maneiras de sair do modo sleep será pelo WDT ou por reset.
Quando o processador central começa a executar o código, recomeça com a mesma fonte de clock do modo Idle.
Modos de Reset
Os dispositivos PIC18F2420/2520/4420/4520 possuem vários tipos de reset: a) Power on Reset (POR) b) MCLR reset durante operação normal c) MCLR reset durante o power managed modo d) Watchdog Timer (WDT) reset durante a execução e) Reset por Brown-out f) Instrução de Reset g) Reset por estouro de pilha (Stack Full) h) Reset por Stack Underflow
Observe o diagrama de bloco simplificado do circuito de reset.
Figura 3.9 - Circuito de reset.
Curso Módulo 4 –Família 18F e MpLab C18 27
Registrador RCON Os eventos que podem gerar reset no microcontrolador podem ser observados no registrador RCON. Os cinco bits menos significativos indicam que um evento específico de reset ocorreu. Na maioria dos casos, estes bits podem somente mudar de estado pelo evento e devem ser ajustados
pela aplicação após o evento. O registrador RCON também tem o bit de controle para ajustar a prioridade da interrupção (IPEN) e o
controle por software do BOR (SBOREN). RCON: Eventos de reset
28
Memória
Este microcontrolador possui três tipos de memória são elas: • Memória de Programa • Memória de Dados • Memória EEPROM
Como este dispositivo utiliza arquitetura Harvard, a memória de dados e a memória de programa utilizam
barramentos separados, isto permite o acesso as duas memórias ao mesmo tempo. A memória de dados EEPROM é práticamente um periférico, como tal é necessário configura-lo para
termos acesso a escrita ou leitura.
Memória de Programa - EEPROM Flash Os microcontroladores da família PIC18 da memória de programa executam um contador de programa
de 21 bits, que seja capaz de dirigir a um espaço de memória do programa 2Mbyte. Alcançar uma posição entre o limite superior da memória fisicamente executada e o endereço 2Mbyte retornará 0 (uma instrução de NOP). Os PIC18F2420 e os PIC18F4420 cada um têm 16 kbytes de memória flash e podem armazenar até 8.192 instruções single-word. Os PIC18F2520 e os PIC18F4520 cada um têm 32 kbytes da memória flash e podem armazenar até 16.384 instruções single-word. Os dispositivos PIC18 têm dois vetores de interrupção.
O endereço de vetor de reset está no endereço 0000h e os endereços dos vetores de interrupção são em 0008h e em 0018h. O mapa de memória do programa para os dispositivos PIC18F2420/2520/ 4420/4520 é mostrado em figura abaixo.
Figura 3.10 - Mapa de memória de programa.
Curso Módulo 4 –Família 18F e MpLab C18 29
Capítulo 4 - Capitulo 4 – Operação com 0a memória RAM
Criando Variáveis
Neste capitulo estudaremos a forma como o MPLAB C18, manipula a memória RAM, como criar variáveis e quais os tipos existentes.
Mapa da Memória RAM
Figura 4.1 – Mapa de memória de dados.
30
Bancos da Memória RAM
Figura 4.1 – Registradores especiais.
Tipos de Variáveis
Até agora estudamos o hardware do microcontrolador, como devemos selecionar bancos da memória RAM, a área de SFR e GPR.
Agora vamos estudar o compilador C18 e aprender como utilizar a memória RAM, como reservar espaço para nossos registradores, como utilizar a área de acess bank, como inicializar nossas variáveis e como classificar estas variáveis.
Vamos ao que interessa, estudaremos primeiro como criar variáveis e como inicializa-las.
Tipos e formato das Variáveis Para criar uma variável devemos primeiro ver quais os tipos disponíveis no compilador C utilizado. O compilador da Microchip segue o padrão ANSI, desta forma podemos classificar as variáveis em dois
tipos: inteiros e ponto flutuante. As variáveis do tipo inteiro podem ter de 8bits a 32bits. Desta forma podemos utilizar uma variável de tamanho adequado a nossa operação, o que permitira o
uso econômico da memória. Facilitando a criação de expressões matemáticas e lógicas, já que podemos escolher o tamanho da
variável. Observe na tabela que uma variável sinalizada divide o range de uma variável ao meio.
Curso Módulo 4 –Família 18F e MpLab C18 31
Já as variáveis do tipo ponto flutuante nos permite realizar operações com valores fracionários, o que é muito útil para realizarmos operações matemáticas com valores reais.
Variáveis do Tipo inteiro Vejamos quais são os tipos de variáveis do tipo inteiro que podemos criar.
Tabela 4.1 – Tipo de dados e tamanho
Tipo Tamanho Mínimo Máximo char(1,2) 8 bits -128 127
int 16 bits -32768 32767 short 16 bits -32768 32767 long 32 bits -2147483648 2147483647
Nota
1: Por padrão todos os tipos de variáveis são do tipo sinalizado. 2: Para que o tipo unsigned char, seja padrão devemos incluir na linha de comando a opção -k.
Variável do Tipo Ponto Flutuante O MPLAB C18, permite criar variáveis de 32 bits do tipo float (ponto flutuante). A Microchip criou um formato especifico para variáveis float, que foi adotado até a versão 2.30.01 do
MPLAB C18. Com a nova versão do compilador, o MPLAB C18 Student Edition – V 2.40, foi adotado o formara
simples da IEEE754. A biblioteca da versão estudante possui duas funções para conversão do formato float de Microchip ↔
IEEE754. Na tabela abaixo podemos ver o range deste tipo de variável.
Tabela 4.2 – Outros tipos de dados e tamanho
Tipo Tamanho Expoente mínimo Expoente máximo Valor mínimo normalizado
Valor máximo normalizado
float 32 bits -126 128 1,17549435e-30 6,80564693e+46
double 32 bits -126 128 1,17549435e-30 6,80564693e+46
Criando Variáveis e Inicializando Nossa primeira pergunta pode ser: Como defino a seleção de bancos da memória RAM? Vejamos
estamos utilizando uma linguagem de alto nível, portanto está não é uma preocupação do programador, o compilador fará a seleção de banco quando necessário.
Mesmo a seleção de banco sendo de responsabilidade do compilador, existe a possibilidade de definirmos o banco em que a variável será criada, estudaremos esta forma mais tarde.
Para criar uma variável devemos informar o seu tipo e o seu nome. Também é possível criar mais de uma variável do mesmo tipo, basta separar por virgula as variáveis
criadas. A forma de criar uma variável serve tanto para variável do tipo inteiro quanto para variável do tipo ponto
flutuante.
Sintaxe: Tipo nome; ou Tipo var_1, var_2, ... , var_n;
32
Exemplo: a) char contador; b) int tempo1, auxiliar1, auxiliar2;
Para facilitar a nossa programação, podemos criar a variável e inicializar ao mesmo tempo. Sintaxe: Tipo nome = valor; ou Tipo var_1 = valor_1, var_2 = valor_2, ... , var_n = valor_n; Exemplo: a) char contador = 10; b) int tempo1 = 0, auxiliar_1 = -20, auxiliar_2 = 10454;
Nomes de Variáveis • Você pode dar qualquer nome para as suas variáveis. Mas devemos seguir algumas regras: • O nome de uma variável de ser iniciada por letra, os demais caracteres pode ser: letras, números e o
sublinhado (_). • Não podemos utilizar palavras reservadas do compilador C e nem utilizar nome dada a funções e ou
rotinas. • Utilizar no máximo 32 caracteres, da para escrever nome e sobre nome da variável. • O compilador C é “case sensitive”, quer dizer que ele diferencia letra maiúscula de minúscula. • Utilize letra minúscula para variável e maiúscula para constante, está é uma boa prática de
programação.
Classificação do tipo de variável As variáveis do tipo inteiro ainda podem ser classificadas como sinalizadas (signed) e não sinalizadas
(unsigned). Quando criamos uma variável devemos informar a sua classificação. Por padrão toda variável criada é
do tipo sinalizada. Veja uma variável signed char pode assumir valores de -128 a 127. Para representar o valor negativo utilizamos o bit 7 como bit de sinal, Quando o bit 7 for 1 o valor
armazenado na variável é negativo, com o bit 7 igual a zero o valor é positivo. Na tabela abaixo temos o número 0 armazenado na variável teste do tipo unsigned char. unsigned char teste =0;
Variável teste 7 6 5 4 3 2 1 0 0 0 0 0 0 0 0 0
Em uma variável não sinalizada utilizamos todos os bits para representar o valor armazenado na
variável, já em uma variável sinalizado utilizamos somente os 7 bits menos significativos. Agora observe o número -128 armazenados na variável teste2 do tipo signed char. signed char teste2 = -128;
Variável teste2 7 6 5 4 3 2 1 0 1 0 0 0 0 0 0 0
Curso Módulo 4 –Família 18F e MpLab C18 33
Tabela 4.3 – Tipos de dados e tamalho.
Tipo Tamanho Mínimo Máximo char 8 bits -128 127
signed char 8 bits -128 127 unsigned char 8 bits 0 255
int 16 bits -32768 32767 unsigned int 16 bits 0 65535
short 16 bits -32768 32767 unsigned short 16 bits 0 65535
short long 24 bits -8388608 8388607 unsigned short long 24 bits 0 16777215
long 32 bits -2147483648 2147483647 unsigned long 32 bits 0 4294967295
Um outro tipo de variável que aparece em nossa tabela é a short long, que quer dizer “longo curto”, a
variável do tipo long é de 32bits, portanto para o C18, uma variável short long é de 24bits.
Variável Global e Local
Quando criamos uma variável, esta pode ser Global ou Local, isto depende do local onde está variável foi criado.
Variável Global Uma variável é Global quando a mesma é criada fora de qualquer tipo de função. E qual a vantagem
dela ser global? A vantagem está em podermos utilizar está variável em qualquer local de nosso programa e dentro de
qualquer função do programa. Mas a principal vantagem é que este tipo de variável não perde o último valor armazenado. No exemplo a seguir podemos ver uma variável global.
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Definição e inicialização das variáveis * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ //Neste bloco estão definidas as variáveis globais do programa. unsigned char CONTADOR = 0; unsigned char TEMPO = 10; /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Protótipo da função de Delay * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ void delay10TCYx(unsigned char tempo_x); /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Função Principal do Programa * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ void main () /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Loop principal * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ while(TRUE) for (CONTADOR = TEMPO;CONTADOR!=0; CONTADOR--) delay10TCYx(TEMPO);
34
Variável Local Para que uma variável seja definida como local ela deve ser criada dentro do bloco de programa de uma
função. Esta função pode ser a principal, de usuário ou uma função especifica do MPLAB C18. A característica marcante desta variável é que ela perde o seu dado ao final da execução da função
onde ela foi criada. Outra característica da variável local é que ela só é “vista” pela função onde ela foi criada. Isso nos permite criar variáveis locais em funções diferentes com o mesmo nome. Prática que eu não
utilizo em meus programas para evitar confusões durante a analise de um código. Fazendo o uso de variáveis locais podemos economizar memória RAM, além de permitir a portabilidade
de uma função. Isso quer dizer que você pode criar as suas funções e passar valores para essa função que serão
armazenados em uma variável local. A seguir seja um exemplo de como criar uma variável local.
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Protótipo da função de Delay * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ void delay10TCYx(unsigned char tempo_x); /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Função Principal do Programa * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ void main () unsigned char CONTADOR = 0, TEMPO = 10; // variáveis local /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Loop principal * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ while(TRUE) for (CONTADOR = TEMPO;CONTADOR!=0; CONTADOR--) delay10TCYx(TEMPO);
Constantes As constantes podem ser numéricas e de caractere, estas por sua vez não podem ser alteradas, em
tempo de processamento.
Constantes Numéricas As constantes numéricas são usadas para representar números inteiros, constantes hexadecimais,
binárias e octais. Uma constante hexadecimal é precedida por 0X ou 0x. Já uma constante octal é precedida por um zero. Um número binário é precedido por 0B ou 0b. Veja na tabela como podemos representar uma constante em sua respectiva base.
Tabela 4.4 – Representação de constantes numéricas
Tipo de constante Representação Inteiro (decimal) 35
Hexadecimal 0x23 Octal 043
Binário 0b00100011
Curso Módulo 4 –Família 18F e MpLab C18 35
Constantes de Caractere Em sistemas computadorizados, os caracteres são representados por valores numéricos, o sistema de
representação foi padronizado pelo uso do código ASCII(American Standard Code for Information Interchange).
Uma constante de caractere é representada por apostrofo, ‘A’, que equivale ao número 0x41. Um conjunto de caractere é chamado de string, uma string é representada por um conjunto de
caracteres entre aspas, veja o exemplo: “Grupo Mosaico” Esta string é formada por um conjunto de constantes e finalizada pelo caractere nulo, ‘\0’, representado
por suas constante teremos: 0x47, 0x72, 0x75, 0x70, 0x6F, 0x20, 0x4D, 0x6F, 0x73, 0x61, 0x69, 0x63, 0x6F, 0x00.
Na tabela ASCII existe um conjunto de caracteres que não permitem a sua impressão, são chamados de seqüência de escape ou código de barra invertida.
No apêndice C poderemos ver a tabela ASCII, mais o conjunto de caracteres de escape.
Classes de armazenamento
Os compiladores C do padrão ANSI possuem cinco classes de armazenamento, o MPLAB C18 suporta estas classes de armazenamento, são elas: auto, extern, register, static e typedef.
Overlay O compilador MPLAB C18 introduz uma classe do armazenamento chamado overlay. Esta classe de
armazenamento aplica-se somente quando o compilador esta operando no modo não extendido, (non-extended). Sua finalidade é economia de memória RAM.
Ela pode ser aplicada a variáveis locais, mas não aos parâmetros formais de uma função, a tipo de função ou a variáveis globais.
A classe de armazenamento overlay alocará os símbolos associados em uma função-específica, e serão do tipo static overlay. Esta variável será alocada e inicializada a cada entrada da função.
Veja o exemplo: void f (void)
overlay int x = 5; x++;
A variável x será inicializada com cinco a cada entrada da função, embora seu armazenamento seja
alocado estaticamente. Caso a variável não seja inicializada, o valor da mesma será indefinido. Se o linker do MPLINK detectar uma função recursiva que contenha uma variável local de classe de
armazenamento overlay, o compilador irá gerar uma mensagem de erro.
Auto Uma variável auto tem o seu endereço da memória RAM definido quando o bloco de programa onde ela
foi criada é executado. Ao final da execução do bloco de programa onde a variável foi criada, o espaço reservado da memória RAM é liberado para que outra variável auto a utilize. Usado para criar variáveis locais.
Este tipo de classe será utilizado quando desejarmos economizar memória RAM, pois a uma grande chance de variáveis da classe auto, ocuparem o mesmo endereço de memória em momentos distintos, evitando a sobre carga.
Quando uma variável de classe auto não é inicializada, ela pode assumir qualquer valor. Sintaxe: auto tipo variável;
Static Uma variável do de classe de armazenamento static, tem a sua posição na memória RAM definida em
tempo de compilação, ou seja, está variável utilizará sempre o mesmo endereço. Será usado para definir uma variável como global.
36
Como uma variável static possui sempre o mesmo endereço, a quantidade de RAM usada para este tipo de classe depende do tipo da variável e da quantidade de variáveis criadas.
Uma variável static deve ser inicializada, isto ocorre em tempo de compilação, caso não o seja, a variável será iniciada com zero.
Este tipo de classe é válido somente com o MPLAB C18 no modo não estendido. Sintaxe: static tipo variável;
Extern Esta classe de armazenamento define que a variável utilizada encontra-se em outro programa, ou seja, é
uma variável externa. Podemos utilizar esta classe de armazenamento para unir diversos módulos de programas, sejam eles grandes e ou complexos, através do MpLink do MPLAB C18. Desta forma é possível utilizar em uma rotina ou função do seu programa principal uma variável de um dos módulos externos de programa.
Sintaxe: extern tipo variável;
Register No padrão ANSI este tipo de classe utiliza registradores da CPU para armazenar dados e acessa-los de
forma mais rápida. Nos microcontroladores os dados já são armazenados em registradores. Sintaxe: register tipo variável;
Typedef Sua finalidade é de redefinir o tipo de dado, o que este comando faz é criar um novo tipo de dado em
função de um tipo já existente. Sintaxe: typedef tipo novo_tipo;
Qualificador de armazenamento
O compilador MPLAB C18 pode trabalhar com os qualificadores de armazenamento do padrão ANSI são eles, const e volatile, também chamados de modificadores de acesso.
Const Modifica o acesso a uma variável, tratando a como uma constante. A diferença fica na forma de armazenamento, uma const fica armazenada na memória de programa,
“não podendo” ser modificada. A finalidade deste qualificador é proteger a variável contra modificações do seu conteúdo. Volatile Este qualificador informa ao compilador que a variável qualificada como volatile, pode ter o seu conteúdo
alterado sem prévio aviso. Imagine que o sistema micro – controlado desenvolvido por você possui um sistema operacional, que
este sistema possui um RTC, as variáveis de atualização do relógio podem ser qualificadas como volatile. Enquanto o seu sistema executa o programa do usuário, o sistema operacional pode atualizar as
variáveis do relógio sem causar nenhum tipo de problema ao sistema, ou seja, são variáveis que podem ser acessadas pelo sistema operacional e pelo programa do usuário.
Qualificadores near e far
Além dos qualificadores const e volatile, o compilador MPLAB C18 introduz dois novos qualificadores de armazenamento far, near, que qualificam o uso da rom e ram.
A condição padrão quando um qualificador não é definido são far e ram.
Curso Módulo 4 –Família 18F e MpLab C18 37
Tabela 4.4 – Posição do objeto baseado nos qualificadores de armazenamento.
Qualificador ROM (memória de programa) RAM (memória de dados) far Em qualquer lugar na memória de programa Em qualquer lugar na memória RAM
near Na memória de programa até o endereço 64K Na área de ACESS BANK
near/far – Memória de Dados O qualificador far é usado para deixar explicito que a variável está em uma área de RAM onde existe a
necessidade de seleção de banco para acessa-la. Já o qualificador near deixa claro que a variável faz parte da área de acess bank da memória RAM.
near/far – Memória de Programa O qualificador far é usado para denotar que uma variável situada na memória do programa pode ser
encontrada em qualquer lugar na memória do programa, através de um ponteiro, que pode alcançar até e ou além de 64k de espaço de memória do programa.
O qualificador near é usado para denotar que uma variável alocada na memória de programa pode ser acessada por um ponteiro, com capacidade de endereçamento de 64k.
Qualificadores ram e rom Os qualificadores ram e rom se fazem necessários devido à divisão interna do barramento de dados, nos
microcontroladores da Microchip temos um barramento para a memória RAM e outro para a memória ROM. Visto que para acessar a variável podemos fazer uso de ponteiros, devemos utilizar os qualificadores
rom e ram, para definir o tipo de ponteiro que será utilizado. O tamanho de um ponteiro depende do tipo de ponteiro declarado. Observe que ao escrever uma variável na ROM, o compilador usa uma instrução de TBLWT.
Tabela 4.5 – Uso dos qualificadores ROM
Tipo de Ponteiro Exemplo Tamanho Ponteiro para memória RAM char *ponteiro; 16 bits Near – memória de programa rom near char *ponteiro 16 bits Far – memória de programa rom far char *ponteiro 24 bits
38
Capítulo 5 - Operações Lógicas e Aritméticas
Hardware de multiplicação de 8X8
Os microcontroladores da família 18 possuem um hardware interno para operação de multiplicação de 8 X 8 bits, com resultado de 16 bits.
A operação é realizada em apenas um ciclo de máquina, ela não afeta nenhum flag da ULA. O resultado da multiplicação é armazenado em dois registradores de função especial PRODH e PRODL. O MPLAB C18 por suas vez faz uso deste hardware para implementar as rotinas aritméticas de
multiplicação, desta forma ele economiza, memória de programa e memória RAM.
Figura 5.1 – Multiplicador 8 x 8
Operadores
Os operadores são utilizados para realização de operações lógicas ou aritméticas. Com eles poderemos montar expressões, com variáveis inteiras e do tipo float.
O MPLAB C18 irá utilizar os recursos da ULA e do hardware de multiplicação para otimização do código. Porém devemos sempre tomar cuidado com o tamanho em bits, do resultado de uma operação, e ter
certeza que o tipo de variável escolhido pode receber este resultado. Vejamos os operadores disponíveis para nossas operações.
Operadores Aritméticos
Estudaremos agora os operadores aritméticos, estes nos permitirão criar expressões matemáticas simples e complexas, com variáveis inteiras e fracionárias.
Teremos uma versatilidade ainda maior, quando utilizarmos estes operadores com as funções aritméticas do MPLAB C18.
Atribuição O operador de atribuição é utilizado para passar um valor a uma variável. O símbolo utilizado é o sinal de igual “=”. Exemplo: int A, B; A = 10; // A variável foi carregada com a constante 10. B = 20; // A variável foi carregada com a constante 20.
Curso Módulo 4 –Família 18F e MpLab C18 39
Soma O operador “soma” tem como símbolo o sinal de mais “+” e obviamente a operação realizada é de
somar. Exemplo: unsigned char X = 15, Y = 34, A; A = X + Y; // “A” será igual a soma de “X” e “Y” “A” será igual a 49.
Subtração Nas operações de subtração utilizaremos o sinal de menos “-“, entre as variáveis ou expressões. Exemplo: unsigned char X = 55, Y = 14, A; A = X - Y; // “A” será igual a “X” menos “Y” “A” será igual a 41.
Multiplicação Para representar a operação de multiplicação utilizaremos o símbolo “*”. Exemplo: unsigned int X = 15, Y = 34, A; X = X * Y; // “A” será a “X” vezes “Y” “X” será igual a 510.
Divisão A divisão entre variáveis e expressões é realizada utilizando como símbolo a barra invertida “/”. Quando
esta operação é realizada por variáveis inteiras, teremos como resposta somente o Quociente, para resgatar o resto da divisão devemos utilizar operador “%”.
Exemplo: unsigned char X = 5, Y = 2, A; A = X / Y; // “A” será igual a “X” dividido por “Y” “A“ será igual a 2.
Se utilizarmos uma variável do tipo float resultado da divisão será: unsigned float X = 5, Y = 2, A; A = X / Y; // “A” será igual a “X” dividido por “Y” “A“ será igual a 2,5.
A operação realizada utilizou no total 12 bytes, cada variável float é formada por 4 bytes.
Resto da Divisão Quando realizamos uma operação com variáveis do tipo inteiro, o resultado da divisão não retorna o
resto. Para retornar o resto de uma divisão utilizaremos o símbolo de porcentagem “%”. Veja como podemos realizar a operação e economizar memória RAM. Exemplo:
unsigned char X = 5, Y = 2, A, B; A = X / Y; // “A” será igual a “X” dividido por “Y” “A“ será igual a 2. B = X % Y; // “B” será igual ao resto da divisão de “X” por “Y” “B“ será igual a 1.
40
Podemos escrever esta operação da seguinte forma:
B = ((X % Y)*10) / Y; Teremos como resposta “B” igual a 5. Os parênteses definem a ordem da execução das operações.
Incremento e Decremento Quando queremos adicionar ou subtrair uma unidade de uma variável, em linguagem C podemos utilizar
um recurso bem interessante que são os operadores de incremento e decremento. Estes operadores são classificados de duas maneiras pré e pós. Vejamos como funciona.
Pré-incremento Uma variável pode ser pré-incrementada, ou seja, antes de você realizar qualquer tipo de operação, sua
variável será acrescida de uma unidade. Exemplo: unsigned char X = 10, Y = 20, Z = 30; ++X; // “X” será igual a 11. Z = X + ++Y;
Antes da soma de X com Y, a variável Y será incrementada(pré-incremento). O resultado final será:
X = 11, Y = 21 e Z = 32.
Pós-incremento Uma variável pode ser pós-incrementada, ou seja, após a realização de uma operação, a variável será
acrescida de uma unidade. Exemplo:
unsigned char X = 10, Y = 20, Z = 30; X++; // “X” será igual a 11. Z = X + Y++;
Após a soma de X com Y, a variável Y será incrementada (pós-incremento). No momento da soma teremos:
X = 11, Y = 20 e Z = 31.
O resultado final será:
X = 11, Y = 21 e Z = 31.
Pré-decremento Uma variável pode ser pré-decrementada, ou seja, antes de você realizar qualquer tipo de operação, a
variável será subtraída de uma unidade. Exemplo
unsigned char X = 10, Y = 20, Z = 30; --X; // “X” será igual a 9. Z = X + --Y;
Antes da soma de X com Y, a variável Y será decrementada (pré-decremento). O resultado final será:
X = 9, Y = 19 e Z = 28.
Curso Módulo 4 –Família 18F e MpLab C18 41
Pós-decremento Uma variável pode ser pós-decrementada, ou seja, após a realização de uma operação, a variável será
subtraída de uma unidade. Exemplo:
unsigned char X = 10, Y = 20, Z = 30; X--; // “X” será igual a 9. Z = X + Y--;
Após a soma de X com Y, a variável Y será incrementada(pós-incremento). No momento da soma teremos: X = 11, Y = 20 e Z = 31.
O resultado final será: X = 11, Y = 21 e Z = 31.
Operadores Relacionais
Os operadores relacionais referem-se as relações de comparação que podem ser usadas entre variáveis e expressões.
Uma operação relacional retorna como resposta, verdadeiro ou falso. Qualquer resultado de uma operação relacional será “zero” para falso e “um” para verdadeiro. Na
linguagem C podemos assumir que todo resultado diferente de zero é verdadeiro e todo resultado igual a zero é falso.
Iremos utilizar estes operadores com os comando de controle de Fluxo e controle Laço, para auxiliar na tomada de decisão. Por eles retornarem verdadeiro ou falso como resposta.
Para representar os operadores relacionais são utilizados os símbolos de comparação, são eles:
Operador Descrição == Comparação != Diferente de > Maior que < Menor que
>= Maior ou igual a <= Menor ou igual a
Exemplo: unsigned char contador; void main (void) contador = 1; // carrega contador com 1 TRISB = 0; //configura PORTB como saída while (contador <= 15) // O contador é menor ou igual a 15? // Sim, PORTB = contador; // carrega o PORTB com o valor da variável contador contador++; // incrementa o contador. // Não, fim do laço volta para inicio da função void main
42
Operadores Lógicos
Os operadores lógicos nos permitirão elaborar expressões de lógica bit a bit e expressões booleanas. Utilizando os operadores lógicos e os operadores relacionais, poderemos implementar expressões
lógicas de tomada de decisão complexas.
Operador Lógico Booleano Os operadores booleanos são utilizados para relacionarmos variáveis expressões ou até mesmo
entradas e saídas digitais. Este tipo de operador também retorna como resultado, verdadeiro ou falso. São três os operadores relacionais: Operador E “&&”, Operador OU “||” e Operador Negação “!“.
Operador Descrição && Operador “E” relacional (AND) || Operador “OU” relacional (OR) ! Operador Negação
Operador Lógico bit a bit Os operadores bit a bit são utilizados para realizar operações lógicas entre variáveis, eles operam
somente com variáveis do tipo inteiro.
Operador Descrição & Operador E | Operador OU ^ Operador OU EXCLUSIVO
<< Operador de rotação à esquerda >> Operador de rotação à direita ~ Complemento de um número
Expressões
Uma expressão em C, representa a associação de variáveis, chamadas de funções e constantes, agrupadas por meio de operadores e separados por parênteses, da mesma forma como estamos acostumados na matemática.
Estas expressões serão avaliadas e nos retornarão valores que dependem do tipo de operador utilizado, poderemos ter como resposta um valor qualquer ou retornarão verdadeiro ou falso.
Abreviando Expressões A linguagem C nos permite simplificar algumas expressões lógicas e aritméticas. Esta simplificação não
quer dizer que facilitará o entendimento de um programa, e sim que deveremos ter um cuidado maior ainda. Portanto para que não tenhamos surpresas desagradáveis sem que montar uma expressão faça o uso
dos parênteses, a ordem de execução da expressão ficará clara e dessa forma facilitará o entendimento.
Figura 5.1 – Tabela de expressões simplificadas
Formato original Formato equivalente A=A+B; A+=B; A=A-B; A-=B; A=A*B; A*=B;
A=A/B; A/=B; A=A%B; A%=B; A=A>>B; A>>=B; A=A<<B; A<<=B; A=A&B; A&=B; A=A|B; A|=B;
A=A^B; A^=B;
Curso Módulo 4 –Família 18F e MpLab C18 43
Operador virgula “,” Este tipo de operador permite agrupar um conjunto de expressões que serão executadas
seqüencialmente. Veja o exemplo: x=(y=2,y+3); No exemplo acima teremos como retorno o valor calculado da última expressão, ou seja, a expressão
mais à direita. Portanto no nosso exemplo teremos como resposta x = 5. Observe, y será igualado ao valor 2, depois será somado a constante 3 com o valor de y, desta forma
teremos como resposta x = 5. Não existe um limite quanto ao encadeamento de expressões, devemos sempre tomar cuidado para que
a expressão fique mais clara possível, a fim de facilitar, as modificações futuras que se fizerem necessárias. O exemplo a seguir mostra um outro uso para o operador “virgula”, dentro de um for: int main() int x, y;
for(x=0 , y=0 ; x+y < 100 ; ++x , y++) PORTB = x + y;
/* No laço for, temos duas variáveis de controle, x e y. As duas variáveis foram inicializadas com zero a cada execução do laço estas variáveis são incrementadas e a soma das duas variáveis, será atribuída ao PORTB */
Precedência dos operadores Esta é a tabela de precedência dos operadores da linguagem C. Entende-se por precedência como
sendo a ordem como a expressão será executada, seguinte do operador de maior precedência até o operador de menor precedência. Como já foi dito, para que não tenhas problemas na montagem de expressões procure sempre separa – las, utilizando os parênteses que possuem maior precedência.
Operador Precedência ( ) [ ] → Maior
! ~ ++ -- . -(unário) (cast) *(unário) &(unário) sizeof * / % + -
<< >> <<= >>=
== != & ^ |
&& || ?
= += -= *= /= , Menor
44
Modelador (Cast) A finalidade do modelador é mudar o tipo de variável, de uma expressão temporariamente. Desta forma
o resultado da expressão virá formatado para o tipo de variável do modelador. Sintaxe: (tipo) expressão; Veja o exemplo: Sem o uso do modelador:
char X = 5, Y = 2; float Z; Z = X / Y;
Veja as variáveis X e Y são do tipo char, portanto de 8 bits, desta forma não é possível, que a operação
retorne um valor fracionário. Deste jeito teremos a variável Z igual a 2. Aplicando o modelador, teremos em Z outro resultado, veja:
char X = 5, Y = 2; float Z; Z = (float) X / Y;
Aplicando o modelador, as variáveis X e Y, tiveram o seu tipo alterado para float, sendo assim o
resultado da operação será Z igual a 2,5. Observe que durante a execução da operação tivemos um aumento no consumo de posições de
memória RAM. Somente ao final da execução as posições de RAM adicionais foram liberadas.
Comando Um comando é qualquer expressão válida finalizada por ponto e virgula “;”. Exemplo: A = 10; A = A + 76; Podemos ter um comando composto ou bloco de comando como também é chamado, é formado por
comandos simples que se encontram entre chaves . Utilizaremos os comandos associados aos comandos de controle de fluxo e comando de lanço da
linguagem C. Exemplo: a) // aqui você colocará os comandos simples. b) while (exp) // comando de laço. // aqui você colocará os comandos simples.
Curso Módulo 4 –Família 18F e MpLab C18 45
Capítulo 6 - Comandos da Linguagem C
Comandos
Os comandos determinam o fluxo de processamento ou controle de laço (execução de uma rotina repetidamente), estes comandos podem ser utilizados varias vezes em um programa.
Isso depende do tipo de comando utilizado. Um comando pode ser a execução de uma operação lógica ou aritmética ou a chamada de uma função.
Podemos executar outros comandos de controle da linguagem C dentro
Controle do Fluxo de Processamento
Freqüentemente em um programa precisamos tomar decisões, que afetarão o fluxo do processamento. Essas decisões determinam se iremos executar um determinado comando ou não.
Podemos até escolher entre executar um comando ou um bloco de comando. Para controlar o fluxo de processamento temos dois comandos, o comando if e o comando switch. Vejamos como eles se comportam.
Comando If – simplificado Este comando verifica se o resultado de uma expressão é verdadeiro ou falso. O comando if significa
“se”. Se o resultado de uma expressão for verdadeiro ele executa um comando ou um bloco de comando, em um bloco de comando, os comando a serem executados estarão entre chaves.
A exp é verdadeira? NÃOSIM
Executa o Comando
Fluxo deProcessamento
Fluxo deProcessamento
Segue o fluxo de Processamentoe não executa o comando.
Figura 6.1 – Fluxograma do comando if
Sintaxe: a) Executa apenas um comando, se a expressão for verdadeira.
if(expressão) comando; // Se a expressão for verdadeira, execute o comando. // Se não segue o fluxo do programa sem executar o comando. b) Executa mais de um comando, se a expressão for verdadeira.
if(expressão) // A expressão é verdadeira? // Sim, executa o bloco de comando.
// Bloco de comandos // Não, salta o bloco de comando e segue o
// fluxo do programa.
46
Comando If – else simplificado Quando desejamos mais de uma alternativa de comando a ser executado, utilizamos o comando if-else.
Ele permite executar um comando ou bloco de comando se a expressão for verdadeira ou falsa. Caso a expressão seja verdadeira ele executa um comando ou se a expressão for falsa, executa outro
comando.
A exp é verdadeira? NÃOSIM
Executa o Comando A
Fluxo deProcessamento
Fluxo deProcessamento
Quando o resultado da expressãofor falso, será executado estebloco do comando IF
Executa o Comando B
Quando o resultado da expressãofor verdadeiro, será executadoeste bloco do comando IF
Figura 6.2 – Fluxograma do comando if-else
Sintaxe: a) Executa apenas um comando, se a expressão for verdadeira.
if(expressão) comando_A; // Se a expressão for verdadeira, execute o comando_A. else comando_B; // Se não for verdadeira, executa o comando_B. b) Executa mais de um comando, se a expressão for verdadeira.
if(expressão) // A expressão é verdadeira? // Sim, executa o bloco de comando. // Bloco de comandos // Não, salta o bloco de comando e segue o
// fluxo do programa.
else // Sim, executa o bloco de comando. // Bloco de comandos // Não, salta o bloco de comando e segue o
// fluxo do programa.
Comando If e if – else Composto O comando a ser executado pode ser uma expressão matemática, uma expressão lógica, pode ser a
execução de uma função ou pode ser executado outro comando if que também terá um comando a ser executado.
Podemos utilizar os outros comando que serão aprendidos no bloco de comando, aumentando assim as possibilidades de controle do programa.
Podemos fazer associações de comandos if para uma lógica mais complexa. Veja outros exemplos de escrita do comando if. Observação: Entre chaves podemos ter quantos comandos desejarmos e o compilador permitir.
Curso Módulo 4 –Família 18F e MpLab C18 47
Exemplos: A)
A exp_1 éverdadeira?
NÃOSIM
Fluxo deProcessamento
Fluxo deProcessamento
Segue o fluxo de Processamentoe não executa o comando.
A exp_2 éverdadeira?
NÃOSIM
Executa o Comando ouBloco de Comando
Quando o resultado da expressãofor verdadeiro, será executadoeste bloco do comando IF
Quando o resultado desta segundaexpressão for verdadeiro, seráexecutado este bloco do comando IF
Bloco de Comando doPrimeiro IF
Figura 6.3 – Fluxograma do comando if-else composto
Sintaxe: if(exp_1) // A expressão 1 é verdadeira? // Sim, verifica a expressão 2. if(exp_2) comando; // A expressão 2 é verdadeira? // Sim, executa o comando. // Não, expressão 1 ou expressão 2 são falsas
48
B)
A exp_1 éverdadeira?
NÃO
SIM
Fluxo de Processamento
Fluxo deProcessamento
A exp_2 éverdadeira?
NÃO
SIM
Executa o Comando ouBloco de Comando
Quando o resultado da exp_1 for verdadeiro, será executado este bloco de comando
Quando o resultado desta exp_2 for verdadeiro, será executado este bloco de comando
Executa o Comando_B
Executa o Comando_A
Para que o comando_B seja executado, basta que a exp_1 seja verdadeira.
Segue o fluxo de processamentoe não executa o comando.
Figura 6.4 – Fluxograma do comando if-else composto
Sintaxe: if(exp_1) // A expressão 1 é verdadeira?
// Sim, executa o Comando_A e verifica a Comando_A; // a expressão 2. if(exp_2) comando; // A exp 2 é verdadeira? Sim, executa o comando
// e depois o Comando_B Comando_B; // Não, então executa somente o
// Comando_B // Não, a expressão 1 é falsa
Curso Módulo 4 –Família 18F e MpLab C18 49
C)
A exp_1 éverdadeira?
NÃOSIM
Fluxo deProcessamento
Fluxo deProcessamento
A exp_2 éverdadeira?
NÃO
SIM
Executa o Comando ouBloco de Comando
Se a exp_1 for verdadeira,executa este bloco de comando
Se a exp_2 for verdadeira, seráexecutado este bloco de comando
Executa o Comando_B
A exp_3 éverdadeira?
Executa o Comando ouBloco de Comando
Executa o Comando_D
Se a exp_3 for verdadeiro, seráexecutado este bloco de comando
Executa o Comando_A Executa o Comando_C
Se a exp_1 for verdadeira,executa este bloco de comando
Para que o comando_B sejaexecutado, basta que a exp_1seja verdadeira.
Para que o comando_B sejaexecutado, basta que a exp_1seja verdadeira.
Figura 6.5 – Fluxograma do comando if-else composto
Sintaxe: if(exp_1) // A expressão 1 é verdadeira?
// Sim, executa o Comando_A e verifica a Comando_A; // a expressão 2. if(exp_2) comando; // A exp 2 é verdadeira? Sim, executa o comando
// e depois o Comando_B Comando_B; // Não, então executa somente o
// Comando_B // Não, a expressão 1 é falsa else
// Sim, executa o Comando_C e verifica a Comando_C; // a expressão 2. if(exp_3) comando; // A exp 2 é verdadeira? Sim, executa o comando
// e depois o Comando_D Comando_D; // Não, então executa somente o
// Comando_D // FIM
50
Comando Switch Em alguns casos encontraremos a necessidade de testar uma variável muitas vezes, a fim de tomar uma
decisão em funções do seu valor. Uma solução para este problema é utilizar o comando if aninhado, em contra partida o seu código ficará
maior e pouco elegante. Então como resolveremos este problema? A solução é utilizarmos o comando switch, este realizará o teste de forma eficiente e mais rápido e
tornará o programa mais legível. Como ele funciona então? Este comando avalia o valor de uma variável e salta para um label, conhecido
como case e executa o bloco de comando, este é finalizado pelo comando break. Cada case é acompanhado de uma constante ou expressão de constante, nunca uma variável, esta
constante define qual case será executado. Em uma avaliação do comando switch, pode acontecer de nenhum case, ter uma constante que
corresponda ao valor da variável comparada, neste caso será executado o bloco de comando do label default.
A sintaxe do comando switch ficará desta forma:
A variável é igual acontante_1?
NÃO
SIM
Fluxo deProcessamento
Segue o fluxo doProcessamento
A variável é igual acontante_2?
NÃO
Executa o Comando ouBloco de Comando
Executa o comando oubloco de comando
A variável é igual acontante_n?
Executa o Comando ouBloco de Comando
NÃO
SIM
SIM
Executa o Comando ouBloco de Comando
Bloco da condiçãodefault
Figura 6.6 – Fluxograma do comando switch
Curso Módulo 4 –Família 18F e MpLab C18 51
Sintaxe: switch(variavel) // Verifica se a variável é igual a constante de um
// dos “cases”
case constante_1: // bloco de comando break; // Fim de execução do SWITCH case constante_2: // bloco de comando break; // Fim de execução do SWITCH case constante_n: // bloco de comando break; // Fim de execução do SWITCH default: // bloco de comando
Podemos ter o comando switch sem a cláusula default, veja a sintaxe do comando.
A variável é igual acontante_1?
NÃO
SIM
Fluxo deProcessamento
Segue o fluxo doProcessamento
A variável é igual acontante_2?
NÃO
Executa o Comando ouBloco de Comando
Executa o comando oubloco de comando
A variável é igual acontante_n?
Executa o Comando ouBloco de Comando
NÃO
SIM
SIM
Figura 6.7 – Fluxograma do comando switch
52
Sintaxe: switch(variavel) // Verifica se a variável é igual a constante de um
// dos “cases”
case constante_1: // bloco de comando break; // Fim de execução do SWITCH case constante_2: // bloco de comando break; // Fim de execução do SWITCH case constante_n: // bloco de comando break; // Fim de execução do SWITCH
No bloco de comandos você pode utilizar qualquer comando de controle de fluxo ou de faço. Portanto
você pode fazer um switch dentro de um bloco de comando de um case, isto aumenta as possibilidades e a complexidade dos nossos programas.
Veja mais um exemplo de construções do comando switch. Exemplos: A) Pode existir uma situação que desejamos executar mais de um case. Veja como pode ser resolvido
este problema.
A variável é igual a1?
NÃO
SIM
Fluxo deProcessamento
Segue o fluxo doProcessamento
A variável é igual a2?
NÃO
Executa o Comando ouBloco de Comando
Executa o comando oubloco de comando
A variável é igual a3?
Executa o Comando ouBloco de Comando
NÃO
SIM
SIM
No final deste Bloco de Comandonão existe a cláusula Break
Figura 6.8 – Fluxograma do comando SWITCH
Curso Módulo 4 –Família 18F e MpLab C18 53
Sintaxe: switch(variavel) // Verifica se a variável é igual a constante de um
// dos “cases”
case 1: // bloco de comando // Ao final da execução desse bloco // o case 2 é imediatamente executado case 2: // bloco de comando break; // Fim de execução do SWITCH case 3: // bloco de comando break; // Fim de execução do SWITCH
Observe que no exemplo anterior não existe a cláusula break no final do bloco de comando do case 1,
desta forma ao final da execução deste bloco o case 2 será executado. Desta forma se a variável for igual a 1 o case 1 e case 2 serão executados. Para a variável igual a 2,
somente o case 2 será executado e para a variável igual a 3, somente o case 3 será executado.
Comandos para Controle de Repetição (Loop)
Em um programa escrito em C, podemos ter a necessidade de ter dentro da função principal um laço infinito, que chamaremos de rotina principal.
Muitas vezes desejamos executar uma certa rotina mais de uma vez, ou para gerar um atraso (delay), ou para que uma tarefa seja concluída. O que estamos querendo fazer é o que chamamos de loop, um laço de repetição.
Um loop é formado por um comando ou um conjunto de comando que será executado de forma seqüencial, tantas vezes quanto se fizer necessário.
Um loop pode ter a sua execução de forma finita ou infinita. Para finalizar a execução do laço, o comando de laço deve realizar um teste, que retornará como
resultado, verdadeiro ou falso. Este teste avalia se o conteúdo de uma variável ou o resultado de uma expressão é verdadeiro, se sim o
laço continua sendo executado, caso seja falso a execução do laço é finalizada. Todo laço pode ter sua execução infinita, basta que a expressão retorne sempre verdadeiro.
A seguir veremos os comandos de laço disponíveis para elaboração de nossos programas.
Comando While O comando while quer dizer “Enquanto for verdadeiro, Faça”. Este comando primeiro avalia uma expressão ou variável, como verdadeiro ou falso. O bloco de comando somente será executado se o resultado da avaliação for verdadeiro. A finalidade da avaliação da expressão ou variável é determinar a quantidade de vezes que o bloco de
comando será executado. A sintaxe para o comando while pode ser vista a seguir bem como o seu fluxograma.
54
A exp é verdadeira?NÃO
SIM
Fluxo deProcessamento
Segue o fluxo doProcessamento
Executa o Comando ouBloco de Comando
Retorno do Laço
Fim da execução do Laço WHILE
Figura 6.9 – Fluxograma do comando while
Sintaxe: while(variavel ou expressão) // O resultado da avaliação é verdadeira? // Sim, executa o bloco de comando // comando ou bloco de comando // Se a avaliação for verdadeira volta para
// nova avaliação. Se não, fim da execução
Podemos ter um laço infinito inserindo no corpo de avaliação do comando a constante 1. Qualquer número diferente de zero é verdadeiro, portanto o laço nunca será finalizado. A sintaxe ficará assim:
while(1) // comando ou bloco de comando
Comando Do-while O comando do-while que dizer “faça enquanto for verdadeiro”. Assim como o comando while, este comando também avalia uma expressão ou variável para poder
executar o bloco de comando do laço. Porém executa-se primeiro o bloco de comando, depois o teste de validação do laço. Portanto quando
desejamos executar o bloco de comando de um laço pelo menos uma única vez, iremos utilizar este comando.
Com um resultado verdadeiro o bloco de comando é executado novamente, com um resultado falso o laço é finalizado.
Observe no fluxograma a seqüência de execução do comando.
Curso Módulo 4 –Família 18F e MpLab C18 55
A exp é verdadeira?
NÃO
SIM
Fluxo deProcessamento
Segue o fluxo doProcessamento
Retorno do Laço
Fim da execuçãodo Laço WHILE
Executa o Comando ouBloco de Comando
Primeiro executa o bloco de comando,depois testa a expressão
Figura 6.10 – Fluxograma do comando do-while
Sintaxe: do // Executa o bloco de comando
// comando ou bloco de comando while(variável ou expressão); // A avaliação é verdadeira? // Sim, volta para executar o bloco de comando. // Não, fim de execução do laço.
Comando For O comando for, em execução é semelhando ao comando while. O laço for somente será executado se o teste da variável de controle retornar verdadeiro. Quanto o laço for é executado pela primeira vez, a variável de controle é inicializada e em seguida
testada, se for verdadeiro executa o bloco de comando, e retorna para testar a variável novamente. Se for falso finaliza a execução do comando for. Sendo assim para que o bloco de comando seja executado pelo menos uma vez, a avaliação da variável
deve ser verdadeira. Veja o fluxograma deste comando e a sintaxe a seguir.
56
A variável éverdadeira?
NÃO
SIM
Fluxo deProcessamento
Segue o fluxo deProcessamento
Retorno do Laço
Fim da execução doLaço For
(Comando do Laço For)Inicializa a variável
Primeiro inicializa a variável depoisexecuta o bloco de comando.
Executa o comando oubloco de comando
(Comando do Laço For)Incrementa a variável
Figura 6.11 – Fluxograma do comando FOR
Sintaxe:
for(inicialização; condição; incremento) comando; ou for(inicialização; condição; incremento) // bloco de comando;
Parâmetros do comando For - Inicialização: É um comando de atribuição para inicilalizar uma variável de controle do laço. - Condição: É um comando para testar a variável de controle do laço, ela define quanto o laço será finalizado. - Incremento: Define como a variável de controle do laço será alterada, este campo pode conter uma expressão para definir a forma de atualização desta variável.
Cláusulas para controle dos comandos de laço Quando estamos em um loop finito ou infinito, podemos ter a necessidade de interromper a execução do
mesmo ou executar apenas uma parte do bloco de comando. Conseguiremos este controle utilizando as cláusulas break e continue.
Cláusula Continue A função desta cláusula é forçar a verificação da condição, que permite que o bloco de comando, do laço
de repetição seja executado. Com isso você pode executar apenas uma parte do bloco de comando. Podemos realizar um teste
condicional, para executar a cláusula continue, desta forma poderemos controlar o que será executado no bloco de comando.
Curso Módulo 4 –Família 18F e MpLab C18 57
Veja um exemplo de aplicação da clausula continue: contador = 100; while (contador) contador--; // decrementa o contador : : // executa os seus comandos : if (!botão_1) continue; // Entrada igual a zero?
// Sim, volta para testar a variável contador : // Não, executa os seus comandos :
Cláusula Break A função desta cláusula é forçar o fim da execução do comando de laço, ele tem prioridade sobre a
verificação da condição, que permite que o bloco de comando do laço de repetição seja executado. Desta forma um laço infinito pode ter a sua execução cancelada, ou um laço que deve ser executado n
vezes pode ser finalizado antes de atingir a quantidade de vezes definida. Para cancelar a execução do laço basta executar a clausula break, portanto para executar está clausula,
podemos realizar um teste condicional, que permitirá a execução ou não da clausula. Veja um exemplo de aplicação da clausula break.
while (contador > 10) contador++; : : // executa os seus comandos : if (!botão_1) break; // Entrada igual a zero?
// Sim, fim da execução do laço. : // Não, executa os comandos : // e volta para testar a variável
Comando Goto O comando goto é utilizado para provocar um salto incondicional. Neste caso utilizamos o comando para
desviar para rotinas dentro de uma função que pode ser a função principal (main) ou uma função criada pelo programador.
Procure não utilizar este comando ele pode dificultar o compilador à otimização do código de preferência trabalhe com funções.
A operação deste comando é saltar para um local especifico. Este local é identificado através de um rótulo (label).
O nome dado ao rótulo, geralmente é o nome da rotina que vamos escrever, este nome para o compilador C, corresponde ao endereço da memória de programa onde está localizada a nossa rotina.
Sintaxe: nome_da_rotina: // rotina goto nome_do_rotina; Exemplo: /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Definição e inicialização das variáveis * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ // Variáveis Globais int unidade = 9; int dezena = 9;
58
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Função de incremento do Timer * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ void incrementa_timer() unidade ++; if (unidade == 10) unidade = 0; dezena ++; if (dezena == 10) dezena = 0; centena ++; /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Loop principal * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ loop: void main() while(1) // Laço infinito. if (!PORTBBits.RB0)// A entrada está em zero? goto trata_bt // desvia para a rotina trata_bt. trata_up: incrementa_timer(); // incrementa o timer. goto loop;
Curso Módulo 4 –Família 18F e MpLab C18 59
Capítulo 7 - Funções
Funções
Antes de falarmos em função vamos definir o que é uma rotina. Podemos definir rotina como sendo um programa curto ou longo que realiza uma determinada tarefa. Por exemplo: while(counter <= 15)
PORTB = converte[contador]; contador++; Definido o que é rotina, vamos falar sobre sub–rotina, uma sub–rotina é um programa que realiza uma
tarefa especifica, e podemos solicitar a execução desta tarefa muitas vezes no decorrer do programa. Quando solicitamos a execução de uma sub–rotina desejamos que no final da execução da mesma, ela
retorne para a rotina que solicitou a execução. Observe que a linha de retorno é a linha imediatamente após a chamada da sub–rotina. Exemplo:
Figura 7.1 – Rotina e sub-rotina.
A linguagem C é conhecida por ser uma linguagem de programação estruturada, esta qualidade vem da possibilidade de criarmos ou trabalharmos com funções já prontas.
Elas nos permitem separar os programas em blocos, desta forma o programa ficará muito mais legível, de fácil compreensão e permitirá o reaproveitamento das rotinas ou até de um programa inteiro, da vem o conceito de biblioteca.
A função tem um comportamento semelhante às sub–rotinas, ao final da execução da mesma, ela retorna ao local que solicitou a execução da função, sempre na linha seguinte a chamada.
As funções podem ser do compilador, são funções com uma tarefa pré–determinada, podemos ter funções especificas para o microcontrolador e funções de usuário, que darão ao programador imensas possibilidades de programação.
Enfim uma função é um conjunto de comandos agrupados para cumprir uma tarefa especifica, e recebem um nome para referência–las.
Uma função pode ser chamada varias vezes em um programa, o seu código só é escrito uma única vez na memória de programa, somente serão acrescidos o comando de chamada da função.
60
Criando uma Função Uma função é definida pelo tipo, nome, parâmetro e corpo da função. O tipo da função determina o tipo de dado a ser retornado, o nome é usado para chamar a função,
(invocar a sua execução) e o campo parâmetros permitirá passarmos valores para a função, podemos não ter nenhum parâmetro, como podemos ter um ou mais de um parâmetro, quando isto acontecer devemos separa-los por virgula.
Sintaxe: tipo nome_função (parametros) // Bloco de comandos, é o corpo da função
Retorno de Função Uma função pode ser finalizada de duas maneiras, pelo fim da execução dos comandos do corpo da
função, ao final será encontrada a chave que fecha o corpo da função, ou pela execução do comando return, quando ele é executado a execução da função é finalizada e provoca o retorno a linha seguinte a chamada da função.
Uma função só pode retornar um único valor, portanto o comando return, retornará somente um valor do mesmo tipo da função.
A sintaxe deste comando pode ser escrita da seguinte forma: return; return expressão; return (expressão); Exemplo: unsigned char a, b; // variáveis globais unsigned char soma () return a+b; // retorna o resultado da soma de a e b void main ()
a = 16; // carrega a variável a com 16 b = 23; // carrega a variável b com 23 a = soma(); // carrega em “a” o resultado da soma
Neste exemplo a função principal (main), chama a função soma, está por sua vez retorna o resultado de
a + b, o valor de retorno e armazenado na própria variável “a”.
Parâmetros de uma Função Os parâmetros de uma função podem ser variáveis inteiras, de ponto flutuante, matrizes, estruturas,
uniões e nulo, podemos ter uma função como parâmetro de uma função. Na declaração dos parâmetros devemos informar o tipo de cada variável.
As variáveis criadas no corpo de parâmetro, só existem enquanto a função está sendo executada. Ao final da execução as posições de memória destas variáveis são liberadas. Estas posições de
memória podem ser usadas por outras variáveis locais, ou por variáveis de parâmetro de função. Sintaxe: a) Apenas um parâmetro. tipo nome_função (tipo parametro) // Bloco de comandos, é o corpo da função
Curso Módulo 4 –Família 18F e MpLab C18 61
b) Mais de um parâmetro. tipo nome_função (tipo parâmetro1, tipo parâmetro2, tipo parâmetroN) // Bloco de comandos, é o corpo da função
Passagem por valor Quando criamos uma variável no corpo de parâmetro da função estamos querendo armazenar um valor
passado a função, desta forma temos uma passagem por valor. O valor passado a função pode ser uma constante ou o conteúdo de uma outra variável, que pode ser
global ou local. A variável criada no corpo da função é uma variável local, logo o seu conteúdo será perdido ao final da
execução da função. Exemplo: unsigned char soma(unsigned char A, unsigned char B)
return A+B; unsigned char C; void main(void)
C = soma(25, 15); No exemplo a função principal (main) chama uma função chamada soma, que tem como finalidade
somar dois números, que são carregas nas variáveis A e B, o dado 25 é carregado em A e o dado 15 é carregado em B, a função soma retorna o resultado da soma e armazena na variável C.
Passagem por referência Podemos passar os parâmetros por referência, quando realizamos este tipo de passagem a variável
criada no corpo de parâmetro da função é um ponteiro. A passagem por referência se justifica, quando desejamos que a função retorne mais de um valor. Os parâmetros recebem o endereço da variável que devem ser lidas. Sintaxe: tipo nome_função (tipo *parâmetro1, tipo *parâmetro2, tipo *parâmetroN) // Bloco de comandos, é o corpo da função
Trabalhando com tipo void em Funções Um de nossos estudos até agora foram os tipos de variáveis, estudamos que um uma delas era o tipo
void, este é um tipo nulo de variável. As funções também podem ser do tipo void, e ou os parâmetros da função, neste vejamos o irá
acontecer : • Função do tipo void: Como ela é de um tipo nulo, ela não retornará valor. Temos como exemplo a função principal, ela é o ponto de origem, é o inicio de nosso programa portanto não há local de retorno. Por este motivo ela é do tipo void. • Corpo de parâmetro do tipo void: Quando o corpo de parâmetro de uma função está preenchido com o tipo void, quer dizer que está função não recebe valor de forma alguma. Dentro do seu corpo de comandos ela pode tranqüilamente manipular variáveis globais e locais.
62
Prototipagem de Função Nos exemplos anteriores você pode observar que as funções são criadas antes da função principal. O
motivo para isso é que o compilador C, realiza a codificação do programa linha por linha. Caso exista algum erro no programa ou o compilador não saiba da existência da função, (na seqüência
de programação a chamada da função veio primeiro que a criação da própria função), a compilação será finalizada na linha do erro.
Por este motivo não podemos escrever um programa onde a chamada da função vem primeiro do que a própria função.
Observe no exemplo que a função soma é escrita antes da função principal. Exemplo: unsigned char soma(unsigned char A, unsigned char B)
return A+B; unsigned char C; void main(void)
C = soma(25, 15); Mas existe uma outra forma de evitar o erro de compilação, mesmo porque temos que nos preocupar
com situações mais importante do que a ordem de definição de funções. Vamos resolver esta situação com um protótipo da função que será criada posteriormente, podendo ser
localizada depois da função principal. O protótipo da função nada mais é do que a definição do seu tipo, nome e parâmetros, sem o seu corpo. Um protótipo ficaria assim: tipo nome (parâmetro); Veja o exemplo da função de soma com o protótipo da função. Exemplo: unsigned char soma(unsigned char A, unsigned char B); // protótipo da função unsigned char C; void main(void)
C = soma(25, 15); unsigned char soma(unsigned char A, unsigned char B)
return A+B; No exemplo anterior utilizamos o protótipo da função desta forma posso criar a minha função antes ou
depois da função que a chama.
Chamada de função externa Uma prática bem interessante é a reutilização de funções criadas por nós em outros programas. Para que possamos realizar está tarefa com sucesso, nossas funções preferêncialmente devem utilizar
variáveis locais, isto facilita a migração e utilizar a diretiva include para carregar a função externa no nosso programa.
Exemplo: #include <funcao_soma.c> unsigned char C; void main(void)
C = soma(25, 15);
Curso Módulo 4 –Família 18F e MpLab C18 63
A diretiva include carregará a rotina funcao_soma.c no programa principal. Desta forma poderemos criar bibliotecas para facilitar a nossa vida. Não é necessário incluir o header do microcontrolador usado nem a configuração dos fuses.
unsigned char soma(unsigned char A, unsigned char B)
return A+B;
Recursividade A recursividade é a capacidade da função chamar a si mesma, para ser executada uma tarefa. O calculo do fatorial e um exemplo perfeito para demonstrar a recursividade. O fatorial de um número é definido por: n! = n * (n - 1)! Uma função recursiva deve ter uma condição de término, para que não entremos em um laço infinito. A função do fatorial tem como condição de término, n igual a zero. Quando n for igual a zero a função é
finalizada. Veja o exemplo: unsigned char fatorial (int n); void main (void) int n; TRISB = 0; PORTB = 0; while(1) // rotina principal if (n < 0)break; PORTB = fatorial (5); // carrega no PORTB o fatorial de 5 unsigned char fatorial (int n) return ((n==0)? 1: n * fatorial (n-1));
64
Capítulo 8 - Manipulação dos Pinos de I/O
O PIC 18F4520 possui 5 ports e 36 pinos de I/O, são 3 pinos em comparação ao o modelo 16F877A. Você além de estar trabalhando com um modelo mais sofisticado você pode contar com mais pinos de I/O.
Alguns destes pinos de I/O são compartilhados com periféricos e o oscilador. Devemos ter atenção com os pinos que possuem mais de uma função, para não termos nenhum conflito
entre periférico e pinos de I/O. Outro ponto a ser observado é a característica elétrica do pino de I/O, eles podem ser do tipo TTL ou
Schmitt Trigger (ST). Um pino TTL considera nível lógico um, uma tensão entre 25% de VDD até VDD, e nível lógico zero uma
tensão entre 0V e 20% de VDD. A região entre 20% e 25% de VDD é considerada região indeterminada. O que pode acontecer com um pino de I/O, quando a tensão injetada no pino encontra – se na região
indeterminada? Neste caso o pino permanece com o estado lógico anterior. Se a condição anterior do pino era nível
lógico um, então o pino permanecerá em um, se o mesmo estava com nível lógico zero, ele permanecerá em zero, até que a tensão no pino seja suficiente para mudar o estado lógico do pino.
Em um pino ST consideramos nível lógico zero uma tensão entre 0V e 20% de VDD e o nível lógico um, uma tensão a partir de 80% de VDD.
0 0,5
1,0(0,2VDD)
1,25(0,25VDD)
5,0
10
VDD
NívelLógico
Região Indeterminada
0 0,5
1,0(0,2VDD)
4,0(0,8VDD)
5,0
10
VDD
Região Indeterminada
NívelLógico
Pino TTL Pino Schmitt Trigger
Figura 8.1 – Gráfico Nível Lógico x Tensão
Configurando os pinos de I/O
Todos os PORT’s podem ter os seus pinos de I/O configurados como entrada ou saída digital. Para esta operação devemos configurar o registrador TRIS, do port desejado. Cada bit do registrador TRISX corresponde a um pino de I/O. Para configurar um pino como entrada
devemos colocar o bit correspondente em nível lógico 1 e para configura – lo como saída devemos colocar o bit correspondente em zero.
TRISx Controle de direção do Pino de I/O.
TRISx7 TRISx6 TRISx5 TRISx4 TRISx3 TRISx2 TRISx1 TRISx0 R/W-1 R/W-1 R/W-1 R/W-1 R/W-1 R/W-1 R/W-1 R/W-1
Exemplo de configuração: a) Vamos configurar os pinos do PORTB, RB0, RB1, RB3, RB7 como saída.
TRISB7 TRISB6 TRISB5 TRISB4 TRISB3 TRISB2 TRISB1 TRISB0
0 1 1 1 0 1 0 0
TRISB = 0b01110100;
Curso Módulo 4 –Família 18F e MpLab C18 65
Devemos ainda observar a existência de periféricos compartilhando os pinos de I/O, e configurar o
registrador correspondente, para ligar ou desligar o periférico conforme o seu projeto.
Descrição dos Pinos
Na tabela abaixo, temos a descrição dos pinos do PIC18F4520 e o diagrama dos tipos de encapsulamento deste microcontrolador.
Figura 8.2 – PIC 18F4520 – Encapsulamento PDIP
Figura 8.3 – PIC 18F4520 – Encapsulamento TQFP
66
Figura 8.4 – PIC 18F4520 – Encapsulamento QFN
Pinos de alimentação, Oscilador e Master Clear Podemos observar que os pinos de I/O RE3, RA6 e RA7 compartilhados com o pino de RESET e com os
pinos do Oscilador do PIC.
NÚMERO DO PINO NOME DO PINO DIP PLCC TQFP TIPO DE
PINO TIPO DE BUFFER DESCRIÇÃO
_____ MCLR/VPP
MCLR VPP
RE3
1
2
18
I
P
ST
ST
Este é o pino de reset, ele é ativo em zero. VPP é o pino de entrada para tensão de programação do dispositivo. Entrada Digital.
OSC1/CLKI OSC1
CLKI
RA7
13 14 30
ST
CMOS
TTL
Entrada para cristal oscilador ou entrada para clock externo Entrada de clock externo, circuito RC. Pino de I/O de uso geral.
OSC2/CLKO/ RA6
OSC2
CLKO
RA6
13 14 30
--
--
TTL
Saída de clock, este pino terá um sinal com a mesma freqüência do cristal. Quando usado como saída teremos ¼ da freqüência de clock. Pino de I/O de uso geral.
VSS 12, 31
6, 30, 31 6, 29 P __ Referência negativa para pinos de
I/O e sistema Lógico.
VDD 11, 32
7, 8, 28, 29 7, 28 P __ Referência positiva para pinos de
I/O e sistema Lógico.
NC __ 13 12, 13, 33, 34
__ __ Não conectar.
Curso Módulo 4 –Família 18F e MpLab C18 67
Legenda da tabela:
TTL Entrada compatível com nível TTL
ST Entrada compatível com nível Schmitt Trigger
I Entrada CMOS Entrada/Saída compatível com CMOS
O Saída P Power
A configuração destas funções é feita através da janela de configuration bits no MPLAB ou através da
diretiva “#pragma config” a ser inserida em seu código, podemos configurar o tipo de oscilador e a configuração do RE3.
Habilitação do MCLR: MCLRE = OFF DISABLED – RA3 PINO DE I/O MCLRE = ON ENABLE – RA3 PINO DE RESET
Exemplo: #pragma config OSC = HS #pragma config MCLRE = ON
68
Estudo do PORTA
O PORTA possui 8 pinos que podemos utilizar como entrada digital/analógica ou saída digital. Alguns destes pinos são compartilhados com os periféricos do microcontrolador são eles, comparador
analógico, conversor de analógico para digital, entrada de clock externa para o timer 0 e os pinos do cristal. Curiosamente o pino RA4 não é dreno aberto como na maioria dos microcontroladores da Microchip,
logo não é necessário o uso do resistor de pull – up, normalmente utilizado, quando o mesmo é configurado como saída.
Descrição dos pinos de I/O do PORTA NÚMERO DO PINO NOME DO PINO DIP PLCC TQFP
TIPO DE PINO
TIPO DE BUFFER DESCRIÇÃO
RA0/AN0 RA0 AN0
2 3 19
I/O I
TTL
A
I/O bi-direcional do PORTA. I/O digital. Entrada Analógica canal 0.
RA1/AN1 RA1 AN1
3 4 20
I/O I
TTL
A
I/O bi-direcional do PORTA. I/O digital. Entrada Analógica canal 1.
RA2/AN2/ VREF- RA2 AN2
VREF-
4 5 21
I/O I I
TTL A A
I/O bi-direcional do PORTA. I/O digital. Entrada Analógica canal 2. Tensão de referência, patamar inferior.
RA3/AN3/ VREF+
RA3 AN3
VREF+
5 6 22
I/O I I
TTL
A A
I/O bi-direcional do PORTA. I/O digital. Entrada Analógica canal 3. Tensão de referência, patamar superior.
RA4/T0CKI RA4
T0CKI 6 7 23
I/O I
ST/OD
ST
I/O bi-direcional do PORTA. I/O digital de dreno aberto. Entrada de clock externo do TMR0.
RA5/AN4/SS/LVDIN RA5 AN4 SS
LVDIN
7 8 24
I/O
TTL A
ST A
I/O Digital. Entrada Analógica canal 4. Seleção de escravo no modo SPI. Entrada de Low Voltage Detect.
OSC2/CLKO/ RA6
OSC2
CLKO
RA6
13 14 30
--
--
TTL
Saída de clock, este pino terá um sinal com a mesma freqüência do cristal. Quando usado como saída teremos ¼ da freqüência de clock. Pino de I/O de uso geral.
OSC1/CLKI OSC1
CLKI RA7
13 14 30
ST
CMOS TTL
Entrada para cristal oscilador ou entrada para clock externo. Entrada de clock externo, circuito RC. Pino de I/O de uso geral.
Registradores para configuração do PORTA NOME BIT 7 BIT 6 BIT 5 BIT 4 BIT 3 BIT 2 BIT 1 BIT 0 PORTA RA7 RA6 RA5 RA4 RA3 RA2 RA1 RA0 LATA LATA7 LATA6 LATA5 LATA4 LATA3 LATA2 LATA1 LATA0 TRISA TRISA7 TRISA6 TRISA5 TRISA4 TRISA3 TRISA2 TRISA1 TRISA0 ADCON1 --- --- VCFG1 VCFG0 PCFG3 PCFG2 PCFG1 PCFG0 CMCON C2OUT C1OUT C2INV C1INV CIS CM2 CM1 CM0 CVRCON CVREN CVROE CVRR CVRSS CVR3 CVR2 CVR1 CVR0
Curso Módulo 4 –Família 18F e MpLab C18 69
Estudo do PORTB
Assim como PORTA, o PORTB possui 8 pinos que podemos utilizar como entrada ou saída digital. Os pinos de RB0 a RB4 podem ser configurados como entrada analógica.
O PORTB pode ainda contar com 8 resistores de pull – up interno que são habilitados pelo registrador INTCON2</RBPU>, quando ocorre um power–up o bit </RBPU> é iniciado com nível lógico um, ou seja, pull – up’s desabilitados. Quando o pino de I/O é configurado como saída, o pull – up é desligado.
Outro recurso do PORTB é a interrupção de mudança de estado nos pinos RB4 até RB7, esta interrupção é habilitada pelo registrador INTCON<RBIE>. Na rotina de tratamento desta interrupção devemos primeiro atualizar o PORTB (Ler ou Escrever), não utilize a instrução MOVFF para está finalidade, para depois limpar o flag de indicação de interrupção INTCON<RBIF>.
No PORTB, temos três fontes para gerar a interrupção externa, que são através dos pinos RB0, RB1 e RB2, podemos escolher a borda que gera a interrupção através do registrador INTCON2<INTEDG0:INTEDG1:INTEDG2>, a habilitação da interrupção é feita pelo registrador INTCON<INT0IE> pelo registrador INTCON3<INT1IE:INT2IE>.
Um último recurso do PORTB é o pino RB3 que pode ser utilizado como saída de PWM do módulo CCP2, através do configuration bits (CCP2MX = 0).
Descrição dos pinos de I/O do PORTB
NÚMERO DO PINO NOME DO PINO DIP PLCC TQFP TIPO DE
PINO TIPO DE BUFFER DESCRIÇÃO
RB0/INT0 RB0
INT0
33 36 8
I/0
I
TTL
ST
I/O bi-direcional do PORTB. I/O Digital. Entrada de int. Externa 0.
RB1/INT1 RB1
INT1
34 37 9
I/0
I
TTL
ST
I/O bi-direcional do PORTB. I/O Digital. Entrada de int. Externa 1.
RB2/INT2 RB2
INT2
35 38 10
I/0
I
TTL
ST
I/O bi-direcional do PORTB. I/O Digital. Entrada de int. Externa 2.
RB3/CCP2 RB3
CCP2 36 39 11
I/O I/O
TTL ST
I/O bi-direcional do PORTB I/O digital. Capture2 ent.,Compare2 saída, PWM2 saída.
RB4
37 41 14
I/O TTL I/O bi-direcional do PORTB I/O digital. Entrada de interrupção de mudança de estado.
RB5/PGM RB5 PGM 38 42 15
I/O I
TTL ST
I/O bi-direcional do PORTB I/O digital. Entrada de habilitação de Low Voltage ICSP. Entrada de interrupção de mudança de estado.
RB6/PGC RB6 PGC 39 43 16
I/O I
TTL ST
I/O bi-direcional do PORTB I/O digital. Entrada de clock para ICSP e In-Circuit Debugger. Entrada de interrupção de mudança de estado.
RB7/PGD RB7 PGD 37 41 14
I/O I/O
TTL ST
I/O bi-direcional do PORTB I/O digital. Entrada de dados para ICSP e In-Circuit Debugger. Entrada de interrupção de mudança de estado.
70
Registradores para configuração do PORTB NOME BIT 7 BIT 6 BIT 5 BIT 4 BIT 3 BIT 2 BIT 1 BIT 0 PORTB RB7 RB6 RB5 RB4 RB3 RB2 RB1 RB0 LATB LATB7 LATB6 LATB5 LATB4 LATB3 LATB2 LATB1 LATB0 TRISB TRISB7 TRISB6 TRISB5 TRISB4 TRISB3 TRISB2 TRISB1 TRISB0
INTCON GIE/GIEH PEIE/GIEL TMR0IE INT0IE RBIE TMR0IF INT0IF RBIF INTCON2 /RBPU INTEDG0 INTEDG1 INTEDG2 --- TMR0IP --- RBIP INTCON3 INT2IP INT1IP --- INT2IE INT1IE --- INT2IF INT1IF ADCON1 --- --- VCFG1 VCFG0 PCFG3 PCFG2 PCFG1 PCFG0
Algumas das opções de configuração do PORTB são feitas através da janela de configuration bits no
MPLAB ou através da diretiva #pragma config a ser inserida em seu código. PORTB A/D Habilitação: PBADEN = OFF PORTB<4:0> PINOS DIGITAIS PBADEN = ON PORTB<4:0> PINOS ANALÓGICOS
Exemplo: #pragma config PBADEN = OFF
Curso Módulo 4 –Família 18F e MpLab C18 71
Estudo do PORTC
O PORTC possui 8 pinos que podemos utilizar como entrada ou saída digital. Alguns destes pinos são compartilhados com os módulos MSSP (SPI e I2C), EUSART, CCP e ECCP. O pino RC1 é usado como saída de PWM que também é compartilhado pelo pino RB3.
Descrição dos pinos de I/O do PORTC
NÚMERO DO PINO NOME DO PINO DIP PLCC TQFP TIPO DE
PINO TIPO DE BUFFER DESCRIÇÃO
RC0/T1OSO/T1CKI RC0 T1OSO T1CKI
15 16 32
I/0 O I
ST -- ST
I/O bi-direcional do PORTC I/O Digital TIMER 1 saída do Oscilador (cristal externo) TIMER1 ou TIMER3 clock externo
RC1/T1OSI/CCP2 RC1 T1OSI CCP2
16 18 35
I/0 I
ST CMOS ST
I/O bi-direcional do PORTC I/O Digital TIMER 1 entrada do oscilador (cristal externo) Capture2 entrada, Compare2 saída, PWM2 saída.
RC2/CCP1 RC2 CCP1 17 19 36
I/0 I
TTL ST
I/O bi-direcional do PORTC I/O Digital Capture2 entrada, Compare2 saída, PWM2 saída.
RC3/SCK/SCL RC3 SCK SCL
18 20 37
I/O I/O I/O
ST ST ST
I/O bi-direcional do PORTC I/O digital Entrada/Saída de Clock da comunicação serial síncrona SPI Entrada/Saída de Clock da comunicação serial síncrona I2C
RC4/SDI/SDA RC4 SDI DAS
23 25 42
I/O I I/O
ST ST ST
I/O bi-direcional do PORTC I/O digital Entrada de Dados da comunicação SPI Entrada/Saída de Dados da comunicação I2C
RC5/SDO RC5 SDO 24 26 43
I/O O
ST --
I/O bi-direcional do PORTC I/O digital Saída de Dados da comunicação SPI
RC6/TX/CK RC6 TX CK
25 27 44
I/O O I/O
ST -- ST
I/O bi-direcional do PORTC I/O digital Pino de Transmissão de Dados da USART, modo Assíncrono. Pino de Clock da USART, modo Síncrono.
RC7/RX/DT RC7 RX DT
26 29 1
I/O I I/O
ST ST ST
I/O bi-direcional do PORTC I/O digital Pino de Recepção da USART, modo Assíncrono. Entrada/Saída de dados da USART, modo Síncrono.
Registradores para configuração do PORTC NOME BIT 7 BIT 6 BIT 5 BIT 4 BIT 3 BIT 2 BIT 1 BIT 0 PORTC RC7 RC6 RC5 RC4 RC3 RC2 RC1 RC0 LATC LATC7 LATC6 LATC5 LATC4 LATC3 LATC2 LATC1 LATC0 TRISC TRISC7 TRISC6 TRISC5 TRISC4 TRISC3 TRISC2 TRISC1 TRISC0
O pino RC1 é usado como saída de PWM que também que compartilhado pelo pino RB3. Esta opção de troca do pino de PWM do módulo CCP2 é realizada através da janela de configuration bits
no MPLAB ou através da diretiva #pragma config a ser inserida em seu código.
72
CCP2 Mux: CCP2MX = PORTBE CCP2 LIGADO AO PINO RB3 CCP2MX = PORTC CCP2 LIGADO AO PINO RC1
Exemplo: #pragma config CCP2MX = PORTBE
Estudo do PORTD
O PORTD pode ser configurado como entrada ou saída digital. Os pinos RD5, RD6 e RD7 são usados para as funções P1B, P1C e P1D respectivamente, do módulo
ECCP. Este PORT, também pode ser usado como barramento de dados do módulo PSP, porta paralela do tipo escravo.
Descrição dos pinos de I/O do PORTD
NÚMERO DO PINO NOME DO PINO DIP PLCC TQFP TIPO DE
PINO TIPO DE BUFFER DESCRIÇÃO
O PORTD é um port de I/O bidirecional ou Porta Paralela do tipo Escravo, neste modo os pinos são TTL.
RD0/PSP0 19 21 38 I/0 ST TTL
I/O Digital. Pino de Dados da Porta Paralela (PSP mode).
RD1/PSP1 20 22 39 I/0 ST TTL
I/O Digital. Pino de Dados da Porta Paralela (PSP mode).
RD2/PSP2 21 23 40 I/0 ST TTL
I/O Digital. Pino de Dados da Porta Paralela (PSP mode).
RD3/PSP3 22 24 41 I/0 ST TTL
I/O Digital. Pino de Dados da Porta Paralela (PSP mode).
RD4/PSP4 27 30 2 I/0 ST TTL
I/O Digital. Pino de Dados da Porta Paralela (PSP mode).
RD5/PSP5 28 31 3 I/0 ST TTL
I/O Digital. Pino de Dados da Porta Paralela (PSP mode).
RD6/PSP6 29 32 4 I/0 ST TTL
I/O Digital Pino de Dados da Porta Paralela (PSP mode)
RD7/PSP7 30 33 5 I/0 ST TTL
I/O Digital Pino de Dados da Porta Paralela (PSP mode)
Registradores para configuração do PORTD NOME BIT 7 BIT 6 BIT 5 BIT 4 BIT 3 BIT 2 BIT 1 BIT 0 PORTD RD7 RD6 RD5 RD4 RD3 RD2 RD1 RD0 LATD LATD7 LATD6 LATD5 LATD4 LATD3 LATD2 LATD1 LATD0 TRISD TRISD7 TRISD6 TRISD5 TRISD4 TRISD3 TRISD2 TRISD1 TRISD0 TRISE IBF OBF IBOV PSPMODE --- TRISE2 TRISE1 TRISE0
CCP1CON P1M1 P1M0 DC1B1 DC1B0 CCP1M3 CCP1M2 CCP1M1 CCP1M0
Curso Módulo 4 –Família 18F e MpLab C18 73
Estudo do PORTE
O PORTE possui apenas 4 pinos, podemos utilizar como entrada digital/analógica ou saída digital. Os pinos RE0, RE1, RE2 são usados como pinos de controle para a porta paralela, PSP, eles possuem
as seguintes funções respectivamente: /RD – Habilitação de Leitura, /WR – Habilitação de Escrita, /CS – Habilita operação da porta paralela.
O pino RE3 pode ser configurado como pino de reset como já foi visto anteriormente.
Descrição dos pinos de I/O do PORTE
NÚMERO DO PINO NOME DO PINO DIP PLCC TQFP TIPO DE
PINO TIPO DE BUFFER DESCRIÇÃO
Os pinos de I/O do PORTE são bidirecionais.
RE0/RD/AN5 RE0 RD
AN5
81 92 25
I/O
ST TTL
A
I/O Digital. Controle para Leitura da Porta Paralela(PSP mode). Entrada Analógica, canal 5.
RE1/WR/AN6 RE1 WR
AN6
9 10 26
I/O ST TTL
A
I/O Digital. Controle de Escrita da Porta Paralela(PSP mode). Entrada Analógica, canal 6.
RE2/CS/AN7 RE2 CS
AN7
10 11 27
I/O ST TTL
A
I/O Digital. Controle para Seleção de Chip da Porta Paralela(PSP mode). Entrada Analógica, canal 7.
______ MCLR/RE3
1
2
18
I
ST
Entrada Digital.
Registradores para configuração do PORTE NOME BIT 7 BIT 6 BIT 5 BIT 4 BIT 3 BIT 2 BIT 1 BIT 0 PORTA RA7 RA6 RA5 RA4 RA3 RA2 RA1 RA0 LATA LATA7 LATA6 LATA5 LATA4 LATA3 LATA2 LATA1 LATA0 TRISA TRISA7 TRISA6 TRISA5 TRISA4 TRISA3 TRISA2 TRISA1 TRISA0 ADCON1 --- --- VCFG1 VCFG0 PCFG3 PCFG2 PCFG1 PCFG0 CVRCON CVREN CVROE CVRR CVRSS CVR3 CVR2 CVR1 CVR0
Acesso aos SFR’s de configuração dos pinos de I/O
O compilador MPLAB C18 nos dá acesso direto aos registradores de função especial. Para configurar os PORT’s basta escrever o nome do registrador de função especial (SFR) e igualar ao valor desejado. Veja no arquivo de header como foram declarados os registradores.
Também é possível acessar facilmente os bits dos SFR’s, veja os exemplos abaixo. Exemplo: a) Vamos configurar todos os pinos do PORTA como saída. PORTA = 0x00; // Limpa os circuitos de saída do PORTA LATA = 0x00; // Desabilita o LATCH do PORTA ADCON1 = 0x07; // Desliga a função analógica do PORTA CMCON = 0x07; // Deliga os comparadores analógicos TRISA = 0b00000000; // Configura os pinos de I/O como saída b) Configurando os pinos RA0, RA4 e RA5 como saída. PORTA = 0x00; LATA = 0x00; ADCON1 = 0x07; CMCON = 0x07; TRISA = 0b11001110;
74
c) Vamos configurar todos os pinos do PORTB como saída. PORTB = 0x00; // Limpa os circuitos de saída do PORTB LATB = 0x00; // Desabilita o LATCH do PORTB ADCON1 = 0x0F; // Desliga a função analógica do PORTB TRISB = 0b00000000; // Configura os pinos de I/O como saída d) Manipulando individualmente os pinos RA0 e RB4. LATA = 0x00, LATB = 0x00; ADCON1 = 0x0F, CMCON = 0x07; TRISA = 0b11111110; TRISB = 0b11101111; PORTbits.RA0 = 0; PORTbits.RB4 = 1;
Funções para manipulação do PORTB ClosePORTB Desabilita a interrupção e pull-up do PortB CloseRBxINT Desabilita interrupção do PORTB pin x DisablePullups Desabilita os pull-ups do PORTB EnablePullups Habilita os pull-ups do PORTB OpenPORTB Configura as interrupções e os pull-ups do PORTB OpenRBxINT Habilita a interrupção do PORTB pin x
Descrição das Funções do PORTB
ClosePORTB Função: Desabilita a interrupção de mudança de estado e os resistores de pull – up interno do PORTB. Include: portb.h Protótipo da Função: void ClosePORTB( void ); Nome do arquivo: pbclose.c CloseRB0INT CloseRB1INT CloseRB2INT Função: Desabilita a interrupção externa, de um pino especifico do PORTB. Include: portb.h Protótipo da Função: void CloseRB0INT( void );
void CloseRB1INT( void ); void CloseRB2INT( void );
Nome do arquivo: rb0close.c, rb1close.c, rb2close.c DisablePullups Função: Desabilita os resistores de pull – up do PORTB. Include: portb.h Protótipo da Função: void DisablePullups( void ); Nome do arquivo: pulldis.c
Curso Módulo 4 –Família 18F e MpLab C18 75
EnablePullups Função: Habilita os resistores de pull – up do PORTB. Include: portb.h Protótipo da Função: void EnablePullups( void ); Nome do arquivo: pullen.c OpenPORTB Função: Configura a interrupção de mudança de estado e os resistores de pull – up do PORTB. Include: portb.h Protótipo da Função: void OpenPORTB( unsigned char config); Argumento da Função: config, Interrupt-on-change: PORTB_CHANGE_INT_ON Habilita a interrupção PORTB_CHANGE_INT_OFF Desabilita a interrupção Enable Pullups: PORTB_PULLUPS_ON Resistores de pull – up habilitados PORTB_PULLUPS_OFF Resistores de pull – up desabilitados Os parâmetros da função são constantes binárias que irão definir através de uma operação lógica E (&), a configuração do PORTB. O valor corresponde as opções de configuração estão no arquivo portb.h Nome do arquivo: pbopen.c Exemplo de código: OpenPORTB(PORTB_CHANGE_INT_ON & PORTB_PULLUPS_ON);
76
OpenRB0INT, OpenRB1INT, OpenRB2INT Função: Habilita as interrupções especificas do PORTB. Include: portb.h Protótipo da Função: void OpenRB0INT( unsigned char config );
void OpenRB1INT( unsigned char config ); void OpenRB2INT( unsigned char config );
Argumento da Função: config, Interrupt-on-change: PORTB_CHANGE_INT_ON Habilita a interrupção PORTB_CHANGE_INT_OFF Desabilita a interrupção Enable Pullups: PORTB_PULLUPS_ON Resistores de pull – up habilitados PORTB_PULLUPS_OFF Resistores de pull – up desabilitados Interrupt-on-edge: RISING_EDGE_INT Interrupt on rising edge FALLING_EDGE_INT Interrupt on falling edge Nome do arquivo: rb0open.c rb1open.c rb2open.c Exemplo de Código OpenRB0INT(PORTB_CHANGE_INT_ON & PORTB_CHANGE_INT_ON & RISING_EDGE_INT & PORTB_PULLUPS_ON);
Curso Módulo 4 –Família 18F e MpLab C18 77
Capítulo 9 - Tratamento de Interrupção
O que é Interrupção? Antes de falarmos em como habilitar e tratar uma interrupção vamos primeiro defini-la. Interrupção é o ato de para a execução da tarefa principal, para executar uma tarefa que julgamos
secundária e ao final desta tarefa retornar para a tarefa principal. Quando trabalhamos com microcontroladores, temos a possibilidade de coloca-los para realizar uma
tarefa principal e somente quando a tarefa secundária solicitar a atenção da CPU, é que o microcontrolador irá executar a rotina escrita para esta tarefa.
Ao final da execução da tarefa secundária a CPU volta a executar a rotina da tarefa principal. Os microcontroladores da família 18 possuem dois vetores de interrupção, estes vetores são endereços
de memória de programa, eles indicam o inicio da rotina de tratamento de interrupção. A família 18 possui dois vetores para tratamento de interrupção, um para interrupção de alta prioridade
que corresponde ao endereço 0x008, e outro para interrupção de baixa prioridade que está localizado no vetor 0x0018.
Veja como temos dois vetores, além de termos que habilitar individualmente a interrupção desejada, temos também que definir a sua prioridade.
Para esta finalidade temos registradores específicos e funções para configurar estes registradores. Os registradores responsáveis são: RCON, INTCON, INTCON2, INTCON3, PIR1, PIR2, PIE1, PIE2,
IPR1 e IPR2. Os registradores IPR1 e IPR2, definem a prioridade da interrupção, alta ou baixa. Toda interrupção possui um bit (flag), para sinalizar que a interrupção aconteceu, nos devemos limpar
este bit, ao final da execução da função de tratamento de interrupção, estes flags se encontram nos registradores citados acima. O MPLAB C18 não realiza está operação automaticamente, como ocorre no compilador da CCS.
Um dos cuidados quando estamos trabalhando com interrupção é na hora de salvar o contexto (WREG, BSR e STATUS), na interrupção de alta prioridade ele é salvo automaticamente no SHADOW, e restaurado ao sair da interrupção.
Já em uma interrupção de baixa prioridade devemos salvar o contexto na pilha criada na memória RAM e restaurar ao sair da mesma.
Uma interrupção de alta prioridade pode interromper uma de baixa prioridade, no caso contrario a de alta prioridade não é interrompida.
O tratamento de interrupção possibilita ao programador gerenciar melhor o tempo de processamento do microcontrolador, desta forma o microcontrolador pode executar a função principal enquanto os periféricos estão trabalhando em paralelo. Somente quando for necessário ou a tarefa de um periférico estiver finalizada, a função principal será interrompida.
78
Hardware da Lógica de Interrupção
Figura 9.1 – Lógica de tratamento das interrupções
Habilitando uma Interrupção O MPLAB C18 possui funções especificas para habilitar o tratamento de interrupção. Elas devem ser habilitadas na função principal ou em uma função de usuário. Estas funções serão vistas quando estudarmos os periféricos.
Definindo o vetor de interrupção Para criar uma função de tratamento de interrupção, utilizaremos a diretiva pragma code, que tem como
finalidade, informar o endereço da memória de programa onde o código será gravado e informar o nome do vetor de alta e baixa prioridade.
Exemplo: #pragma code VETOR_INT_HIGH=0x0008 // Vetor de alta prioridade. void atVETOR_INT_HIGH (void) // nome da função _asm goto INT_HIGH // Salta para a função de tratamento de alta _endasm #pragma code #pragma code VETOR_INT_LOW=0x18 // Vetor de baixa prioridade void atVETOR_INT_LOW (void) // nome da função _asm goto INT_LOW // Salta para a função de tratamento de baixa _endasm #pragma code
Curso Módulo 4 –Família 18F e MpLab C18 79
Tratando uma interrupção Depois que definimos o vetor de interrupção, podemos escrever a função de tratamento em qualquer
posição da memória de programa. Podemos incluir na função outra diretiva pragma, pragma interrupt, para salvar o valor dos registradores
que desejarmos, além daqueles já sitados. Não podemos esquecer de limpar o flag de interrupção na função de tratamento da mesma. Sintaxe: #pragma interrupt INT_HIGH save = Reg1, Reg2 #pragma interrupt INT_LOW save = RegA, RegB Vejamos como ficará a função: Exemplo:
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // Tratamento da interrupção de alta prioridade * // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * #pragma code INT_HIGH=0x100 //Posiciona o código (ORG) #pragma interrupt INT_HIGH save=PRODL,PRODH void INT_HIGH(void) TRATA_INT_HIGH(); //TRATA INT DE ALTA PRIORIDADE #pragma code void TRATA_INT_HIGH(void) if (PIR1bits.TMR1IF) TRATA_TIMER1() //Int. de Timer1 void TRATA_TIMER1(void) WriteTimer1(ReadTimer1()+0xfc49); // Inicia timer1 para contar 1000 pulsos PIR1bits.TMR1IF = 0; // Limpa flag da int
80
Capítulo 10 - Estudo dos Timers
Timer 0
O Timer 0 é um contador / temporizador que pode operar com 8 ou 16 bits, na linha 16 o timer 0 possui 8 bits.
Outra vantagem em relação a linha 16 é o prescaler dedicado, que pode ser ajustado a qualquer momento.
Podemos selecionar a origem do sinal de clock do Timer 0, através do bit T0CS. Para o TMR0 ser incrementado pelo ciclo de máquina, devemos colocar o bit T0CS em 0. Quando
ocorrer uma operação de escrita o no timer ele ficará parado por dois ciclos de máquina. Para clock externo (vindo do pino RA4) colocaremos o bit T0CS em 1, agora podemos escolher o borda
que irá incrementar o timer, com o bit T0SE em 0, selecionamos a borda de subida, com o bit T0SE em 1, selecionamos a borda de descida.
Este timer possui dois registradores responsáveis pela contagem / temporização, TMR0L e TMR0H, podemos ler ou escrever nestes registradores a qualquer momento.
A modalidade de 16 Bits O registrador TMR0H não é o byte mais significativo do Timer0, na modalidade 16-bit, ele é um buffer
intermediaria. A parte alta do Timer 0 não está disponível para leitura ou escrita. A atualização do TMR0H ocorre toda vez que realizamos uma leitura do registrador TMR0L. Isto fornece a habilidade de ler todos os 16 bits do Timer0 de uma única vez, evitando erro de leitura
caso o timer esteja ligado. Da mesma forma podemos carregar os 16 bits do Timer0 de uma única vez, basta para isso carregar o
valor desejado no TMR0H de depois carregar o registrador TMR0L, neste momento os dois bytes são transferidos ao Timer.
O Prescaler O prescaler é um contador de 8 bits que trabalha como um divisor de freqüência para o módulo Timer0. Não temos acesso ao prescaler ele não pode ser diretamente lido ou escrito, o seu valor é ajustado
pelos bits PSA e <T0PS2:T0PS0> do registrador T0CON. Colocando o bit PSA em 0, atribuiremos o prescaler ao módulo Timer0. Quando isto ocorre podemos
selecionar o divisor de freqüência na faixa de 1:2 a 1:256, por exemplo selecionando 1:4 o Timer0 precisara receber quatro pulsos de clock para incrementar uma unidade.
Quando realizamos uma operação de escrita no módulo Timer0, o prescaler é zerado, porém o ajuste dos bits <T0PS2:T0PS0> não são afetados.
A atribuição do prescaler ao Timer0 está inteiramente sob o controle do software e pode ser mudada "on-the-fly", ou seja, durante a execução de programa.
Interrupção do Timer0 A interrupção é gerada pelo “estouro” do timer, ou seja quando ele chegar na sua capacidade máxima de
contagem, para o modo 8 bits o valor máximo será de 255 (0XFF) e no modo 16 bits o valor máximo será de 65535 (0XFFFF), e voltar para zero, será gerado uma interrupção.
O tratamento da interrupção é habilitado pelo bit INTCON<TMR0IE>, não podemos esquecer de definir a prioridade no tratamento da interrupção, quando ocorre um estouro no Timer0 o bit INTCON<TMR0IF>, e colocado em nível lógico um, na rotina de tratamento de interrupção devemos coloca-lo em zero antes de sair da rotina de tratamento de interrupção.
Diagrama do Timer 0 no modo 8 Bit
Figura 10.1 – Timer 0 modo 8 bit
Curso Módulo 4 –Família 18F e MpLab C18 81
Diagrama do Timer 0 no modo 16 Bit
Figura 10.2 – Timer 0 modo 16 bit
Após um reset o TIMER 0 é configurado para 8 bits e o prescaler ajustado para o valor máximo.
O registrador T0CON Este registrador é responsável por configurar o TIMER0, definir o valor do prescaler, a origem do sinal de
clock, a borda de incremento, operação em 8 ou 16 bit’s e finalmente ligar ou desligar o timer. Através do MPLAB C18 temos funções especificas para configuração deste registrador, mas lembre-se
que podemos configurar o TIMER 0 através deste registrador, para isso basta escrever o nome do registrador e igualar ao valor desejado.
Registradores para configuração do TIMER0 NOME BIT 7 BIT 6 BIT 5 BIT 4 BIT 3 BIT 2 BIT 1 BIT 0
INTCON GIE/GIEH PEIE/GIEL TMR0IE INT0IE RBIE TMR0IF INT0IF RBIF T0CON TMR0ON T08BIT T0CS T0SE PSA T0PS2 T0PS1 T0PS0 TRISA TRISA7 TRISA6 TRISA5 TRISA4 TRISA3 TRISA2 TRISA1 TRISA0 TMR0L Registrador do Timer 0, Low byte TMR0H Registrador do Timer 0, High byte
T0CON: Registrador de Controle
82
Funções para manipulação do Timer 0 CloseTimer0 Desabilta o Timer0 OpenTimer0 Configura o Timer0 WriteTimer0 Escreve no Timer0 ReadTimer0 Lê o valor do Timer0
Timer 1
O Timer1 é um contador/temporizador de 16 bits que possui as seguintes características: Podemos selecionar através do software a forma de trabalho do timer, temporizador ou um contador de
16-bits. Ele possui dois registradores para está operação que podem ser lidos ou escritos, TMR1H e TMR1L.
A origem da fonte de clock pode ser selecionada como interna ou externa. Este timer gera Interrupção na transição de 0XFFFF -> 0X0000, pode ser zerado por uma das
configurações do módulo CCP. Uma opção nova é a utilização do Timer1, como gerador de clock para o microcontrolador, está opção é
sinalizada pelo bit T1RUN. Com o seu próprio oscilador low-power, podems utiliza-lo como um RTC (Relógio de Tempo Real). Este
oscilador independe do fonte de clock do microcontrolador. O Timer1 é controlado através do registrador T1CON. Neste registrador encontraremos o bit T1OSCEN, responsável em habilitar o oscilador interno do timer.
Outro bit importante é o TMR1ON este bit pode habilitar ou desabilitar o sinal de clock do Timer1,interrompendo a contagem ou a temporização, este bit não zera o timer .
Diagrama do Timer 1
Figura 10.3 – Timer 1
Modo de 16 bits para Leitura ou Escrita O Timer1 pode ser configurado para operação de Leitura ou Escri ta em 16 bi ts. Quando
co locamos o b i t RD16 em um o registrador TMR1H é mapeado como um buffer, para acesso indireto da parte alta do Timer1.
Uma operação de leitura do registrador TMR1L, carrega o registrador TMR1H com a parte alta do timer. Desta forma realizamos uma leitura de 16 bits.
Curso Módulo 4 –Família 18F e MpLab C18 83
Para uma operação de escrita de 16 bits devemos carregar o registrador TMR1H como o valor desejado e depois carregar o registrador TMR1L. Logo após a carga do TMR1L, o timer recebe os 16 bits ao mesmo tempo. Veja a parte alta do timer só acessada indiretamente neste modo.
O prescaler do Timer1 é zerado logo após a operação de escrito do registrador TMR1L.
Diagrama do Timer 1 modo de escrita e leitura de 16 Bit
Figura 10.4 – Timer 1 modo de escrite e leitura de 16 bit
Quando o bit T1CON<T1OSCEN>, é colocado em zero o circuito inversor e o resistor de feedback são desligados para eliminar o consumo de energia.
Oscilador do Timer1 Os pinos T1OSI, entrada, e T1OSO, saída, são utilizados para ligarmos um cristal ou ressonador para
gerar sinal de clock para o Timer1. Este oscilador externo é habilitador pelo bit T1OSCEN, Este circuito oscilador é de baixa potencia (LP),
logo poderemos utilizar cristais de 32kHz a 200kHz.
Figura 10.5 – Oscilador do Timer 1
Usando o TIMER1 como uma fonte de clock O Timer1 pode ser usado como uma fonte de clock para o dispositivo. Esta possibilidade é configurada
através dos bits, OSCCON<SCS1:SCS0>, podemos configurar a modalidade SEC_RUN, o microcontrolador e os periféricos são recebem o sinal de clock do Timer1. Caso o bit OSCCON<IDLEN> seja zerado e a instrução de Sleep seja executada, o dispositivo entra no modo SEC_IDLE. Sempre que o oscilador Timer1 está sendo usado como fonte de clock o bit T1RUN, é ajustada para sinalização do mesmo.
Isto pode ser usado para determinar a modalidade de clock usada pelo microcontrolador. Pode também indicar a fonte do pulso de disparo que está sendo usada atualmente pelo monitor à prova
de falhas do pulso de disparo.
84
Timer1 em Low Power O oscilador do Timer1 pode se configurado para operar em dois níveis distintos de consumo de potência
baseados na configuração de dispositivo. Quando o bit de configuração, CONFIG3H<LPT1OSC>, é colocado em um, o Timer1 opera em low power.
Quando CONFIG3H<LPT1OSC>, é colocado em zero, o Timer1 opera em um nível de um consumo mais elevado.
Com o Timer1 operando em low power, ele tende a ser mais sensível à interferência, os ambientes de ruído elevados podem causar alguma instabilidade do oscilador. A opção low power, conseqüentemente, é melhor para as aplicações de baixo nível de ruído e o baixo consumo é uma consideração importante do projeto.
Interrupção do Timer1 A interrupção é gerada pelo estouro do Timer, quando o mesmo chegou ao seu valor máximo 0XFFFF e
volta para 0X0000, isto é provocado pelo incremento do par de registradores do TMR1<TMR1H:TMR1L>. O evento é sinalizado pelo bit PIR1<TMR1IF>. Este deve ser zerado por software. Esta interrupção
somente será tratada se o bit, PIE1<TMR1IE>, estiver em nível lógico um.
Reset do Timer1 Uma das possibilidade de configuração do módulo CCP, configura o modo capture para gerar um reset
no Timer1. Isto só será possível caso o Timer1 esteja configurado para trabalhar no modo síncrono. Este evento não
gera interrupção de Timer1, apenas zera o Timer. O timer será zerado quando o valor dos registradores CCPRH e CCPRL, forem exatamente iguais ao
valor dos registradores do Timer1.
Registradores para configuração do TIMER1 NOME BIT 7 BIT 6 BIT 5 BIT 4 BIT 3 BIT 2 BIT 1 BIT 0
INTCON GIE/GIEH PEIE/GIEL TMR0IE INT0IE RBIE TMR0IF INT0IF RBIF T1CON RD16 T1RUN T1CSSPIF T1CKPS0 T1OSCEN /T1SYNC TMR1CS TMR1ON
PIR1 PSPIF ADIF RCIF TXIF SSPIF CCPIF TMR2IF TMR1IF PIE1 PSPIE ADIE RCIE TXIE SSPIE CCP1IE TMR2IE TMR1IE IPR1 PSPIP ADIP RCIP TXIP SSPIP CCP1IP TMR2IP TMR1IP
TMR1L Registrador do Timer 1, Low byte TMR1H Registrador do Timer 1, High byte
T1CON: Registrador de Controle
Curso Módulo 4 –Família 18F e MpLab C18 85
Funções para manipulação do Timer 1 CloseTimer1 Desabilta o Timer1 OpenTimer1 Configura o Timer1 WriteTimer1 Escreve no Timer1 ReadTimer1 Lê o valor do Timer1
Timer 2
O Timer2 é um módulo temporizador que utiliza o clock da máquina, ele não permite incremento por clock externo.
Ele possui dois registradores de 8 bit, TMR2 temporizador e PR2 periodo, ambos permitem escreta e leitura.
O seu Prescaler é programável por software (1:1, 1:4 e 1:16) e o Postscaler que também é programável por software (1:1 a 1:16), permite contar quantas vezes o timer foi zerado.
Sua Interrupção ocorre quando o TMR2 é igual ao PR2. Este timer é configurado pelo registrador T2CON, que habilita ou desabilita o timer e configura o
prescaler e o postscaler. O Timer2 pode ser desabilitado zerando o bit, TMR2ON<T2CON>, para minimizar o consumo de
energia.
Diagrama do Timer 2
Figura 10.5 – Diagrama do Timer 2
Operação do Timer2
Na operação normal, o TMR2 é incrementado de 00h até o valor ajustado no PR2 a cada pulso de clock
(FOSC/4). Um contador de 4 bits, prescaler, ligado a entrada de clock, nos permite dividir a freqüência de clock por 1, 4, 16. Este precaler é configurado pelos bits, T2CON<T2CKPS1:T2CKPS0>.
86
O valor do TMR2 é comparado ao registrador PR2, a cada pulso de clock. Quando os dois valores forem iguais, o comparador gera um pulso na saída do temporizador. Este sinal
também zera o valor do TMR2. O registrador TMR2 é zerado em toda operação de reset do dispositivo e quando o registrador PR2
inicializar em FFh. Os contadores do prescaler e do postscaler são zerados nos seguintes eventos: • Uma operação de escrita no registrador TMR2; • Uma operação de escrita no registrador de T2CON; • Algum tipo de reset do dispositivo.
O TMR2 não é zerado quando o registrador T2CON é escrito.
Interrupção do Timer2 O Timer2 também pode gerar interrupção. Sua interrupção ocorre quando o TMR2 é igual ao PR2. Ela
depende também do valor ajustado o Postscaler, este conta a quantidade de vezes que o TMR2 atingiu o mesmo valor do PR2, quando a quantidade de vezes é igual ao valor ajustado no Postscale é então gerado uma interrupção. Uma das escalas do postscale (de 1:1 a 1:16) pode ser selecionada com os bits de controle do postscaler, T2CON<T2OUTPS3:T2OUTPS0>.
Esta ocorrência é sinalizada pelo bit PIR1<TMR2IF>. A interrupção será tratada se o bit PIE1<TMR2IE>, estiver com nível lógico um.
Timer como Base de Tempo A Saída do Timer2 está disponível aos módulos de CCP, onde é usada como uma base do tempo para
operações do PWM. O Timer2 pode opcionalmente ser usado como fonte de clock ao modulo SPI do módulo de MSSP.
Registradores para configuração do TIMER2 NOME BIT 7 BIT 6 BIT 5 BIT 4 BIT 3 BIT 2 BIT 1 BIT 0
INTCON GIE/GIEH PEIE/GIEL TMR0IE INT0IE RBIE TMR0IF INT0IF RBIF T2CON --- T2OUTPS3 T2OUTPS2 T2OUTPS1 T2OUTPS0 TMR2ON T2CKPS1 T2CKPS0
PIR1 PSPIF ADIF RCIF TXIF SSPIF CCPIF TMR2IF TMR1IF PIE1 PSPIE ADIE REIE TXIE SSPIE CCP1IE TMR2IE TMR1IE IPR1 PSPIP ADIP RCIP TXIP SSPIP CCP1IP TMR2IP TMR1IP TMR2 Registrador do Timer 2 PR2 Registrador de período do Timer2
T2CON: Registrador de Controle
Curso Módulo 4 –Família 18F e MpLab C18 87
Funções para manipulação do Timer 2
CloseTimer2 Desabilta o Timer2 OpenTimer2 Configura o Timer2 WriteTimer2 Escreve no Timer2 ReadTimer2 Lê o valor do Timer2
Timer 3
O Timer3 é um contador/temporizador de 16 bits que possui as seguintes características: Podemos selecionar através do software a forma de trabalho do timer, temporizador ou um contador de
16-bits. Ele possui dois registradores para está operação que podem ser lidos ou escritos, TMR3H e TMR3L.
A origem da fonte de clock pode ser selecionada como interna ou externa. Este timer gera Interrupção na transição de 0XFFFF -> 0X0000, pode ser zerado por uma das
configurações do módulo CCP. Com o seu próprio oscilador low power, podemos utiliza-lo como um RTC (Relógio de Tempo Real). Este
oscilador independe do fonte de clock do microcontrolador. O Timer3 é controlado através do registrador T3CON. No registrador T1CON encontramos o bit T1OSCEN, responsável em habilitar o oscilador interno do Timer3.
Outro bit importante é o TMR3ON este bit pode habilitar ou desabilitar o sinal de clock do Timer1,interrompendo a contagem ou a temporização, este bit não zera o timer .
Diagrama do Timer 3
Figura 10.6 – Diagrama do Timer 3
Modo de 16 bits para Leitura ou Escrita O Timer3 pode ser configurado para operação de Leitura ou Escrita em 16 bits. Quando colocamos o bit
RD16 em um o registrador TMR3H é mapeado como um buffer, para acesso indireto da parte alta do Timer1. Uma operação de leitura do registrador TMR3L, carrega o registrador TMR3H com a parte alta do timer.
Desta forma realizamos uma leitura de 16 bits. Para uma operação de escrita de 16 bits devemos carregar o registrador TMR3H como o valor desejado e
depois carregar o registrador TMR3L. Logo após a carga do TMR3L, o timer recebe os 16 bits ao mesmo tempo. Veja a parte alta do timer só acessada indiretamente neste modo.
O prescaler do Timer3 é zerado logo após a operação de escrito do registrador TMR3L.
88
Diagrama do Timer 3 modo de escrita e leitura de 16 Bit
Figura 10.7 – Diagrama do Timer 3 modo 16 bits
Quando o bit T1CON<T1OSCEN>, é colocado em zero o circuito inversor e o resistor de feedback são desligados para eliminar o consumo de energia.
Oscilador do Timer3 Os pinos T1OSI, entrada, e T1OSO, saída, são utilizados para ligarmos um cristal ou ressonador para
gerar sinal de clock para o Timer1. Este oscilador externo é habilitador pelo bit T1OSCEN, Este circuito oscilador é de baixa potencia (LP),
logo poderemos utilizar cristais de 32kHz a 200kHz.
Figura 10.8 – Oscilador do Timer 3
Interrupção do Timer3 A interrupção é gerada pelo estouro do Timer, quando o mesmo chegou ao seu valor máximo 0XFFFF e
volta para 0X0000, isto é provocado pelo incremento do par de registradores do TMR3 (TMR3H e TMR3L). O evento é sinalizado pelo bit PIR2<TMR3IF>. Este deve ser zerado por software. Esta interrupção
somente será tratada se o bit, PIE2<TMR3IE>, estiver em nível lógico um.
Reset do Timer3 Uma das possibilidade de configuração do módulo CCP, configura o modo capture para gerar um reset
no Timer1. Isto só será possível caso o Timer1 esteja configurado para trabalhar no modo síncrono. Este evento não
gera interrupção de Timer1, apenas zera o Timer. O timer será zerado quando o valor dos registradores CCPR2H e CCPR2L, forem exatamente iguais ao
valor dos registradores do Timer1.
Curso Módulo 4 –Família 18F e MpLab C18 89
Registradores para configuração do TIMER3 NOME BIT 7 BIT 6 BIT 5 BIT 4 BIT 3 BIT 2 BIT 1 BIT 0
INTCON GIE/GIEH PEIE/GIEL TMR0IE INT0IE RBIE TMR0IF INT0IF RBIF PIR2 OSCFIF CMIF --- EEIF BCLIF HLVDIF TMR3IF CCP2IF PIE2 OSCFIE CMIE --- EEIE BCLIE HLVDIE TMR3IE CCP2IE IPR2 OSCFIF CMIP --- EEIP BCLIP HLVDIP TMR3IP CCP2IP
TMR3L Registrador do Timer 3, Low byte TMR3H Registrador do Timer 3, High byte T1CON RD16 T1RUN T1CSSPIF T1CKPS0 T1OSCEN /T1SYNC TMR1CS TMR1ON T3CON RD16 T3CCP2 T3CKPS1 T3CKPS0 T3CCP1 /T3SYNC TMR3CS TMR3ON
T3CON: Registrador de Controle
Funções para manipulação do Timer 3 CloseTimer3 Desabilta o Timer3 WriteTimer3 Escreve no Timer3 ReadTimer3 Lê o valor do Timer3 OpenTimer3 Configura o Timer3
90
Capítulo 11 - Módulo CCP
Introdução
Vamos estudar agora os módulos denominados CCPs, cujo nome é originado dos três tipos de recursos oferecidos por eles: Capture, Compare e PWM. Cada um desses recursos é empregado para uma finalidade diferente, que será conhecida a partir de agora.
Teoria e recursos do PIC
O PIC 18F4520 possui dois módulos de CCP, denominados CCP1 e CCP2. Esses módulos são práticamente idênticos e, existe uma diferença no PWM do módulo CCP1, ele é dedicado para controle de motor,os demais módulos serão explicados ao mesmo tempo, sendo feito os comentários necessários quando houver algum tipo de diferença. Para facilitar o entendimento, cada um dos recursos (Capture, Compare e PWM) será descrito separadamente.
Para começar, entretanto, é bom esclarecermos a nomenclatura padrão que será utilizada para descrevermos características comuns aos dois módulos existentes:
Padrão CCP1 CCP2 Descrição
CCPxCON CCP1CON CCP2CON Registrador de configuração
CCPRxH CCPR1H CCPR2H Parte alta do valor de controle
CCPRxL CCPR1L CCPR2L Parte baixa do valor de controle
CCPx CCP1 (RC2)
CCP2 (RC1) Pino relacionado
Outro dado interessante que devemos informar neste momento é quanto ao uso dos dois módulos
conjuntamente. Como eles utilizam recursos compartilhados para suas bases de tempo (Timer 1 e Timer 2), podem existir algumas limitações ou conflitos, conforme a combinação de recursos desejada:
Modo Base de tempo Capture Timer 1/Timer 3 Compare Timer 1/Timer 3
PWM Timer 2
Recursos Desejados Observações
Capture Capture Sem conflitos, entretanto, ambos utilizarão a mesma base de tempo TMR1 e, por isso, serão sincronizados.
Capture Compare Caso o Compare esteja configurado para zerar o Timer 1, isso poderá acarretar em um conflito com o outro modo.
Compare Compare Caso o Compare esteja configurado para zerar o Timer 1, isso poderá acarretar em um conflito com o outro modo.
PWM PWM Ambos os PWM terão a mesma freqüência e serão sincronizados, devido ao uso da mesma base de tempo. Os Duty Cycles possuem controles independentes.
PWM Capture Ambos os modos são completamente independentes. PWM Compare Ambos os modos são completamente independentes.
Modo Capture Este módulo tem como objetivo a contagem de tempo entre dois eventos ocorridos em um dos pinos
CCPx. Para isso será utilizado como base de tempo o Timer 1 e no momento do evento seu valor será capturado (dai o nome Capture). Como o Timer 1 / Timer 3 são de 16 bits, a captura será feita em dois registradores: CCPRxH e CCPRxL.
Curso Módulo 4 –Família 18F e MpLab C18 91
Com esse recurso é possível então criarmos um periodímetro, contando o tempo gasto, por exemplo, entre duas bordas de subida da onda ligada ao pino CCPx. É importante observarmos, entretanto, que a captura do valor de Timer 1 não reseta este timer, e por isso, para definirmos o tempo real entre duas leituras será necessário uma conta de subtração entre a última leitura e a anterior. Esta conta deverá ser implementada pelo software.
Vejamos, então, como configurar o Capture para que possamos utilizá-lo corretamente. O modo Capture opera com os pinos como entrada, mas essa configuração não é feita automati-
camente. Por isso, antes de mais nada configure, através do TRISC, o pino CCPx como entrada. Caso este esteja configurado como saída, operações de escrita neste pino podem ser consideradas como mudança de borda, ativando a captura.
Quanto ao Timer 1 / Timer 3 , que será usado como base de tempo para este modo, ele não pode estar configurado para operação assíncrona, isto é, confirme a condição T1CON</T1SYNC>=0 e T3CON</T3SYNC>=0.
O Capture possui também quatro diferentes configurações (fora o desligamento), que podem ser escolhidas através de CCPxCON<CCPxM3:CCPxM0>:
CCPxM3: CCPxM0 Descrição
0000 CCPx desligado
0100 Capture ligado para borda de descida Prescale de 1:1
0101 Capture ligado para borda de subida Prescale de 1:1
0110 Capture ligado para borda de subida Prescale de 1:4
0111 Capture ligado para borda de subida Prescale de 1:16
Obs. As demais opções de configuração dizem respeito aos outros modos (Compare/PWM).
As diferenças básicas entre essas configurações dizem respeito à borda que gerará o evento e ao
prescale adotado. Esse prescale é um contador interno (não acessível) de bordas. Por exemplo: caso seja escolhida a opção 0111, a captura do TMR1/TMR3 acontecerá a cada 16 bordas de subida. A finalidade desse prescale é o aumento da precisão do sistema. Quando optamos em trabalhar com prescaler de 1:1, a erro máximo em um período é de um ciclo de máquina (TCY). Quando aumentamos o prescale, esse erro diminui proporcionalmente, e teremos para 1:16 um erro máximo de TCY/16.
Toda vez que o evento de Capture acontecer, o flag CCPxIF será ativado, e caso essa interrupção esteja ligada, ela irá acontecer. Esta é uma maneira fácil de implementarmos a conta de subtração dos tempos absolutos para chegarmos ao período correto. Não se esqueça de que, quando uma nova captura acontece, ela será gravada em CCPRxH e CCPRxL, sobrescrevendo os valores anteriores.
Para alterar entre as opções de configuração do modo, alguns cuidados devem ser tomados. Como o prescale é um contador interno que não pode ser zerado manualmente, a alteração de configuração poderá gerar uma interrupção. Uma maneira de evitarmos isso seria o desligamento da interrupção (CCPxIE) antes dessa operação. Uma outra maneira, mais prática, é desligarmos o modo Capture (limpando o registrador CCPxCON). Isso irá resetar o modo, limpando também o contador de prescale. Depois, basta escolhermos a nova configuração, carregando CCPxCON com o valor desejado.
Quanto ao funcionamento do Capture em modo SLEEP, a história é um pouco confusa, pois nessa situação ou o Timer 1 não está funcionando ou está em modo assíncrono. Por isso, a interrupção de CCP pode até acontecer, acordando o PIC, mas os registradores CCPRxH e CCPRxL não serão atualizados.
92
Resumo dos registradores associados ao Capture
Endereço Nome Bit 7 Bit 6 Bit 5 Bit 4 Bit 3 Bit 2 Bit 1 Bit 0 0Bh ... INTCON GIE PEIE T0IE INTE RBIE T0IF INTF RBIF OCh PIR1 PSPIF ADIF RCIF TXIF SSPIF CCP1IF TMR2IF TMR1IF 8Ch PIE1 PSPIE ADIE RCIE TXIE SSPIE CCP1IE TMR2IE TMR1IE ODh PIR2 - - - EEIF BCLIF - - CCP2IF 8Dh PIE2 - - - EEIE BCLIE - - CCP2IE 17h CCP1CON - - DC1B1 DC1B0 CCP1M3 CCP1M2 CCP1M1 CCP1M0 16h CCPR1H Captura de TMR1H (Parte alta) 15h CCPR1L Captura de TMR1L (Parte baixa) 1Dh CCP2CON - - DC2B1 DC2B0 CCP2M3 CCP2M2 CCP2M1 CCP2M0 1Ch CCPR2H Captura de TMR1H (Parte alta) 1Bh CCPR2L Captura de TMR1L (Parte baixa) 87h TRISC Configuração do PORTC com Entrada(1) ou Saída(0)
Modo Compare Enquanto no modo Capture o valor de Timer 1 / Timer 3 era capturado e armazenado, aqui ele será
constantemente comparado (olha a origem do nome novamente) com os valores dos registradores CCPRxH e CCPRxL. Sempre que essa comparação de 16 bits resultar numa igualdade, o flag CCPxIF será ativado e a interrupção poderá acontecer, caso a mesma esteja devidamente ligada. Além disso, se desejado, podemos alterar automaticamente o estado do pino CCPx.
Aqui também vale a observação quanto ao funcionamento do Timer 1 / Timer 3, que deve obrigatoriamente estar ajustado em modo síncrono (T1CON</T1SYNC>=0). Não esqueça também de configurar o pino CCPx como saída, através do TRISC.
A ativação do Compare e as opções para mudança no pino podem ser configuradas em CCPxCON<CCPxM3:CCPxM0>:
CCPxM3: CCPxM0 Descrição
0000 CCPx desligado
1000 Inicia com o pino em 0 (baixo) e altera para 1 (alto) quando a comparação for bem-sucedida.
1001 Inicia com o pino em 1 (alto) e altera para 0 (baixo) quando a comparação for bem-sucedida.
1010 Não altera o pino. 1011 Não altera o pino, mas reseta TMR1/TMR3
Obs: As demais opções de configuração dizem respeitos aos outros modos (Compare/PWM).
Em todas as opções, o flag da interrupção sempre será ativado. A última opção (1011) é chamada no Data Sheet do PIC de Special Event Trigger, e apesar dela não
alterar o estado do pino CCPx, uma outra alteração muito importante acontece. Os registradores do Timer 1 ou Timer 3 são zerados. Com isso podemos utilizar o Timer 1 de forma similar ao Timer 2 com o registrador de limite PR2. A vantagem aqui é que estamos trabalhando com 16 bits, e não mais com 8. Este é o único caso de diferença entre CCP1 e CCP2, pois CCP2 além de resetar o TMR1/ TMR3, irá também iniciar uma conversão analógica (ADCON0<GO/DONE>) se o conversor estiver ligado. Atenção ao escolher essa configuração quando os dois modos CCP1 e CCP2 estiverem em uso, pois tanto o Compare quanto o Capture utilizam o Timer 1 como base de tempo.
No modo SLEEP, ou Timer 1/ Timer 3, está em modo assíncrono ou está paralisado. Em nenhum dos dois casos o Compare poderá funcionar. Por isso, este modo não opera durante o SLEEP. Só não se esqueça de que a saída CCPx é um pino como outro qualquer e, por isso, o nível de tensão da mesma será garantido mesmo em SLEEP.
Curso Módulo 4 –Família 18F e MpLab C18 93
Resumo dos registradores associados ao Compare
Endereço Nome Bit 7 Bit 6 Bit 5 Bit 4 Bit 3 Bit 2 Bit 1 Bit 0 0Bh ... INTCON GIE PEIE T0IE INTE RBIE T0IF INTF RBIF OCh PIR1 PSPIF ADIF RCIF TXIF SSPIF CCP1IF TMR2IF TMR1IF 8Ch PIE1 PSPIE ADIE RCIE TXIE SSPIE CCP1IE TMR2IE TMR1IE ODh PIR2 - - - EEIF BCLIF - - CCP2IF 8Dh PIE2 - - - EEIE BCLIE - - CCP2IE 17h CCP1CON - - DC1B1 DC1B0 CCP1M3 CCP1M2 CCP1M1 CCP1M0 16h CCPR1H Comparação com TMR1H (Parte alta) 15h CCPR1L Comparação com TMR1L (Parte baixa) 1Dh CCP2CON - - DC2B1 DC2B0 CCP2M3 CCP2M2 CCP2M1 CCP2M0 1Ch CCPR2H Comparação com TMR1H (Parte alta) 1Bh CCPR2L Comparação com TMR1L (Parte baixa) 87h TRISC Configuração do PORTC com Entrada(1) ou Saída(0)
Modo PWM O Modo PWM é provavelmente o recurso mais poderoso dos módulos CCPs, pois com ele podemos
obter uma tensão analógica a partir de um sinal digital. Na verdade, esta saída é meramente digital, isto é, somente pode assumir os estados 0 e 1. Porém, pelo conceito aplicado ao PWM, podemos transfor-má-la em uma tensão variável. Obviamente isso exigirá um hardware complementar (filtros) depois da saída do PIC, mas isso é uma outra história. Por enquanto, vamos entender melhor o que é um PWM.
O nome PWM tem sua origem no inglês Pulse Width Modulation que em Português pode ser considerado como Modulação por Largura de Pulso. Mas o que exatamente significa isso? Trata-se de uma onda com freqüência constante (período fixo) e largura de pulso (duty cycle) variável.
Na figura seguinte temos a representação de duas formas de onda tipo PWM, cada uma delas com uma largura de pulso diferente:
Esse tipo de sinal é particularmente importante, já que a partir dele é possível implementar um conversor
digital analógico com um único pino do microcontrolador, uma vez que controlando a largura do pulso é possível obter uma tensão analógica variável.
Vejamos a teoria. Genericamente, a tensão média de uma forma de onda é dada por:
( )dttVT1V
T
0dc ∫=
Onde T é o período da forma de onda e V(t) é a função da tensão no tempo. Para o caso do PWM temos que:
( )⎩⎨⎧
≤<→≤≤→
=Ttt0
tt0VtV
p
ppulso
Onde: pt é a duração do pulso em nível lógico 1
pulsoV é a tensão de pulso do sinal PWM.
Então,
94
⎟⎟
⎠
⎞
⎜⎜
⎝
⎛+= ∫∫
T
t
t
0pulsodc
p
p
0dtdtVT1V
pulsop
dc VTt
V =
A razão entre a largura de pulso e o período da forma de onda recebe o nome de duty cycle, ou em Português, ciclo ativo. O pulso da onda PWM apresenta tensão fixa, porém o valor médio da tensão desta forma de onda varia em função do duty cycle. A tensão média (Vdc) é diretamente proporcional ao duty cycle e como este varia entre 0 (quando tp = 0) e 1 (quando tp = T) temos que a tensão média de onda pode variar entre 0 e Vpulso. No nosso caso a variação será de VSS a VDD, ou seja, de 0 a 5V.
Assim, para obtermos um conversor digital analógico a partir do pino CCPx, basta implementar um sinal tipo PWM e adicionar à saída um filtro que passa baixa freqüência de corte menor que a própria freqüência do PWM.
Cálculo da freqüência de corte do filtro (fc):
RC21Fc π
=
Adotar:
10f
F PWMc ≤
Quando não é necessário obter uma tensão média continua, a implementação do filtro é descartada,
como nos casos da placa proposta. Tanto o resistor de aquecimento quanto o ventilador trabalham com PWMs sem filtro, pois a função desses componentes faz com que eles atuem como filtros, desde que a freqüência do PWM não seja muito baixa.
Assim sendo, a teoria continua válida, o que significa que podemos, através do PWM, regular a taxa de aquecimento do resistor e a velocidade do ventilador, variando a tensão média aplicada a eles.
Vamos aprender agora um pouco mais sobre o funcionamento dos PWMs dentro do 18F4520. Esse PIC possui dois canais de PWMs (CCP1 e CCP2), cada um com resolução máxima de dez bits.
Isso significa que nosso duty cycle poderá ser regulado de 0 a 100% com uma resolução máxima de 1024 pontos. No entanto, dependendo da configuração adotada, essa resolução não será atingida.
Vamos estudar como os tempos do PWM (pulso e período) são controlados internamente para podermos enterder melhor esse problema.
O período do PWM (T) é controlado diretamente pelo Timer 2, através do registrador PR2. Como já foi visto no primeiro capítulo, sempre que TMR2 = PR2, o timer é zerado. Neste momento, um novo período do PWM é iniciado. Desta forma, podemos definir o período e a freqüência do PWM pelas seguintes fórmulas:
T = [(PR2) + 1] x 4 x TOSC x (Prescale do TMR2) PWMFreq = 1 / T Tudo bem quanto ao período, mas como definimos o duty cycle? Na realidade, no PIC não definimos o
valor do duty cycle e sim o tempo do pulso em nível alto. Desta forma, o tempo do pulso pode ser calculado por:
tp = CCPRxL:CCPxCON<CCPxX:CCPxY> x TOSC x (Prescale do TMR2) Repare que a largura do pulso é ajustada em dois registradores: CCPRxL, que armazena os 8 bits mais
significativos, e CCPxCON, que armazena os dois bits menos significativos. Assim, temos os 10 bits que controlam o duty cycle do PWM alocados da seguinte maneira:
Para ficarmos de acordo com a teoria, calcularemos efetivamente o duty cycle dividindo o tempo do
pulso em nível alto pelo período total do PWM.
TMR2)do(PrescaleT41][(PR2)TMR2)do(PrescaleTxB0DCxB1:CCPxCON:CCPRxL
Tt
OSC
OSCp
×××+××><
=
41][(PR2)b0dcDCxB1:CCPxCON:CCPRxL
Ttp
×+>×<
=
Curso Módulo 4 –Família 18F e MpLab C18 95
Verifica-se então que apesar do período e o do tempo de pulso dependerem do cristal (Tosc) e do ajuste do prescale do Timer 2, o duty cycle depende única e exclusivamente dos valores ajustados nos registradores PR2, CCPRxL e CCPxCON (bits 5 e 4).
Veja que o registrador PR2 (8 bits) que controla o período do PWM é multiplicado por quatro para poder igualar-se aos 10 bits que controlam o duty cycle. É justamente esse o problema da resolução máxima atingida. Se o registrador PR2 for ajustado com um valor menor que 8 bits, ou seja, menor do que 255, serão necessários menos do que 10 bits para atingir um PWM com 100% de duty cycle. Portanto, o número de pontos para ajuste do duty cycle é quatro vezes maior do que o valor ajustado em (PR2+1). Em termos de bits, podemos dizer que a resolução do duty cycle é 2 bits maior do que o número de bits que formam o valor ajustado em PR2. Repare também que, caso PR2 seja ajustado com 255, nunca será atingido um duty cycle de 100%, pois o período atinigirá o valor máximo de 1024 ([PR2+1]x4), enquanto o tempo do pulso em nível alto (<DCxB9:DCxB0>) será no máximo 1023.
É fácil notar também que a resolução para o ajuste do período depende do prescale do Timer 2, assim:
Prescale Tempo do menor passo (resolução)
1 TOSC 4 4 TOSC ou TCY
16 16 TOSC ou 4 TCY
Porém, de qualquer forma, a menor resolução para o tempo do pulso (duty cycle) será sempre quatro vezes menor que a do período.
Note também que o postscale do Timer 2 não é utilizado para a construção dos tempos envolvidos no PWM.
Uma forma de calcular a quantidade máxima de bits que define a quantidade máxima de passos do nosso PWM é:
log(2)FFlog
PWM PWM
OSC
Resolução
⎟⎟⎠
⎞⎜⎜⎝
⎛
=
Vamos a um exemplo prático. Calculemos os valores para um PWM de 78,125 kHz, como um PIC rodando a 20 MHz e ajuste do
prescale do Timer2 em 1:1.
1 / 78,125kHz = [(PR2) + 1) x 4 x (1 / 20 MHz) x 1
12,8µs = [(PR2) + 1] x 4 x 50ns x 1
PR2 = 63 Partimos agora para a conta da resolução:
1 / 78,125kHz = 2PWM Resolução x (1 / 20 MHz) x 1
12,8µs = 2PWM Resolução x 50 ns x 1
2PWM Resolução = 256
PWM Resolução x log (2) = log(256)
PWM Resolução = 8 (bits)
Quanto à operação prática do PWM, já ficou claro que antes de mais nada é necessário definirmos a
freqüência de trabalho, com base na freqüência de funcionamento do PIC e na resolução desejada. Com isso calculamos o valor para ser colocado em PR2.
Depois devemos configurar o pino CCPx para ser utilizado como saída. Essa configuração não é automática e deve ser feita através do TRISC. Em seguida, devemos calcular a largura de pulso desejada. O resultado deve ser armazenado em dois registradores, sendo os 8 bits mais significativos em CCPRxL e os outros dois bits restantes em CCPxCON<DCxB1:DCxB0>.
Para o PWM, o registrador CCPRxH é do tipo somente leitura e ele é utilizado pelo sistema para armazenar uma cópia do CCPRxL. Essa cópia é utilizada para possibilitar que a largura de pulso seja alterada durante o funcionamento do PWM. Assim sendo, a nova largura será adotada automaticamente no próximo período do PWM. Os dois bits adicionais também são armazenados internamente pelo sistema.
96
Quando TMR2 = PR2, as seguintes ações irão acontecer: • TMR2 = 0, iniciando o próximo período;
• O pino CCPx é colocado em 1 (alto), a menos que a largura do pulso esteja definida para 0 (zero); • O valor do registrador CCPRxL é copiado para CCPRxH, atualizando a largura de pulso. O sistema passa, então, a monitorar o término do pulso, quando TMR2 = CCPRxH. Essa compa-ração
irá considerar ainda os 2 bits menos significativos. Neste momento, a saída CCPx será colocada em 0 (baixo), até que um novo período comece. Caso o tamanho do pulso seja especificado como sendo maior que o período total, a saída CCPx nunca será colocada em 0 (baixo), mas o sistema funcionará normalmente, como ajustado para 100% do PWM.
Para que, finalmente, a saída comece a operar, é necessário ainda ajustar o prescale do Timer 2 e ligá-lo, através do registrador T2CON. Por último, ligue também o módulo de PWM, através dos bits CCPxCON<CCPxM3:CCPxM0>:
CCPxM3:CCPxM0 Descrição 0000 CCPx desligado. 11XX Ativa a saída PWM.
Os PWMs não funcionam em modo SLEEP nem geram interrupções; porém, não se esqueça de que a
interrupção de Timer2 pode continuar acontecendo.
Resumo dos registradores associados ao PWM
Endereço Nome Bit 7 Bit 6 Bit 5 Bit 4 Bit 3 Bit 2 Bit 1 Bit 0 17h CCP1CON - - DC1B1 DC1B0 CCP1M3 CCP1M2 CCP1M1 CCP1M016h CCPR1H Cópia de CCPR1L (somente leitura) 15h CCPR1L Largura do pulso, bits de 9 a 2 (Parte baixa) 1Dh CCP2CON - - DC2B1 DC2B0 CCP2M3 CCP2M2 CCP2M1 CCP2M01Ch CCPR2H Cópia de CCPR2L (somente leitura) 1Bh CCPR2L Largura do pulso, bits de 9 a 2 (Parte baixa) 87h TRISC Configuração do PORTC com Entrada(1) ou Saída(0)
Curso Módulo 4 –Família 18F e MpLab C18 97
Capítulo 12 - Módulo Conversor AD
Teoria
Para começarmos a entender melhor a teoria da conversão dos sinais analógicos em dados digitais, nada melhor que partirmos para um exemplo prático: os sensores de temperatura normalmente fornecem uma informação analógica (como, por exemplo, uma tensão) proporcional à temperatura e, portanto, para que esta possa ser analisada pelo microcontrolador, necessitamos de um conversor analógico digital (CAD ou simplesmente A/D).
O menor passo, ou resolução, de um CAD é dado diretamente pelo seu número de bits e pode ser expresso por:
nref
2Vresolução =
Em que: refV é uma tensão de referência e n é o número de bits do conversor. Cada um dos n bits que compõem a informação digital representa uma parcela do valor da tensão
analógica a ser convertida, de forma que a soma de todas as contribuições de cada um dos n bits forma a tensão de entrada do conversor A/D. Assim, a parcela de tensão proporcional ao bit m do conversor A/D é dada por:
refn
1)(mm
entrada V22bV
−
=
Em que: mb é o valor do bit m, ou seja, 0 ou 1. Veja, que apenas os bits em 1 representam algum valor em termos de tensão analógica, uma vez que os bits em zero não contribuem para formar a tensão de entrada. Quanto maior a quantidade de bits, maior a resolução e a precisão do conversor. O PIC 18F4520 possui um conversor interno de 10 bits, mas existem outros modelos com 8 ou 12 bits. Vamos supor que o CAD para o nosso exemplo da temperatura seja de quatro bits, a tensão de referência seja de 5V e o valor da conversão em binário seja 1101. A tensão de entrada, então, é:
( ) ( ) 4,0625V52
1x20x21x21x2V 4
0123
entrada =+++
=
Este valor de tensão é equivalente a uma dada temperatura, e por isso o valor convertido (1101) é equivalente à mesma temperatura. Obviamente, para o exemplo da temperatura, assim como para a maioria dos demais casos práticos, não será necessária somente a conversão. Teremos também que nos preocupar com uma equação ou tabela para a adequação dos valores convertidos para a unidade desejada. Muitas vezes essa equação ou tabela será a responsável também pela linearização. Assim sendo, temos então duas conversões a serem feitas: a primeira de sinal analógico para valor digital, e a segunda de valor digital para a unidade realmente desejada, como por exemplo °C.
98
Existem diversas maneiras de implementarmos um conversor analógico/digital. Porém, como este livro trata do microcontrolador PIC18F4520 faremos um estudo aprofundado do sistema de conversão adotado pelo mesmo, que é denominado conversor de aproximação sucessiva.
Nesse tipo de conversor, a conversão é realizada do bit mais significativo para o menos significativo. Uma vez que o bit mais significativo (Msb) representa metade da tensão de referência, conhecer o estado deste bit (0 ou 1) já significa saber se a tensão de entrada é maior ou menor que a metade da referência. Conhecido o bit mais significativo, passa-se ao próximo bit, que representa a metade da metade da tensão de referência, ou seja, ¼ da tensão de referência. A conversão segue assim até o bit menos significativo (Lsb).
Vamos supor um CAD de quatro bits com tensão de referência de 5V:
Bit Tensão 4 (Msb) 2,5000 V 3 1,2500 V 2 0,6250 V 1 (Lsb) 0,3125 V
Suponha que a tensão de entrada seja de 3,3V. Neste caso, o procedimento de conversão seria assim:
Testa-se o bit mais significativo, ou seja, a tensão de entrada é maior do que 2,5V? Sim, portanto, este bit vale 1. Testa-se o próximo bit, ou seja, a tensão de entrada é maior do que 3,75V (2,5V + 1,25V)? Não,
portanto, este bit é 0. Testa-se o próximo bit, ou seja, a tensão de entrada é maior do que 3,125V (2,5V + 0,625V)? Sim,
portanto, este bit é 1. Finalmente testa-se o bit menos significativo, ou seja, a tensão de entrada é maior do que 3,4375V
(2,5V + 0,625V + 0,3125V)? Não, portanto, este bit é 0 e o valor final da conversão em binário é 1010. Essa forma de conversão é muito rápida, pois veja que para um conversor de n bits são necessárias n
interações, independente do valor a ser convertido. Em termos de hardware (diagrama de blocos), esse tipo de conversor pode ser representado por:
Recursos do PIC
Vamos agora estudar os modos de operação do conversor interno do PIC 18F4520, conhecendo seus recursos e os registradores de configuração e trabalho.
A primeira coisa que precisamos saber são as características desse conversor: • Conversor interno de 10 bits, dando um total de 1024 pontos;
• Até oito canais de conversão, com diversas configurações entre analógicos e digitais;
• Quatro tipos de referência: VDD (interna), VSS (interna), VREF+ (externa) e VREF- (externa);
• Freqüência de conversão baseada no clock da máquina ou em RC dedicado, possibilitando o funcionamento em modo SLEEP;
• Três ajustes de freqüência (divisores) para o clock de máquina;
• Dois tipos de justificação para o resultado da conversão: direita e esquerda;
• Um interrupção para término da conversão. Bem, agora que já sabemos o resumo dos recursos analógicos do nosso PIC, aprenderemos a utilizá-
los.
Curso Módulo 4 –Família 18F e MpLab C18 99
O primeiro conceito que deve ser entendido é que apesar do microcontrolador possuir diversos canais analógicos, internamente só existe um sistema de conversão. Por isso, somente um canal pode ser utilizado de cada vez. Vamos começar, então, aprendendo como configurar os canais corretamente.
Inicialmente devemos definir, conforme as necessidades do nosso projeto, qual a quantidade de canais analógicos que serão necessários. Como este PIC possui até oito canais analógicos, podemos utilizar todos ou só parte deles, deixando os demais pinos configurados como I/Os digitais. O problema é que, infelizmente, não é possível configurar individualmente cada canal, conforme nosso desejo. Existem valores padrões de configuração que devem ser respeitados.
Para configurarmos os canais analógicos precisamos alterar o valor dos bits existentes em ADCON1<PCFG3:PCFG0>:
PCFG3: PCFG0
AN11RB7
AN10 RB6
AN9 RB5
AN8RB4
AN7RE2
AN6RE1
AN5RE0
AN4RA5
AN3 RA3
AN2 RA2
AN1 RA1
AN0 RA0
0000 A A A A A A A A A A A A 0001 A A A A A A A A A A A A 0010 A A A A A A A A A A A A 0011 A A A A A A A A A A A A 0100 D A A A A A A A A A A A 0101 D D A A A A A A A A A A 0110 D D D A A A A A A A A A 0111 D D D D A A A A A A A A 1000 D D D D D A A A A A A A 1001 D D D D D D A A A A A A 1010 D D D D D D D A A A A A 1011 D D D D D D D D A A A A 1100 D D D D D D D D D A A A 1101 D D D D D D D D D D A A 1110 D D D D D D D D D D D A 1111 D D D D D D D D D D D D
Pinos configurados como analógicos: A=Analógico / D=Digital.
Além da configuração dos pinos analógicos, conforme tabela apresentada, não pode ser esquecido de
configurar corretamente os registradores TRISA e TRISE, de forma que os canais utilizados estejam ajustados para entrada (TRIS = 1). Caso um canal analógico esteja como saída, o sistema de conversão continuará funcionando, mas os valores convertidos serão equivalentes aos níveis alto (1 = VDD) e baixo (0 = VSS).
Outro ponto importante a ser observado nesta tabela diz respeito às tensões de referência. A conversão sempre será feita comparando-se a tensão no pino em relação as tensões de referência VREF+ e VREF-. Desta forma, quando a tensão de entrada for igual à tensão VREF+, a conversão resultará no valor máximo (1024) e quando a tensão de entrada for igual à VREF-, então o resultado da conversão será zero (0). Como já foi dito anteriormente, podemos ter quatro tipos de referências, sendo duas internas (VSS e VDD) , e duas externas, ligadas aos pinos RA2 (VREF-) e RA3 (VREF+).
Para configurarmos os canais analógicos AN2 e AN3, como entrada de tensão de referência precisamos alterar o valor dos bits existentes em ADCON1<VCFG1:VCFG0>:
VCFG1:VCFG0 VREF+ VREF-
0 0 VDD VSS 0 1 AN3 VSS 1 0 VDD AN2 1 1 AN3 AN2
Nossa entrada analógica poderá operar, por exemplo, com uma tensão variável de 0 a 5V. Para isso
podemos utilizar as referências internas como VREF+ = VDD(5V) e VREF- = VSS(0V). Em uma outra aplicação, nosso sensor pode variar de 1 a 4V. Para que não percamos resolução do A/D, podemos trabalhar com tensões de referência externas: VREF+ (RA3) = 4V e VREF- (RA2) = 1V. O importante é respeitarmos os limites elétricos impostos a essas referências:
100
Referência Mínimo (V) Máximo (V)VREF+ VDD − 2,5 VDD + 0,3 VREF- VSS − 0,3 VREF+ −2,0
(VREF+ − VREF- ) 2,0 VDD + 0,3 O próximo ponto a ser aprendido diz respeito à velocidade e, conseqüentemente, aos tempos, de
amostragem para a conversão A/D. Vamos começar entendendo como o sistema de conversão funciona internamente.
Para evitarmos problemas de ruído e variações da entrada analógica durante o processo de conversão (não podemos esquecer que nada é absolutamente instantâneo), o PIC utiliza internamente um processo denominado Sample and Hold (S/H). Esse nome poderia ser traduzido como amostra e congela. Mas como isso é feito? Muito simples. Internamente o PIC possui um capacitor (120pF) que é ligado ao canal analógico em uso. Sendo assim, ele fica carregado com a tensão existente no canal (amostra). Quando o processo de conversão é iniciado, este capacitor é desligado, automaticamente, do canal analógico, mantendo a tensão sobre o capacitor constante (congela). Por isso, durante todo o processo de conversão, a tensão utilizada é a existente no capacitor e não mais a do canal analógico. Mesmo que a tensão externa no canal varie, a conversão não será afetada.
Agora que já sabemos como o sistema funciona no âmbito do hardware, vamos conhecer quais são os tempos a serem respeitados de forma a não gerarmos erros durante a conversão. Para ilustrar o processo, mostraremos um gráfico resumido dos tempos de conversão:
Conforme o gráfico mostrado, podemos agora analisar cada um dos tempos envolvidos no sistema de
conversão:
Código Nome A Adequação do capacitor
B Desligamento do capacitor
C Conversão
D Religamento do capacitor
E Nova adequação do capacitor
Adequação do capacitor Internamente o capacitor nunca fica desligado, exceto durante a conversão. Ele sempre encontra-se
ligado ao sistema de entradas analógicas. Mas imaginemos que no momento o canal ativo esteja ligado ao GND. Desta forma o capacitor estará totalmente descarregado. No momento seguinte desejamos medir uma outra entrada analógica (outro canal) e por isso o sistema será chaveado internamente (veremos como isso é possível mais à frente). Caso o outro canal esteja com uma tensão de 5V, o capacitor interno terá que ser completamente carregado antes que possamos começar a efetuar a conversão. Obviamente essa carga não será instantânea. Para evitar problemas, sugerimos que o tempo padrão deixado para garantir a carga do capacitor seja de pelo menos 40 µs. Obviamente, este é um tempo que garante a pior condição. Na verdade, o tempo de adequação pode ser bem menor que isso, chegando no mínimo a 10 µs. Essa variação depende basicamente da diferença de tensão entre a entrada e o capacitor, da temperatura e da impedância de entrada. Essa impedância deve ser de no mínimo 50Ω e no máximo 10 kΩ. Quanto menor a impedância, menor também o tempo de adequação. Para o cálculo exato desse tempo, consulte o data sheet do PIC.
Curso Módulo 4 –Família 18F e MpLab C18 101
Desligamento do capacitor Depois de iniciada a conversão, o capacitor será desligado internamente. Isso é feito em
aproximadamente 100ns, sendo um valor práticamente desprezível.
Conversão O tempo de conversão já não é tão desprezível assim. Para entendermos o tempo de conversão,
precisaremos entender melhor a freqüência de trabalho do A/D. Para que o sistema de conversão funcione corretamente, um clock deve ser aplicado a ele. Cada período deste clock será chamado de TAD e é equivalente ao tempo de conversão de 1 bit. Como nosso conversor é de 10 bits, o tempo total da conversão será de 11 TAD + 2 TAD. Os dois períodos adicionais são para a adequação e o início da conversão. O valor de TAD dependerá da freqüência empregada, e para isso o sistema possui um RC interno ou divisores para o oscilador da própria máquina, mas veremos isso mais adiante. Assim sendo, o tempo de conversão é 13 TAD.
Religamento do capacitor Ao final da conversão, o valor dos registradores ADRESH e ADRESL de resultado serão atualizados, o
flag da interrupção será marcado é o capacitor será religado. O tempo mínimo recomendado para que tudo isso aconteça é de 2 TAD.
Nova adequação do capacitor Durante o tempo da conversão, o valor da tensão no capacitor interno não foi alterado; porém, ao
término da conversão ele será religado à entrada analógica, que pode ter sofrido uma variação brusca (como no exemplo), seja por imposição do próprio sistema, seja por uma troca de canal. Por isso, entre uma conversão e outra é necessário uma nova adequação da carga do capacitor. Recomendamos novamente um tempo de 40µs. As observações do item "Adequação do capacitor" também são válidas aqui para a diminuição deste tempo. O hardware de sample and hold requer 1,4us para atualizar o capacitor.
Este microcontrolador nos permite ajustar o tempo de aquisição por hardware, caso você queira escolher um tempo de aquisição maior basta configurar os bits ADCON0<ACQT2:ACQT0>, em 000.
Pode ajustar o tempo de aquisição utilize os bits ADCON0<ACQT2:ACQT0>:
ACQT2 ACQT1 ACQT0 Tempo de aquisição
0 0 0 0 TAD(1) 0 0 1 2 TAD 0 1 0 4 TAD 0 1 1 6 TAD 1 0 0 8 TAD 1 0 1 12 TAD 1 1 0 16 TAD 1 1 1 20 TAD
Nota: 1 – Tempo a ser definido por software Agora que já entendemos um pouco mais as características e conceitos do A/D, vejamos como
realmente devemos proceder para operar com ele. Depois de ter configurado quais serão os canais utilizados (ADCON1 e TRIS), teremos que configurar
também a freqüência de trabalho. Para isso devemos ajustar 2 bits em ADCON0<ADCS2:ADCS0>:
ADCS1 ADCS1 ADCS0 Freqüência 0 0 0 FOSC / 2 0 0 1 FOSC / 8 0 1 0 FOSC / 32 0 1 1 RC Interno 1 0 0 FOSC / 4 1 0 1 FOSC / 16 1 1 0 FOSC / 64 1 1 1 RC Interno
102
Para os três primeiros ajustes, o freqüência de trabalho do A/D será a freqüência do oscilador externo do PIC (FOSC) dividida por uma das opções (2, 4, 8, 16, 32 ou 64). Desta forma, obteremos o valor de TAD. Por exemplo, para um cristal externo de 4MHz e uma opção de FOSC / 8 teremos:
TAD = 8 / 4.000.000
TAD = 0,000002s ou 2µs
Neste caso, nosso TAD é de 2µs. O importante na escolha do cristal e da opção de divisão da freqüência é respeitar os valores mínimos de TAD aceitos pelo PIC. No caso do PIC 18F4520 (Standard) este valor deve ser maior que 0,7µs. Valores muito altos para o TAD também não são muito aconselháveis, e por isso recomendamos que não seja ultrapassado o limite de 25µs.
Continuando com o exemplo dado, chequemos então a freqüência máxima de amostragem para a comutação entre canais:
Desconsiderando-se o tempo de desligamento (desprezível), teríamos um tempo total para a conver-são
de 68µs (40 + 24 + 4). Sendo assim, nossa freqüência de amostragem máxima seria de 14,7 kHz. A última opção de escolha para o clock do A/D refere-se a um RC interno dedicado para essa finalidade.
Neste caso o valor nominal para TAD é de 1µs. Esta opção pode ser utilizada para que o sistema de conversão continue funcionando mesmo em modo SLEEP. Na verdade, este RC pode ser usado a qualquer momento, mas é recomendado que para sistemas que operem com freqüências superiores a 4 MHz este oscilador seja usado somente no modo SLEEP.
A última configuração necessária quanto às características de funcionamento do A/D diz respeito à forma como o resultado será armazenado nos registradores específicos. Como a conversão gera um resultado de 10 bits, serão necessários dois registradores para armazenar este valor. Para isso existem os registradores ADRESH e ADRESL, equivalentes à parte alta e à parte baixa do resultado. Acontece que a soma desses dois registradores resultam em 16 bits. Por isso, o resultado pode ser armazenado neles justificando pela esquerda ou direita. O bit de configuração ADCON1<ADFM> ajusta esta orientação:
ADFM Freqüência
1 Justificado pela direita. Utiliza todos os bits de ADRESL<7:0> e somente 2 bits de ADRESH<1:0> 0 Justificado pela esquerda. Utiliza todos os bits de ADRESH<7:0> e somente 2 bits de ADRESL<7:6>
Essa justificação é válida quando queremos, por exemplo, trabalhar com somente 8 bits do conver-sor.
Neste caso, podemos justificar pela esquerda e acessarmos somente a parte mais significativa através de ADRESH. Com isso estaremos criando um filtro, onde jogamos fora os 2 bits menos signifi-cativos, que se encontram em ADRESL.
Agora que os ajustes da configuração já foram feitos, podemos finalmente efetuar uma conversão. Comecemos ligando o sistema de conversão. Isso é feito através de ADCON0<ADON>:
ADON Estado do A/D 0 Sistema desligado 1 Sistema ligado
O sistema pode ser mantido desligado sempre que não estiver sendo utilizado para redução do
consumo. A próxima ação é a escolha do canal a ser utilizado, entre os oito disponíveis. Essa escolha será feita
através de ADCON0<CHS3:CHS0>:
Curso Módulo 4 –Família 18F e MpLab C18 103
CHS3:CHS0 Canal Selecionado CHS3: CHS0 Canal Selecionado 0000 Canal 0 1000 Canal 8 0001 Canal 1 1001 Canal 9 0010 Canal 2 1010 Canal 10 0011 Canal 3 1011 Canal 11 0100 Canal 4 1100 Canal 12 0101 Canal 5 1101 Não implementado 0110 Canal 6 1110 Não implementado 0111 Canal 7 1111 Não implementado
O último passo é iniciar a conversão por meio do bit ADCON0<GO/DONE>:
GO/DONE Estado do A/D 1 Inicia a conversão
0 Indica que a conversão términou. Caso seja forçado manualmente, cancela a conversão atual.
Depois de iniciada a conversão, todo o processo será executado. O capacitor interno é desconectado. A
conversão é efetuada (12 TAD) e logo em seguida (no próximo ciclo de máquina), os registradores ADRESH e ADRESL são atualizados. Neste momento, o bit ADCON0<GO/DONE> volta automaticamente para 0 (zero) e poder ser monitorado pelo programa para checar o término da conversão. Além disso, o flag da interrupção PIR1<ADIF> também é ativado. Caso a conversão seja abortada manualmente através de ADCON0<GO/DONE>=0, os registradores ADRESH e ADRESL não serão alterados.
Para fazer uso da interrupção de A/D, não se esqueça de efetuar os seguintes ajustes antes de iniciar a conversão:
• Limpar o flag PIR1<ADIF>=0;
• Ligar a interrupção de A/D em PIE1<ADIE>=1;
• Ligar as interrupções de periféricos em INTCON<PEIE>=1;
• Ligar a chave geral das interrupções em INTCON<GIE>=1;
Resumo dos registradores associados ao A/D Endereço Nome Bit 7 Bit 6 Bit 5 Bit 4 Bit 3 Bit 2 Bit 1 Bit 0
0Bh INTCON GIE PEIE T0IE INTE RBIE T0IF INTF RBIF Och PIR1 PSPIF ADIF RCIF TXIF SSPIF CCP1IF TMR2IF TMR1IF 8Ch PIE1 PSPIE ADIE RCIE TXIE SSPIE CCP1IE TMR2IE TMR1IE 1Eh ADRESH Resultado da conversão (Parte alta) 9Eh ADRESL Resultado da conversão (Parte baixa) 1Fh ACON0 ADCS1 ADCS0 CHS2 CHS1 CHS0 GO/DONE - ADON 9Fh ADCON1 ADFM - - - PCFG3 PCFG2 PCFG1 PCFG0 85h TRISA - - Configuração do PORTA como Entrada(1) ou Saída(0)
89h TRISE IBF OBF IBOVPSP
MODE - Configuração do PORTE como
Entrada(1) ou Saída(0)
104
Capítulo 13 - Módulo MSSP
Introdução
Neste capítulo começaremos o estudo de um assunto muito procurado pelos profissionais atuais: a comunicação serial. Dissemos que estaremos começando o estudo porque o PIC possui tantos recursos voltados a ele que utilizaremos dois capítulos para completá-lo. Nesta primeira parte, estudaremos dois tipos distintos de comunicação serial, denominados SPI e I2C. Esses protocolos poderão ser utilizados para a comunicação do PIC com outros microcontroladores, com memórias externas, drives de LCD, conversores, sensores e uma infinidade de outros periféricos.
Os dois protocolos mencionados (SPI e I2C) fazem parte de um sistema comum do PIC denominado MSSP (Master Synchronous Serial Port). Como esse sistema é comum, ele compartilha os mesmos recursos (pinos, hardware internos e registradores) entre os dois casos. Por isso só podemos utilizar um sistema de cada vez. Eles serão estudados separadamente.
Comecemos, então, conhecendo as diferenças e semelhanças básicas entre eles:
SPI I2C
Foi criado pela Motorola e seu nome significa: Serial Peripherall Interface
Foi criado pela Philips e seu nome significa: Inter-Integrade Circuit
Opera em modo Master e Slave Opera em modo Master e Slave Comunicação síncrona (com Clock) Comunicação síncrona (com Clock) A comunicação é feita por três vias: Clock (SCK) Data In (SDI) Data Out (SDO)
A comunicação é feita por duas vias: Clock (SCL) Data In/Out (SDA)
Não endereçável Endereçável (7 ou 10 bits) 8 bits de dados 8 bits de dados
Teoria e recursos do PIC para SPI
Antes de mais nada precisamos saber alguns conceitos básicos sobre a comunicação serial síncrona. Como o próprio nome diz, essa comunicação exige uma via de sincronismo entre os dois componentes envolvidos (receptor e transmissor) para que haja a marcação do momento exato da transmissão/recepção de cada bit. Isso é feito através do clock, que nada mais é que uma onda quadrada para sincronizar o sistema. Acontece que só pode existir um clock, e por isso não podemos deixar ambos os lados da comunicação responsáveis por ele. Para resolver esse problema foi criado o conceito de Master e Slave. O componente denominado Master (Mestre) será responsável pelo controle da comunicação, gerando o clock de sincronismo. Do outro lado, o componente será denominado Slave (Escravo) e ficará sempre aguardando o clock enviado pelo mestre.
Como a SPI não permite o endereçamento, a comunicação só pode ser feita entre dois pontos, sendo um deles o Master e o outro o Slave.
Como podemos observar na tabela comparativa apresentada anteriormente, neste modo de comunicação a ligação entre os componentes deve ser feita através de três vias:
Clock: Trata-se da via de clock, que pode ser uma entrada (Slave) ou saída (Master). O SPI aceita os
quatro tipos de clocks diferentes especificados para o padrão SPI. A descrição e a configuração desses tipos será vista posteriormente. No PIC 18F4520 é chamado de SCK e está localizado no pino 18 (RC3);
Data In: Trata-se da entrada de dados, ou seja, a via de recepção. No PIC 18F4520 é chamada de SDI e está localizada no pino 23 (RC4);
Data Out: Trata-se da saída de dados, ou seja, a via de transmissão. No PIC 18F4520 é chamada de SDO e está localizada no pino 24 (RC5);
O próximo diagrama mostra a ligação entre dois componentes, que podem, por exemplo ser dois PICs:
Curso Módulo 4 –Família 18F e MpLab C18 105
O PIC do lado esquerdo é o Master e, por isso, é responsável pela geração do clock. Seu pino SCK deve
ser configurado como saída. Já do lado direito o PIC é definido como Slave, ficando o pino de clock configurado como entrada.
Através do diagrama podemos também começar a entender melhor o funcionamento interno do SPI. Esse protocolo de comunicação trabalha com um registrador interno (não acessível) denominado SSPSR. Esse registrador é do tipo rotativo (Shift Register). A cada ciclo do clock, um bit é lido da porta SDI é escrito nesse registrador, entrando pelo bit menos significativo e rotacionando todos os bits para a esquerda. Ao mesmo tempo, o bit mais significativo é enviado para a porta SDO. Por isso, enquanto estamos recebendo um byte estamos também transmitindo outro. O registrador SSPBUF é o responsável pelo valor a ser transmitido e também pelo byte recebido.
A comunicação funciona então da seguinte maneira: 1. Quem manda na comunicação é o Master. Por isso, um valor qualquer é escrito no registrador
SSPBUF deste PIC. 2. O valor então é movido para o registrador SSPSR. 3. Em seguida oito pulsos são gerados na saída de clock SCK (cada pulso transmite 1 bit). 4. Ao término dos oito pulsos o valor que estava no SSPSRMaster é trocado com o valor que estava
SSPSRSlave. 5. Tanto de um lado quanto do outro o valor de SSPSR é então movido novamente para o registrador
SSPBUF. 6. Também de ambos os lados flags são ativados para informar ao sistema o fim da comunicação. Os
dados podem então ser acessados novamente em SSPBUF. Bem simples não é mesmo? Desta forma, a comunicação é feita sempre em sentido duplo, ao mesmo
tempo. Desta forma, três tipos diferentes de situações podem acontecer: • Master envia dado válido / Slave envia dado não-válido: Neste caso o Master está enviando
algum dado para o Slave; porém não interessa a informação que é enviada pelo Slave neste mesmo momento. É muito comum quando o Master está fazendo uma pergunta ao Slave, que terá de ser processada antes da resposta ser transmitida de volta. Neste caso, o dado recebido pelo Master ao final da transmissão deve ser descartado.
• Master envia dado não válido / Slave envia dado válido: Este caso é complementar ao anterior. Depois que o Master efetuou uma pergunta e o Slave a processou, uma resposta deverá ser retornada. Acontece que como é o Master quem manda no clock, ele precisará disparar uma nova transmissão para poder receber o dado do Slave.
• Master envia dado válido / Slave envia dado válido: Pode ser o caso mais raro, mas é usado para
aumento da velocidade. Por exemplo: o Master faz uma pergunta e ao mesmo tempo recebe a resposta da pergunta anterior.
Obviamente, apesar do sistema de comunicação ser de transmissão e recepção ao mesmo tempo, nem
sempre isso é necessário. Caso a comunicação seja em um só sentido, a via de transmissão do Slave (SDO) pode ser desativada configurando-se esse pino como entrada, a recepção do Master (SDI) sem uso.
106
Existe também um outro pino denominado /SS (RA5) que pode ser usado do lado Slave. Na maioria dos casos, este pino é opcional e serve como um sinal de Chip Select. Sua função é muito simples, quando este pino está em nível baixo (0), o sistema de comunicação funciona normalmente, com o pino SDO funcionando como saída de dados a cada pulso do clock. No entanto, se este pino for colocado em nível alto (1), o sistema de comunicação é desligado e o pino SDO fica flutuante. Com este recurso podemos montar uma rede de comunicação com um Master e vários Slaves, desde que o Master controle individualmente os pinos /SS de cada um dos Slaves. Este controle terá de ser implementado manualmente no software e deve garantir que somente um Slave está ativado de cada vez, evitando conflitos nas saídas SDO.
Quando configurado para Master, o pino /SS opera como um I/O normal (RA5).
Vejamos agora como configurar corretamente o sistema para efetuar uma transmissão. A primeira coisa a ser feita diz respeito à configuração correta dos pinos, como entradas ou saídas:
Pino Configuração SDI Este pino é configurado automaticamente pelo sistema como entrada. SDO Este pino deve ser configurado manualmente como saída através do TRISC.
SCK Este pino deve ser configurado como saída no Master e entrada do Slave, através do TRISC.
/SS Quando habilitado no Slave, deve ser configurado manualmente como entrada.
Depois devemos configurar a comunicação como sendo Slave ou Master. Existe mais de uma opção
para cada tipo, devendo ser configuradas em SSPCON<SSPM3:SSPM0>:
SSPM3: SSPM0
Descrição
0000 SPI Master, com clock = FOSC / 4 0001 SPI Master, com clock = FOSC / 16 0010 SPI Master, com clock = FOSC / 64 0011 SPI Master, com clock = TMR2 / 2 0100 SPI Slave, com /SS habilitado 0101 SPI Slave, com /SS desabilitado.
Obs: As demais opções de configurações dizem respeitos aos outros modos (I2C).
As quatro primeiras opções dizem respeito à escolha do modo Master, cada uma com uma freqüência diferente para o clock. Nas três primeiras, o clock é uma divisão (4, 16 ou 64) da freqüência do oscilador do próprio PIC. Com isso, podemos deduzir que na freqüência máxima de 20 MHz nossa taxa de transmissão é de 5,0Mbps. Na outra opção, o clock é gerado com base na metade do TMR2. Assim, a base de tempo será PR2 / 2. O postscale não é usado para este caso.
As duas últimas opções devem ser escolhidas quando estamos configurando um PIC em modo Slave. Neste caso devemos escolher entre trabalhar ou não com o pino /SS. Mais para a frente será comentado um caso em que a ativação deste pino é obrigatória.
Outra configuração ajusta a condição de recebimento. Assim, o sistema pode efetuar a leitura da porta SDI em dois pontos distintos, conforme o valor imposto em SSPSTAT<SMP>:
Curso Módulo 4 –Família 18F e MpLab C18 107
SMP Descrição
0 A recepção é feita na borda do meio do período. Pode ser usada tanto no Master quanto no Slave.
1 A recepção é feita na borda do final do período. Só pode ser usada no Master.
O próximo passo diz respeito à escolha da forma de onda do clock. O padrão SPI disponibiliza quatro
opções que podem ser ajustadas em dois bits distinto, SSPCON<CKP> e SSPSTAT<CKE>:
CKP Descrição 0 Clock normalmente em nível baixo (0) 1 Clock normalmente em nível alto (1)
CKE Descrição
0 Pulso do clock no começo do período 1 Pulso do clock no final do período
Graficamente fica muito mais fácil percebermos as diferenças entre os quatro modos resultantes da
combinação desses 2 bits:
Para o Master não existe limitação quanto ao uso de qualquer um dos quatro tipos. Para o Slave, a
questão é um pouco mais complicada. Como os pinos de clock estão diretamente ligados, então o valor padrão da via (nível alto ou baixo) deve
ser o mesmo. Desta forma o valor de CKP para o Slave deve ser obrigatoriamente o mesmo adotado para o Master.
O outro problema está em relação à transmissão/recepção do dado pelo Slave. Para o Master, o primeiro bit (o mais significativo) é colocado em SDO logo depois do começo do período, permanecendo lá até o final do mesmo. Desta forma, podemos considerar que o bit é transmitido no meio do período. Como ele sabe o momento exato de início do período, fica fácil também controlar a leitura da entrada SDI (no começo ou no fim). Para o Slave entretanto, um problema acontece quando CKE=1, pois não há como ele saber o início do período uma vez que não existe nenhuma borda neste ponto. Por esse motivo, para utilizarmos o Slave com CKE=1, obrigatoriamente teremos que habilitar o pino /SS. Quando o pino de /SS for colocado em nível baixo, o primeiro bit é colocado em SDO (até este momento ele estava flutuante) e o sistema fica aguardando o clock para dar continuidade na transmissão. Quanto à recepção, como o Slave só pode operar com leitura no meio do período (SMP=0), a entrada SDI será lida logo na primeira borda do clock.
Para ligar a comunicação SPI, basta ativar o sistema SSP através do bit SSPCON<SSPEN>:
SSPEN Descrição 0 Desabilita a comunicação.
1 Habilita a comunicação. Neste momento, os pinos SCK, SDO, SDI e em alguns casos o /SS, deixam de operar como I/Os convencionais.
108
Por último, basta escrever o dado que se deseja transmitir no registrador SSPBUF. Se isso for feito no
Master, a transmissão será iniciada imediatamente após a escrita. Para o Slave, o sistema ficará aguardando o clock. No término dos oito pulsos do clock, o dado terá sido enviado do Master para o Slave e vice-versa, como já explicado. Os registradores SSPBUF serão, então, atualizados e o flag SSPSTAT<BF> indicará que o buffer está cheio:
BF Descrição 0 Recebimento em operação. SSPBUF está vazio.1 Recebimento terminado. SSPBUF carregado.
Esse bit é limpo automaticamente toda vez que o registrador SSPBUF é lido, indicando que o programa
já processou a informação recebida. Neste mesmo momento o bit da interrupção PIR1<SSPIF> é ativado. A interrupção só acontecerá no
caso das suas chaves estarem ligadas. Caso seja escrito outro valor em SSPBUF antes do término da transmissão/recepção, ele será ignorado,
não afetando a operação. Entretanto, um bit será setado informando uma colisão de informações. Este bit é denominado SSPCON<WCOL> e deve ser limpo manualmente pelo programa. Por outro lado, caso o Slave receba um novo valor antes do SSPBUF ser lido (BF=1), este novo valor será perdido e o flag de overflow será ativado (SSPCON<SSPOV>=1). Este bit também deve ser limpo manualmente. Para o Master este flag não possui função.
Quanto ao modo SLEEP, o resultado é diferente quando aplicado ao Master ou ao Slave. No caso do Master, a comunicação será completamente interrompida, pois não é possível a geração do clock sem o oscilador estar funcionando. Por outro lado, para o Slave, a comunicação continuará funcionando e o PIC poderá acordar caso a interrupção de SSP esteja ligada.
Resumo dos registradores associados a SPI
Endereço Nome Bit 7 Bit 6 Bit 5 Bit 4 Bit 3 Bit 2 Bit 1 Bit 0 0Bh ... INTCON GIE PEIE T0IE INTE RBIE T0IF INTF RBIF OCh PIR1 PSPIF ADIF RCIF TXIF SSPIF CCP1IF TMR2IF TMR1IF 8Ch PIE1 PSPIE ADIE RCIE TXIE SSPIE CCP1IE TMR2IE TMR1IE 94h SSPSTAT SMP CKE D/A P S R/W UA BF 14h SSPCON WCOL SSPOV SSPEN CKP SSPM3 SSPM2 SSPM1 SSPM0 13h SSPBUF Buffer de transmissão / recepção
Curso Módulo 4 –Família 18F e MpLab C18 109
Teoria para I2C
O padrão de comunicação I2C possui características bem diferentes do SPI. Isso o torna muito mais poderoso, mas, em contrapartida, muito mais complexo também. Vejamos, então, suas principais características:
• Este protocolo continua com o conceito de Master/Slave, entretanto ele permite o endereçamento de
diversos pontos da rede por meio das próprias vias de comunicação. Com isso podemos ter um Master com diversos Slaves sem necessitarmos de pinos de controle adicionais. Na verdade, é possível também a estruturação de uma rede com diversos Mestres;
• A comunicação é feita somente em duas vias: Clock e Data. Como também se trata de uma comunicação síncrona, a via de clock continua sendo indispensável. Quanto aos dados, eles transitam em uma única via, tanto para a transmissão quanto para a recepção;
• Tanto o Master quanto o Slave podem transmitir e receber dados, mas o controle é sempre do Máster;
• Para evitar conflitos na via de dados (SDA), os pinos são chaveados como entrada ou saída (impõe somente o nível baixo, forçando o GND), conforme a necessidade imposta pelo protocolo. Por esse fato, um resistor de pull-up é necessário, sendo recomendado valores de 10kΩ a 1kΩ. Os valores mais baixos (até 1kΩ) podem ser necessários para velocidades muito elevadas ou uma rede com muitos periféricos;
• O clock (SCL) continua sendo totalmente controlado pelo Mestre. Acontece que, nesse protocolo, pelo compartilhamento de uma única via de dados e pela possibilidade de mais de um Mestre, o clock necessário para a resposta do Escravo deve ser dado no momento correto, evitando conflitos no processamento da informação. Para que isso seja possível, a via de clock também é alterada como entrada e saída (impõe somente o nível baixo, forçando o GND) durante a transmissão, ficando em determinados momentos em um estado flutuante. Por isso, é necessário que esta linha possua um resistor de pull-up para evitar esse estado flutuante e não gerar problemas no protocolo. Este resistor deve ser de 10kΩ a 1kΩ, adotando-se os valores mais baixo quando a velocidade de transferência é maior. O detalhamento deste funcionamento será visto mais à frente;
• Fica claro, então, que o valor padrão tanto para SCL quanto para SDA é o nível alto(1), com esses pinos em alta impedância (entradas). Aproveitamos para comentar também que os bits são lidos sempre da borda de descida do SCL;
• Além desse protocolo permitir a especificação do endereço, existem dois modos diferentes para que isso seja feito: 7 bits ou 10 bits. No primeiro caso é possível acessar até 128 endereços diferentes. Para o segundo caso, este número eleva-se para 1024 endereços.
Com essas explicações básicas, podemos concluir que na comunicação existem quatro situações
distintas do protocolo que teremos de conhecer: • Transmissão do Master / Recepção do Slave, com endereço de 7 bits;
• Recepção do Master / Transmissão do Slave, com endereço de 7 bits;
• Transmissão do Master / Recepção do Slave, com endereço de 10 bits;
• Recepção do Master / Transmissão do Slave, com endereço de 10 bits.
Nas próximas páginas serão mostradas quatro cartas de tempo completas, para cada uma das situações apresentadas. Antes, porém, vamos conhecer alguns outros pontos importantes deste protocolo que aparecerão nos diagramas mencionados.
110
Condição de Start Trata-se de uma condição especial entre as vias SDA e SCL para informar à rede que uma transmissão
irá começar. Essa condição é controlada pelo Mestre e todos os Escravos podem ficar prontos, esperando um dado ser transmitido. Em rede de Múltiplos Mestres, essa condição serve também para informar aos demais Mestres que a linha está ocupada.
Essa condição é atingida respeitando-se o seguinte diagrama: em que TBRG é o tempo equivalente a um pulso de clock.
Condição de Stop Outro estado especial dos pinos SDA e SCL, com efeito contrário à condição de Start, pois informa a
toda a rede que a transmissão términou, ficando a mesma disponível. Uma condição de Start obriga uma posterior condição de Stop, para que o sistema não fique travado.
Curso Módulo 4 –Família 18F e MpLab C18 111
Condição de Re-Start É uma condição muito semelhante a de Start, mas que pode ser executada antes do Stop. Isso será
necessário, por exemplo, para a implementação da comunicação com endereço de 10 bits ou nos casos em que precisamos enviar novamente o primeiro byte antes de finalizarmos a comunicação através de um Stop.
Condição de Acknowledge (ACK) Na verdade, isso é uma resposta dada pelo Mestre ou pelo Escravo, no lugar do 9º bit, informando se o
dado foi corretamente recebido ou não. Um acknowledge alto (1) representa um erro, enquanto um acknowledge baixo (0) representa OK. No final de uma transmissão de dados do Escravo para o Mestre, isto é, após o último byte, o Mestre deve sempre responder ACK=1.
Transmissão de endereço Independentemente da informação a ser transmitida (endereço de 7 bits, endereço de 10 bits ou dados
de 8 bits), a comunicação sempre é feita em pacotes de 8 bits e mais um retorno de acknowledge. Desta forma, para cada informação transmitida são necessários nove pulsos no clock. O último bit do primeiro byte não faz parte da informação, pois é utilizado como um flag de marcação para escrita (W-Write) ou leitura (R-Read). Quando o Mestre deseja enviar dados ao escravo, esse bit recebe o valor 0 (zero). Quando ele deseja receber um valor do Escravo, ele começa perguntando com este bit em 1 (um). É por isso que o endereço só comporta 7 bits: são exatamente os sete que sobraram nesse mesmo byte.
Para o endereçamento de 10 bits, será necessária a transmissão inicial de 2 bytes só para o endereço. No primeiro byte, o bit menos significativo continua válido como R/W. Dos outros 7 bits, entretanto, somente dois serão considerados como a parte alta do endereço. Trata-se dos bits 2 e 1. Os bits de 7 a 3 devem ser escritos com o valor 11110. Após a transmissão do primeiro byte, SDA é transformado em entrada para checar o acknowledge ao ser dado o 9º pulso do clock. Se a resposta for afirmativa termos ACK=0.
Para a geração de um pulso no clock, o nível baixo é conseguido impondo-se o aterramento do pino SCL (como se ele fosse uma saída). Em seguida, para o nível alto, desliga-se o aterramento e o nível é conseguido pelo pull-up da linha. Durante todo o tempo do pulso, o pino SCL deve ser monitorado pelo Mestre e, caso ele desça, é porque algum Escravo está pedindo uma pausa no processo. O tempo do clock é, então, paralisado e o Mestre só volta a responder quando a linha de SCL estiver liberada.
Quando o clock estimer liberado, o Mestre irá iniciar a transmissão do segundo byte de endereço. Neste segundo byte estão os 8 bits menos significativos que faltam para completar o endereço de 10 bits. Ao término, um novo ACK será aguardado e conferido.
Transmissão de dados Depois do término da transferência da parte relacionada ao endereço, um dos dois lados (Mestre ou
Escravo) deverá transmitir um ou mais bytes de dados. Caso seja o Escravo que esteja transmitindo para o Mestre, para finalizar a comunicação o Mestre deve responder um ACK=1 ao término do último byte recebido, para só depois enviar uma condição de Stop. Isso se faz necessário para que o Escravo seja informado de que o Mestre não mais deseja receber nenhum dado, evitando conflitos quando a condição de Stop for gerada. Nesta condição o Escravo deve ter sua comunicação paralisada e resetada, ficando no aguardo de uma nova condição de Start. Com isso o protocolo fica mais robusto, evitando erros na contagem do número de bytes enviados.
112
Pausas Sempre que algum módulo Slave (ou até outro Master) necessitar de uma pausa na comunicação, por
exemplo para processar a informação recebida, ela será conseguida mantendo-se a linha de clock em nível baixo, isto é, em zero.
Diagramas de tempo Os diagramas apresentados nas páginas seguintes demonstram graficamente o protocolo I2C para os
quatro casos possíveis já comentados: 1 Transmissão do Master / Recepção do Slave, com endereço de 7 bits; 2 Recepção do Master / Transmissão do Slave, com endereço de 7 bits; 3 Transmissão do Master / Recepção do Slave, com endereço de 10 bits; 4 Recepção do Master / Transmissão do Slave, com endereço de 10 bits. Em cada um deles são mostradas as informações que trafegam pelas vias de Dados e de Clock. Além disso,
acima e abaixo dos desenhos das formas de onda existem também barras que representam qual dos dois lados está controlando a via. São duas barras para cada pino (SDA e SCL), uma representando o lado do Master e a outra o lado do Slave. Quando a barra está cinza significa que o pino está configurado como entrada e quando a barra está branca é porque o pino está impondo o nível 0 (baixo). Desta forma fica mais fácil visualizarmos o sentido de tráfego da informação:
1 Master para Slave; 2 Slave para Master.
Observando essas barras para o pino de clock (SCL) podemos ver claramente quando o Slave força
pausas na comunicação, impondo o nível baixo (zero) nesta via.
Curso Módulo 4 –Família 18F e MpLab C18 113
114
Curso Módulo 4 –Família 18F e MpLab C18 115
Recursos do PIC para I2C
Agora que as formas gerais do protocolo já estão explicadas, vamos ver como ativar e utilizar esse recurso no PIC 18F4520.
Antes de mais nada, vamos explicar melhor como o PIC controla os pinos SCL e SDA. Como já foi dito na teoria do I2C, para que o protocolo funcione corretamente, evitando conflitos na linha, cada um dos módulos ligados à rede deve somente impor nível baixo (0), sendo o nível alto imposto pelos resistores de pull-up. O hardware interno do PIC respeita essas condições. Para isso, entretanto, é necessário que ambos os pinos (SCL e SDA) sejam configurados inicialmente como entrada através do TRISC, para que os mesmos fiquem em alta impedância, possibilitando que o controle da I2C possa forçar o nível baixo quando necessário. A imposição do GND na linha é feita por intermédio de um transistor interno do tipo N. Este fato possibilita a checagem de colisão de dados na linha SDA, que será explicada posteriormente.
Caso um desses pinos, por exemplo o SCL, seja configurado como saída em nível alto, o sistema poderá continuar funcionando, pois quando a I2C não estiver impondo zero, a própria porta imporá 1. Isso não gerará problemas internos para o PIC, mas poderá gerar problemas de conflito na linha, caso seja utilizado mais de um Master ou caso algum Slave solicite uma pausa através da linha de clock.
Para facilitar o entendimento, dividiremos essas explicações em duas partes: uma dedicada ao Slave e outra ao Master.
Modo Slave Inicie sempre configurando os pinos SCL e SDA como entrada por meio do TRISC. Para configurarmos um PIC para trabalhar em modo Slave, devemos ajustar os bits de
SSPCON<SSPM3:SSPM0>:
SSPM3: SSPM0
Descrição
0110 I2C Slave, endereço de 7 bits. 0111 I2C Slave, endereço de 10 bits.
Obs: As demais opções de configuração dizem respeitos aos outros modos (SPI e I2C Master).
Depois é necessário também ajustar o bit SSPSTAT<SMP> que, neste caso, serve para habilitar um
sistema interno de tratamento das bordas quando operando em freqüência de 400kHz:
SMP Descrição 0 Tratamento desabilitado.
1 Tratamento habilitado, quando usando 400kHz.
O bit SSPSTAT<CKE> serve para configurar o tipo de Smith Trigger utilizado nos pinos SCL e SDA:
CKE Descrição
0 Entradas conforme especificações I2C.
1 Entradas conforme especificações SMBUS.
Por último é necessário ainda habilitar o sistema de comunicação através do bit SSPCON<SSPEN>:
SSPEN Descrição 0 Desabilita a comunicação.
1 Habilita a comunicação. Neste momento os pinos SCL e DAS deixam de operar como I/Os convencionais.
Feito isso, vejamos quais tarefas serão feitas automaticamente e quais deverão ser implementadas pelo
programa. Trataremos separadamente os possíveis casos de transferência de informações.
116
Recepção para endereçamento de 7 bits
1. Tanto SCL quanto SDA estão em sua configuração padrão, isto é, como entradas. A linha receberá então uma condição de Start.
2. A condição de Start é reconhecida e o bit SSPSTAT<S> é setado; porém nenhuma interrupção acontece.
3. O sistema aguardará, então, um byte com os 7-bits de endereço e mais o bit R/W=0, com oito pulsos de clock. Teremos SSPSTAT<R/W>=0. Como o byte recebido é equivalente a um endereço, então SSPSTAT<D/A>=0.
4. Neste momento, o endereço recebido é comparado automaticamente com o registrador SSPADD. Caso não seja o mesmo, a comunicação do lado Slave é resetada (ACK=1) e o mesmo ficará aguardando um novo Start. Caso o endereço seja idêntico, o bit SSPSTAT<BF> é setado, informando que um valor foi transferido para o buffer (SSPBUF), o pino SDA é transformado automaticamente em saída com nível baixo (ACK=0) e fica aguardando o 9o pulso de clock. Logo depois SDA é liberado, tornando-se novamente entrada. Neste momento também o flag PIR1<SSPIF> é setado e a interrupção pode acontecer (se as chaves estiverem corretamente ligadas). É importante que a interrupção aconteça para que seja feita uma leitura do SSPBUF, forçando novamente BF=0.
5. O sistema aguardará então um byte de dado, com oito novos pulsos de clock. Como agora o byte recebido é equivalente a um dado, então SSPSTAT<D/A>=1. Neste momento, também teremos BF=1 e o dado será colocado em SSPBUF.
6. Mais uma vez o pino SDA impõe um nível baixo para responder ACK=0 no 9º pulso do clock. SDA volta em seguida à condição de entrada.
7. Uma nova interrupção acontece (SSPIF=1) para que o dado recebido possa ser tratado.
8. Novos bytes de dados podem continuar sendo recebidos até que uma condição de Stop seja imposta na linha. Neste momento, a condição será reconhecida, setando o bit SSPSTAT<P> e finalizando a comunicação. A interrupção não é gerada.
9. Caso seja recebido uma condição de Re-Start (entre um Start e um Stop), o Slave ficará esperando um novo byte de endereço, com o R/W, e não mais um byte de dados.
Devido ao buffer duplo de recepção, isto é, o dado entra em SSPSR e depois é colocada em SSPBUF, a
informação recebida, pode então ser lida mesmo durante o recebimento do próximo byte. Depois que lemos SSPBUF, o flag BF é limpo automaticamente. Entretanto, caso o SSPBUF não seja lido antes da nova recepção terminar, o dado mais novo será perdido (SSPBUF não é atualizado) e o bit SSPCON<SSPOV> será setado, indicando um overlflow. SSPOV deve ser limpo manualmente pelo programa. Dependendo das condições de BF e SSPOV, a resposta automática do acknowledge pode ser negativa (ACK=1).
BF SSPOV
Atualiza SSPBUF ACK Interrupção SSPIF=1
0 0 Sim 0 (Ok) Sim 1 0 Não 1 (Erro) Sim 1 1 Não 1 (Erro) Sim
0 (Nota) 1 (Nota) Sim 1 (Erro) Sim Nota: Esta condição representa um erro de programação, pois o SSPBUF foi limpo e o bit de SSPOV não.
Transmissão para endereçamento de 7-bits
1. Tanto SCL quanto SDA estão em sua configuração padrão, isto é, como entradas. A linha receberá, então, uma condição de Start;
2. A condição de Start é reconhecida e o bit SSPSTAT<S> é setado; porém nenhuma interrupção acontece;
3. O sistema aguardará então um byte com os 7-bits de endereço e mais o bit R/W=1, com oito pulsos de clock. Teremos SSPSTAT<R/W>=1. Como o byte recebido é equivalente a um endereço, então SSPSTAT<D/A>=0;
Curso Módulo 4 –Família 18F e MpLab C18 117
4. O endereço recebido é comparado automaticamente com o registrador SSPADD. Caso não seja o mesmo, a comunicação do lado Slave é resetada (ACK=1) e o mesmo ficará aguardando um novo Start. Caso o endereço seja idêntico, o pino SDA é transformado automaticamente em saída com nível baixo (ACK=0) e o sistema aguarda o 9º pulso de clock. Logo depois SDA é transformado novamente em entrada;
5. O sistema automaticamente altera SSPCON<CKP>=0, desabilitando o clock (pausa). Isso é feito impondo-se nível baixo (0) em SCL (saída);
6. O flag PIR1<SSPIF> é, então, setado e a interrupção pode acontecer (se as chaves estiverem corretamente ligadas). É importante que a interrupção aconteça para que seja escrito o valor a ser transmitido em SSPBUF, o que irá fazer com que BF=1;
7. Depois do buffer atualizado, para que a comunicação proceda, basta impor CKP=1. Neste momento, SCL será novamente liberado (entrada);
8. O clock será liberado e o byte existente em SSPBUF será enviado nos próximos oito pulsos. Ao término teremos BF=0;
9. Mais uma vez o pino SDA fica aguardando um ACK no 9º pulso do clock. Caso seja recebido um ACK=1 é porque a comunicação será encerrada, sendo aguardada, então, uma condição de Stop. Neste momento a condição será reconhecida, setando-se o bit SSPSTAT<P> e finalizando-se a comunicação. A interrupção não é gerada. Caso ACK=0, o sistema volta ao item 5 para que um novo byte de dado seja transmitido;
10. Durante a transferência de dados não pode acontecer uma situação de Re-Start. Para reiniciar completamente o sistema é necessário terminar a transferência com um ACK=1 por parte do Master, seguido de um Stop. Só então uma nova condição de Start poderá ser gerada;
Caso tente-se escrever um valor em SSPBUF enquanto uma transmissão está em progresso (BF=1) o valor
de SSPBUF não será atualizado e o bit SSPCON<WCOL> será setado, indicando uma colisão na escrita. Este bit deve ser limpo manualmente pelo programador.
Recepção para endereçamento de 10 bits
1. Tanto SCL quanto SDA estão em sua configuração padrão, isto é, como entradas. A linha receberá, então, uma condição de Start;
2. A condição de Start é reconhecida e o bit SSPSTAT<S> é setado; porém nenhuma interrupção acontece;
3. O sistema aguardará então um byte com os 2-bits de endereço (A9:A8) e mais o bit R/W=0, com oito pulsos de clock. Teremos, então, SSPSTAT<R/W>=0. Como o byte recebido é equivalente a um endereço, teremos também SSPSTAT<D/A>=0;
4. Neste momento o endereço recebido é comparado automaticamente com o registrador SSPADD. Caso não seja o mesmo, a comunicação do lado Slave é resetada (ACK=1) e o mesmo ficará aguardando um novo Start. Caso esta parte do endereço esteja correta, o bit SSPSTAT<BF> é setado, informando que um valor foi transferido para o buffer (SSPBUF) e teremos SSPSTAT<UA>=1, indicando que o endereço deve ser atualizado. O pino SDA é transformado automaticamente em saída com nível baixo (ACK=0) e fica aguardando o 9º pulso de clock. Logo depois, SDA é transformado novamente em entrada. Neste momento, o flag PIR1<SSPIF> é setado e a interrupção pode acontecer (se as chaves estiverem corretamente ligadas). É importante que a interrupção aconteça para que seja feita uma leitura do SSPBUF, forçando novamente BF=0. O registrador SSPADD deve, então, ser atualizado com a parte baixa do endereço (A7:A0), o que forçará automaticamente UA=0. Depois do ACK e até o momento em que o SSPADD é atualizado, o Slave trava a comunicação (pausa) impondo um nível baixo (0) em SCL. Este controle é totalmente automático;
5. Será aguardado, então, um byte com a segunda parte do endereço (A7:A0), para os próximos oito pulsos do clock. Como o byte recebido ainda é equivalente a um endereço, continuamos com SSPSTAT<D/A>=0;
6. Todo o processo descrito para o item 4 será então repetido. No final, o sistema entra em nova pausa até que SSPADD seja novamente atualizado com a parte alta do endereço (A9:A8);
118
7. O sistema aguardará então um byte de dado, com mais oito pulsos de clock. Como o byte recebido é equivalente a um dado, então SSPSTAT<D/A>=1. Neste momento também teremos BF=1 e o dado será colocado em SSPBUF;
8. O pino SDA é transformado automaticamente em saída com nível baixo (ACK=0) e o sistema aguarda o 9º pulso do clock. Logo depois SDA é transformado novamente em entrada;
9. Uma nova interrupção acontece (SSPIF=1) para que o dado recebido possa ser tratado;
10. Novos bytes de dados podem continuar sendo recebidos até que uma condição de Stop seja imposta na linha. Neste momento, a condição será reconhecida, setando-se o bit SSPSTAT<P> e finalizando-se a comunicação. A interrupção não é gerada;
11. Caso seja recebido uma condição de Re-Start (entre um Start e um Stop), o Slave ficará esperando um novo byte de endereço, com o bit de R/W e não mais um byte de dados;
Os comentários feitos a respeito do buffer e dos flags BF e SSPOV para a comunicação de 7-bits também são válidos aqui.
Transmissão para endereçamento de 10 bits
1. Tanto SCL quanto SDA estão em sua configuração padrão, isto é, como entradas. A linha receberá, então, uma condição de Start;
2. A condição de Start é reconhecida e o bit SSPSTAT<S> é setado; porém nenhuma interrupção acontece;
3. O sistema aguardará então um byte com os 2 bits de endereço (A9:A8) e mais o bit R/W=0, com oito pulsos de clock. Teremos, então, SSPSTAT<R/W>=0. Como o byte recebido é equivalente a um endereço, teremos também SSPSTAT<D/A>=0;
4. Neste momento, o endereço recebido é comparado automaticamente com o registrador SSPADD. Caso não seja o mesmo, a comunicação do lado Slave é resetada (ACK=1) e ficará aguardando um novo Start. Caso esta parte do endereço esteja correta, o bit SSPSTAT<BF> é setado, informando que um valor foi transferido para o buffer (SSPBUF) e teremos SSPSTAT<UA>=1, indicando que o endereço deve ser atualizado. O pino SDA é transformado automaticamente em saída com nível baixo (ACK=0) enquanto aguarda o 9º pulso do clock. Logo depois SDA é transformado novamente em entrada. Neste momento, o flag PIR1<SSPIF> é setado e a interrupção pode acontecer (se as chaves estiverem corretamente ligadas). É importante que a interrupção aconteça para que seja feita uma leitura do SSPBUF, forçando novamente BF=0. O registrador SSPADD deve, então, ser atualizado com a parte baixa do endereço (A7:A0), o que forçará automaticamente UA=0. Depois do ACK e até o momento em que o SSPADD é atualizado, o Slave trava a comunicação (pausa) impondo um nível baixo (0) em SCL. Esse controle é totalmente automático;
5. Será aguardado então um byte com a segunda parte do endereço (A7:A0) nos próximos oito pulsos do clock. Como o byte recebido ainda é equivalente a um endereço, continuamos com SSPSTAT<D/A>=0;
6. Todo o processo descrito para o item 4 será repetido. No final o sistema entra em pausa até que SSPADD seja novamente atualizado com a parte alta do endereço (A9:A8);
7. A linha fornecerá, então, uma condição de Re-Start, informando que o próximo byte é novamente uma informação de controle;
8. Será aguardado, então, outro byte com a parte alta do endereço e com R/W=1, para os próximos oito pulsos do clock. Como o byte recebido ainda é equivalente a um endereço, continuamos com SSPSTAT<D/A>=0. O valor recebido será colocado em SSPBUF e BF=1;
9. O pino SDA é transformado automaticamente em saída com nível baixo (ACK=0), enquanto o sistema aguarda o 9º pulso do clock. Logo depois, SDA é transformado novamente em entrada.
10. O sistema automaticamente altera SSPCON<CKP>=0, desabilitando o clock (pausa). Isso é feito transformando SCL em saída com nível baixo (0);
11. O flag PIR1<SSPIF> é, então, setado e a interrupção pode acontecer (se as chave estiverem corretamente ligadas). É importante que a interrupção aconteça para que SSPBUF seja lido, forçando BF=0 novamente. Depois devemos escrever o valor a ser transmitido em SSPBUF, o que irá fazer com que BF=1;
Curso Módulo 4 –Família 18F e MpLab C18 119
12. Depois do buffer atualizado, para que a comunicação proceda, basta impor CKP=1. Neste momento, SCL será novamente liberado;
13. O clock será liberado e o byte existente em SSPBUF será enviado nos próximos oito pulsos. Ao término teremos BF=0;
14. Mais uma vez o pino SDA fica aguardando um ACK no 9º pulso do clock. Caso seja recebido um ACK=1 é porque a comunicação será encerrada, sendo aguardada então uma condição de Stop. Neste momento, a condição será reconhecida, setando-se o bit SSPSTAT<P> e finalizando-se a comunicação. A interrupção não é gerada. Caso ACK=0, o sistema volta ao item 5 para que um novo byte de dado seja transmitido;
15. Durante a transferência de dados não pode acontecer uma nova situação de Re-Start. Para reiniciar completamente o sistema é necessário terminar a transferência com um ACK=1 por parte do Master, seguido de um Stop. Só então uma nova condição de Start poderá ser gerada.
A condição de colisão (WCOL) descrita na comunicação de 7-bits também é valida para a de 10-bits.
Tratamento da interrupção SSP Como só existe uma interrupção de porta serial (SSP) e diversas condições, os bits adicionais desta porta
devem ser checados para sabermos exatamente que atitude deve ser tomada. Veja algumas condições:
BF UA D/A SSPOV Situação
1 X X X
Sempre que a interrupção acontecer BF=1, é porque alguma informação foi colocada em SSPBUF. Para o caso da transmissão, este flag pode ser checado para sabermos se o processo já términou, evitando uma colisão (WCOL).
1 X 0 X Foi recebido um endereço. Como o endereço é checado automaticamente, a interrupção deve somente ler SSPBUF para limpar BF.
1 1 0 X Somente quando configurada comunicação em 10 bits. A interrupção deverá atualizar o endereço, primeiro com o valor baixo e da segunda vez com o valor alto.
1 0 0 X Esta situação só acontece para 7 bits ou quando recebido o segundo byte de controle para a transmissão quando operando em 10 bits, onde também teremos R/W=1.
1 X X 1 Significa que a última informação recebida foi perdida, pois o SSPBUF não havia sido limpo.
Endereçamento global O Slave possui ainda um último recurso, que podemos chamar de endereço global. Isso significa que o
endereço 0 (zero) poderá ser aceito automaticamente por todos os PICs. Desta forma, cada unidade da rede possui um endereço específico (SSPADD) e um endereço global (Zero).
Para habilitar o uso de endereço global em um módulo Slave, basta ativar o bit SSPCON2<GCEN>:
GCEN Descrição 0 Endereço global (Zero) desabilitado. 1 Endereço global (Zero) habilitado.
Quando o endereço global é inserido na rede, todos os módulos Slave irão receber a informação e todos aqueles com GCEN deverão responder com um ACK. Isso nos lembra um comentário muito importante. Quando ACK=0, o Slave coloca SDA como saída em nível baixo. Porém, quando ACK=1, SDA continua como entrada e o nível alto é imposto pelo resistor de pull-up. Isso possibilita que diversos módulos respondam ACKs diferentes, tendo sempre a preferência o erro, ou seja, ACK=0.
Com a habilitação do endereço global a interrupção será gerada e o tratamento ou não das informações será uma escolha do programador.
120
Resumo dos registradores associados a I2C Slave
Endereço Nome Bit 7 Bit 6 Bit 5 Bit 4 Bit 3 Bit 2 Bit 1 Bit 0 0Bh ... INTCON GIE PEIE T0IE INTE RBIE T0IF INTF RBIF OCh PIR1 PSPIF ADIF RCIF TXIF SSPIF CCP1IF TMR2IF TMR1IF 8Ch PIE1 PSPIE ADIE RCIE TXIE SSPIE CCP1IE TMR2IE TMR1IE 94h SSPSTAT SMP CKE D/A P S R/W UA BF 14h SSPCON WCOL SSPOV SSPEN CKP SSPM3 SSPM2 SSPM1 SSPM0 91h SSPCON2 GCEN ACKSTAT ACKDT ACKEN RCEN PEN RSEN SEN 13h SSPBUF Buffer de transmissão / recepção 93h SSPADD Endereço (parte alta ou parte baixa)
Modo Master
O Master não diferencia automaticamente o tipo de comunicação em 7 ou 10-bits, ele somente transmite e recebe informações de 8-bits. Por isso, toda a lógica da comunicação deve ser feita por intermédio do programa. Internamente, o PIC possui controle de hardware para os seguintes eventos:
• Gerar/Monitorar condição de Start;
• Gerar/Monitorar condição de Stop;
• Gerar/Monitorar condição de Re-Start;
• Gerar/Monitorar condição de Pausa;
• Gerar/Monitorar Acknowledge;
• Gerar/Monitorar o clock;
• Transmitir informação de 8-bits;
• Receber informação de 8-bits. Inicie sempre configurando os pinos SCL como saída e SDA como entrada através do TRISC. Para configurarmos um PIC para trabalhar em modo Master, devemos ajustar os bits de
SSPCON<SSPM3:SSPM0>:
SSPM3: SSPM0
Descrição
1000 I2C Master, controle por hardware com Clock = FOSC / (4 x (SSPADD+1))
1001 Reservado 1010 Reservado 1011 Reservado 1100 Reservado 1101 Reservado 1110 Reservado 1111 Reservado
Obs: As demais opções de configuração dizem respeitos aos outros modos (SPI e I2C Slave).
Depois, é necessário também ajustar o bit SSPSTAT<SMP> que, neste caso, serve para habilitar um
sistema interno de tratamento das bordas, quando operando em freqüência de 400kHz:
SMP Descrição 0 Tratamento desabilitado 1 Tratamento habilitado, quando usando 400kHz
O bit SSPSTAT<CKE> serve para configurar o tipo de Smith Trigger utilizado nos pinos SCL e SDA:
CKE Descrição 0 Entradas conforme especificações I2C 1 Entradas conforme especificações SMBUS
A freqüência do clock deve ser configurada através do registrador SSPADD, respeitando-se a seguinte
fórmula:
Curso Módulo 4 –Família 18F e MpLab C18 121
14
.FreqF
SSPADD
OSC
−⎟⎟⎠
⎞⎜⎜⎝
⎛
=
As freqüências padronizadas para o protocolo I2C são as de 100kHz e 400kHz. Com o PIC, entretanto, é
possível configurar outras freqüências. O fato é que todos os periféricos da rede devem estar aptos a responder na freqüência escolhida. Por isso, o recomendável é verificar a freqüência mais alta suportada pelos periféricos e escolher o valor mais próximo possível dos padronizados.
Por último, é necessário ainda habilitar o sistema de comunicação através do bit SSPCON<SSPEN>:
SSPEN Descrição 0 Desabilita a comunicação.
1 Habilita a comunicação. Neste momento os pinos SCL e SDA deixam de operar como I/Os convencionais.
Feito isso, vejamos que tarefas serão feitas automaticamente e quais deverão ser implementadas pelo
programa. Trataremos separadamente os possíveis casos de transferência de informações.
Transmissão para endereçamento de 7 bits
1. Tanto SCL quanto SDA estão em sua configuração padrão, isto é, como entradas;
2. Deve ser imposta uma condição de Start na linha. Para isso, basta ativar o bit SSPCON2<SEN>. A condição de Start será, então, gerada automaticamente;
3. Ao término do Start, o flag da interrupção será ativado (SSPIF=1) e SEN=0. Deve-se, então, escrever em SSPBUF o endereço do periférico com o qual desejamos nos comunicar. O endereço deve ser escrito nos bits de 7 a 1. O bit 0 deve ser considerado como o valor R/W=0;
4. Automaticamente, logo após a escrita no registrador SSPBUF, teremos SSPSTAT<BF>=1, SSPSTAT<R/W>=1 e o byte será transmitido através de oito pulsos gerados em SCL. No término da transmissão teremos novamente BF=0;
5. O pino SDA aguarda um ACK (deve ser 0) no 9º pulso do clock. O valor recebido é armazenado no bit SSPCON2<ACKSTAT>. O valor de ACK não é checado automaticamente;
6. Neste momento, R/W=0 e SSPIF=1, podendo gerar uma nova interrupção;
7. O dado a ser transmitido é, então, colocado em SSPBUF. Teremos BF=1 e R/W=1;
8. Mais uma vez teremos a transmissão do byte através de oito pulsos em SCL, a partir do momento em que nenhum periférico esteja solicitando uma pausa. No término BF=0;
9. Novamente o pino SDA aguarda um ACK (deve ser 0) no 9o pulso do clock. O valor recebido é armazenado no bit SSPCON2<ACKSTAT>. O valor de ACK não é checado automaticamente;
10. Neste momento R/W=0 e SSPIF=1, podendo gerar outra interrupção;
11. Se um novo byte deve ser transmitido, o processo é reiniciado no item 7. Para interromper a transmissão, uma condição de Stop deve ser gerada. Isso é conseguido por intermédio do bit SSPCON2<PEN>=1;
12. No final da condição de Stop teremos automaticamente PEN=0 e SSPIF=1.
Recepção para endereçamento de 7 bits
1. Tanto SCL quanto SDA estão em sua configuração padrão, isto é, como entradas;
2. Deve ser imposta uma condição de Start na linha. Para isso, basta ativar o bit SSPCON2<SEN>. A condição de Start será, então, gerada automaticamente;
3. Ao término do Start, o flag da interrupção será ativado (SSPIF=1) e SEN=0. Deve-se, então, escrever em SSPBUF o endereço do periférico com o qual desejamos nos comunicar. O endereço deve ser escrito nos bits de 7 a 1. O bit 0 deve ser considerado como o valor R/W=1;
122
4. Automaticamente, logo após a escrita no registrador SSPBUF, teremos SSPSTAT<BF>=1, SSPSTAT<R/W>=1 e o byte será transmitido através de oito pulsos gerados em SCL. No término da transmissão teremos novamente BF=0;
5. O pino SDA aguarda um ACK (deve ser 0) no 9º pulso do clock. O valor recebido é armazenado no bit SSPCON2<ACKSTAT>. O valor de ACK não é checado automaticamente;
6. Neste momento, R/W=0 e SSPIF=1, podendo gerar uma nova interrupção;
7. O Master deve, então, ser colocado em modo de recepção, ajustando-se SSPCON2<RCEN>=1;
8. Assim que a linha de clock não estiver em pausa, será aguardado um byte de dados, para os próximos oito pulsos do clock. Como o byte recebido é equivalente a um dado, teremos SSPSTAT<D/A>=1. O valor recebido será colocado em SSPBUF e BF=1;
9. Neste momento, RCEN=0 (automaticamente) desligando-se o modo de recepção. Também teremos SSPIF=1, gerando uma nova interrupção;
10. Se um novo byte deve ser recebido, então o Master deverá responder ACK=0. Caso seja o último byte desejado, então ACK=1. Para responder o ACK, o valor desejado deve ser colocado no bit SSPCON2<ACKDT>. Liga-se a geração do sinal de ACK através de SSPCON2<ACKEN>=1;
11. Ao término do ACK teremos ACKEN=0 e SSPIF=1, gerando-se outra interrupção. Caso tenha sido respondido ACK=0, então o processo deve retornar ao item 5 para que um novo byte seja recebido. Se ACK=1, o processo deve ser finalizado através de uma condição de Stop que será gerada com SSPCON2<PEN>;
12. No final da condição de Stop teremos automaticamente PEN=0 e SSPIF=1.
Existem duas situações que podem gerar uma transmissão: • O Master efetua uma pergunta e deseja uma resposta: Neste caso, primeiramente será feita uma
recepção pelo Slave (pergunta) e depois uma transmissão (resposta). Entre a pergunta e a resposta pode ser gerada uma condição de Re-Start, não sendo necessário liberar a linha (Stop). Um exemplo disso são as memórias E2PROM externas. Primeiro devemos informar qual endereço desejamos acessar para que depois ela responda o valor contido no endereço em questão.
• O Master só deseja uma resposta: Em casos de Slaves mais "burros" é provável que somente com seu endereço já seja possível responder corretamente. Por exemplo, para um módulo de teclado, basta que ele transmita o número da tecla pressionada, não precisando processar perguntas diferentes.
Transmissão para endereçamento de 10 bits
1. Tanto SCL quanto SDA estão em sua configuração padrão, isto é, como entradas;
2. Deve ser imposta uma condição de Start na linha. Para isso, basta ativar o bit SSPCON2<SEN>. A condição de Start será, então, gerada automaticamente;
3. Ao término do Start, o flag da interrupção será ativado (SSPIF=1) e SEN=0. Deve-se, então, escrever em SSPBUF a parte alta do endereço do periférico com o qual desejamos nos comunicar. Os 2 bits do endereço (A9:A8) devem ser escritos nos bits de 2 e 1. O bit 0 deve ser considerado como o valor R/W=0. Os demais bits (7:3) devem conter os valores 11110;
4. Automaticamente, logo após a escrita no registrador SSPBUF, teremos SSPSTAT<BF>=1, SSPSTAT<R/W>=1 e o byte será transmitido através de oito pulsos gerados em SCL. No término da transmissão teremos novamente BF=0;
5. O pino SDA aguarda um ACK (deve ser 0) no 9º pulso do clock. O valor recebido é armazenado no bit SSPCON2<ACKSTAT>. O valor de ACK não é checado automaticamente;
6. Neste momento, R/W=0 e SSPIF=1, podendo gerar uma nova interrupção;
7. A segunda parte do endereço (A7:A0) é, então, escrita em SSPBUF;
8. Automaticamente, logo após a escrita no registrador SSPBUF, teremos SSPSTAT<BF>=1, SSPSTAT<R/W>=1 e o byte será transmitido através de oito pulsos gerados em SCL. No término da transmissão teremos novamente BF=0;
9. O pino SDA aguarda um ACK (deve ser 0) no 9º pulso do clock. O valor recebido é armazenado no bit SSPCON2<ACKSTAT>. O valor de ACK não é checado automaticamente.
Curso Módulo 4 –Família 18F e MpLab C18 123
10. Neste momento, R/W=0 e SSPIF=1, podendo gerar uma nova interrupção;
11. O dado a ser transmitido é, então, colocado em SSPBUF. Teremos BF=1 e R/W=1;
12. Mais uma vez teremos a transmissão do byte através de oito pulsos em SCL, a partir do momento em que nenhum periférico esteja solicitando uma pausa. No término BF=0;
13. Novamente o pino SDA aguarda um ACK (deve ser 0) no 9º pulso do clock. O valor recebido é armazenado no bit SSPCON2<ACKSTAT>. O valor de ACK não é checado automaticamente;
14. Neste momento, R/W=0 e SSPIF=1, podendo gerar outra interrupção;
15. Se um novo byte deve ser transmitido, o processo é reiniciado no item 11. Para interromper a transmissão, uma condição de Stop deve ser gerada. Isso é conseguido por intermédio do bit SSPCON2<PEN>=1;
16. No final da condição de Stop teremos automaticamente PEN=0 e SSPIF=1.
Recepção para endereçamento de 10 bits
1. Tanto SCL quanto SDA estão em sua configuração padrão, isto é, como entradas;
2. Deve ser imposta uma condição de Start na linha. Para isso, basta ativar o bit SSPCON2<SEN>. A condição de Start será, então, gerada automaticamente;
3. Ao término do Start, o flag da interrupção será ativado (SSPIF=1) e SEN=0. Deve-se, então, escrever em SSPBUF a parte alta do endereço do periférico com o qual desejamos nos comunicar. Os 2 bits do endereço (A9:A8) devem ser escritos nos bits de 2 e 1. O bit 0 deve ser considerado como o valor R/W=0. Os demais bits (7:3) devem conter os valores 11110;
4. Automaticamente, logo após a escrita no registrador SSPBUF, teremos SSPSTAT<BF>=1, SSPSTAT<R/W>=1 e o byte será transmitido através de oito pulsos gerados em SCL. No término da transmissão teremos novamente BF=0;
5. O pino SDA aguarda um ACK (deve ser 0) no 9º pulso do clock. O valor recebido é armazenado no bit SSPCON2<ACKSTAT>. O valor de ACK não é checado automaticamente;
6. Neste momento, R/W=0 e SSPIF=1, podendo gerar uma nova interrupção;
7. A segunda parte do endereço (A7:A0) é, então, escrita em SSPBUF;
8. Automaticamente, logo após a escrita no registrador SSPBUF, teremos SSPSTAT<BF>=1, SSPSTAT<R/W>=1 e o byte será transmitido através de oito pulsos gerados em SCL. No término da transmissão teremos novamente BF=0;
9. O pino SDA aguarda um ACK (deve ser 0) no 9º pulso do clock. O valor recebido é armazenado no bit SSPCON2<ACKSTAT>. O valor de ACK não é checado automaticamente;
10. Neste momento, R/W=0 e SSPIF=1, podendo gerar uma nova interrupção;
11. Deve ser gerada então uma condição de Re-Start. Para isso, basta ativar o bit SSPCON2<RSEN>=1;
12. Ao término do Re-Start, o flag da interrupção será ativado (SSPIF=1) e RSEN=0;
13. Mais uma vez devemos colocar em SSPBUF o valor 11110, seguido dos bits A9:A8 e o último bit como R/W=1. Teremos BF=1 e R/W=1;
14. Teremos novamente a transmissão do byte através de oito pulsos em SCL, a partir do momento em que nenhum periférico esteja solicitando uma pausa. No término BF=0;
15. Novamente o pino SDA aguarda um ACK (deve ser 0) no 9º pulso do clock. O valor recebido é armazenado no bit SSPCON2<ACKSTAT>. O valor de ACK não é checado automaticamente;
16. Neste momento, R/W=0 e SSPIF=1, podendo gerar outra interrupção;
17. O Master deve, então, ser colocado em modo de recepção, ajustando-se SSPCON2<RCEN>=1;
18. Assim que a linha de clock não estiver em pausa, será aguardado um byte de dados para os próximos oito pulsos do clock. Como o byte recebido é equivalente a um dado, teremos SSPSTAT<D/A>=1. O valor recebido será colocado em SSPBUF e BF=1;
19. Neste momento, RCEN=0 (automaticamente) desligando-se o modo de recepção. Também teremos SSPIF=1, gerando uma nova interrupção;
124
20. Se um novo byte deve ser recebido, então o Master deverá responder ACK=0. Caso seja o último byte desejado, então ACK=1. Para responder o ACK, o valor desejado deve ser colocado no bit SSPCON2<ACKDT>. Liga-se a geração do sinal de ACK através de SSPCON2<ACKEN>=1;
21. Ao término do ACK teremos ACKEN=0 e SSPIF=1, gerando-se outra interrupção. Caso tenha sido respondido ACK=0, então o processo deve retornar ao item 13 para que um novo byte seja recebido. Se ACK=1, o processo deve ser finalizado por intermédio de uma condição de Stop que será gerada com SSPCON2<PEN>;
22. No final da condição de Stop teremos automaticamente PEN=0 e SSPIF=1.
Outras considerações Existem outros bits e flags envolvidos com o Master que não foram comentados até o momento. Como um sistema pode possuir mais de um Master, as condições de Start e Stop geradas na linha
acarretarão na ativação de SSPIF, podendo gerar uma interrupção em qualquer Master. Os flags SSPSTAT<S> e SSPSTAT<P> informam, respectivamente, as condições de Start e Stop. Desta forma é possível que outros Masters percebam quando a linha está ou não ocupada.
Aqui valem também os comentários para os flags SSPCON<WCOL> e SSPCON<SSPOV> que indicam, respectivamente, uma escrita inválida (na hora errada) em SSPBUF e o recebimento de um novo byte sem a leitura do byte anterior.
As condições de Start, Stop, Re-Start e ACK não podem ser geradas fora dos momentos corretos, quando o modo de comunicação não está em stand-by. A situação de stand-by acontece sempre que o hardware da I2C acaba o controle de um evento qualquer.
Detecção de colisão na linha (Bus colision)
Outro recurso que o Master possui está relacionado à colisão de informações na linha de dados (SDA). Vejamos como isso funciona.
Para que o PIC possa impor o nível baixo da linha (normalmente nós consideramos uma condição de saída) existe um transistor interno conectando esse pino ao GND. Quando essa conexão está feita, a linha realmente está em nível baixo e não há o que monitorar. No outro caso, entretanto, quando esse transistor está em aberto, o pino é considerada uma entrada e o nível alto é imposto pelo resistor de pull-up (externo). Nesta situação, o PIC monitora a linha. Caso seja necessário transmitir um bit em “1”, (nível alto / transistor aberto) e a linha esteja em zero, é porque algum outro ponto da rede está em conflito de comunicação.
Quando isso acontecer, o bit PIR2<BCLIF> será forçado para “1”, e se PIE2<BCLIE>=1 a interrupção será gerada. O sistema de comunicação será inicializado (reset), voltando ao estado de stand-by.
As colisões podem acontecer durante a transmissão de dados e as condições de Start, Stop, Re-Start e ACK.
Resumo dos registradores associados a I2C Master
Endereço Nome Bit 7 Bit 6 Bit 5 Bit 4 Bit 3 Bit 2 Bit 1 Bit 0 0Bh ... INTCON GIE PEIE T0IE INTE RBIE T0IF INTF RBIF OCh PIR1 PSPIF ADIF RCIF TXIF SSPIF CCP1IF TMR2IF TMR1IF 8Ch PIE1 PSPIE ADIE RCIE TXIE SSPIE CCP1IE TMR2IE TMR1IE 94h SSPSTAT SMP CKE D/A P S R/W UA BF 14h SSPCON WCOL SSPOV SSPEN CKP SSPM3 SSPM2 SSPM1 SSPM0 91h SSPCON2 GCEN ACKSTAT ACKDT ACKEN RCEN PEN RSEN SEN 13h SSPBUF Buffer de transmissão / recepção 93h SSPADD Ajuste da freqüência
Curso Módulo 4 –Família 18F e MpLab C18 125
Capítulo 14 - Módulo USART
Introdução
O nome USART significa Universal Synchronous Asynchronous Receiver Transmitter. Com um nome desse, o negócio parece um tanto complicado, não é mesmo? Mas não é complicado não. Acontece que esse é um protocolo universal e possui dois modos distintos de trabalho: o sincronizado e o não-sincronizado. Mas vamos logo ao que interessa e tratemos de conhecer melhor este recurso.
Teoria
Como estávamos dizendo, a USART, que também é conhecida como SCI (Serial Communications Interface), possui dois modos de funcionamento, vejamos as características de cada um deles:
Modo Assíncrono A comunicação é feita somente com duas vias; entretanto, como este modo não é sincronizado, essas
duas vias são utilizadas para dados. Uma delas para transmissão (TX) e a outra para recepção (RX). Isso possibilita que as informações sejam enviadas e recebidas ao mesmo tempo, cada qual na sua via. Este recurso é conhecido como Full Duplex. Esse modo é o utilizado, por exemplo, na porta serial dos computadores, para implementar o padrão RS-232, mas pode ser utilizado para acesso a outros sistemas também.
Mas como é possível os dados serem transmitidos entre dois pontos se não há sincronismo entre eles? Quando estudamos a comunicação SSP, vimos que uma via era perdida exatamente para essa função. Era definida como clock do sistema e servia para informar os dois lados (Master e Slave) do momento correto de transmissão de cada bit. Como aqui não há essa via, a sincronização deve ser feita pela própria via de dados. Isso será conseguido através do Baud Rate ou velocidade de transmissão. Vejamos como funciona.
Comecemos definindo exatamente o que é o Baud Rate. Para que o sistema funcione, veremos que o tamanho dos dados (intervalo de cada bit) deve ser completamente padronizado, e ambos os lados devem estar ajustados para o mesmo valor. Como essa comunicação trabalha sempre com base nos bits, essa velocidade é normalmente indicada em bits por segundo, ou bps. Com ela somos capazes de calcular o tempo de duração de cada bit.
BaudRate1TBIT =
Com isso, existe somente um sincronismo de tempo feito para a transmissão/recepção de cada byte.
Esse sincronismo é conseguido através do Start bit. Devemos entender também que ambas as vias devem ser tratadas igualmente, pois o TX de um lado
deve estar conectado ao RX do outro, e vice-versa. Em ambos os lados, TX é sempre saída e RX é sempre entrada. Desta forma, quando falarmos de transmissão ou recepção, serve para qualquer uma das vias.
As vias possuem seu estado padrão como sendo o nível alto. Temos, então, uma situação de stand- -by. Quando um lado inicia uma transmissão, ele força seu TX para nível baixo, mantendo-o assim pelo tempo TBIT. Essa borda de descida é reconhecida pelo outro lado (em RX) e é suficiente para iniciar o processo de sincronização para recebimento desse byte. Este pulso em nível baixo é chamado de Start Bit.
Depois disso, os dois lados já sabem o que fazer. TX enviará então os 8 bits de dados, todos eles com o mesmo tamanho do Start Bit. Como RX soube exatamente o momento de início do Start Bit, ele deixa passar o tempo e depois coleta os 8 bits, pegando o dado mais ou menos no meio do tempo do bit (TBIT / 2).
Por último, para garantir o processo, TX envia um Stop Bit, que nada mais é que outro bit com valor fixo em 1, garantindo assim que a linha voltará ao seu estado padrão e o sistema voltará ao stand-by, ficando apto ao próximo dado. O lado RX deve considerar a leitura do Stop Bit para garantir que nenhum erro grosseiro aconteça com a recepção. Caso o Stop Bit seja 0 (zero), pode ter acontecido um erro de temporização, e no seu lugar foi lido o bit 8 ou, então, o próximo Start.
Repare também que o erro desse processo é acumulativo. Por exemplo, caso o lado TX esteja com sua velocidade no limite superior do erro, e o RX com a velocidade menor do que devia, cada bit será lido mais perto do começo do pulso. Como existem 10 bits ao total (Start + Dado + Stop), é possível que, no final, aconteça a leitura errada de um bit.
Vejamos o processo graficamente:
126
O primeiro caso de recepção está com a velocidade bem próxima à velocidade do transmissor, e por isso
não houve erro na recepção. Já no outro caso a velocidade está mais lenta, e houve erro no último bit e no Stop Bit. Por isso, é importante o acerto e a precisão da velocidade em ambos os lados. Quanto maior o Baud Rate, mais crítica é a situação.
Observe também que a ordem de transmissão dos bits é a inversa das comunicações SSP. Aqui, o bit menos significativo é enviado primeiro.
Esse padrão aceita também a comunicação com 9 bits, sendo que o bit adicional poder ser utilizado para dado, paridade ou endereçamento. A paridade nada mais é que uma confirmação matemática dos 8 bits de dados. Somando-se a quantidade de bits em 1 (incluindo dados e paridade), o resuldado correto deve ser um número par (quando utilizando paridade PAR) ou ímpar (quando utilizando paridade ÍMPAR). Quanto ao endereçamento, respeita-se o seguinte critério: 0 para dado e 1 para endereço.
O importante é que ambos os lados (TX e RX) estejam configurados para operar com a mesma quantidade de bits. A configuração mais comum é a de 8 bits de dado (sem paridade) com 1 bit de Stop e é normalmente chamada de padrão 8N1.
No caso de comunicações padronizadas, o Baud Rate (BR) também obedece a valores pré- -ajustados, tais como 300, 1.200, 2.400, 9.600, 19.200bps e muitos outros.
Modo síncrono Este modo pode ser considerado como uma certa mistura entre os padrões SPI e I2C. Assim como no modo assíncrono, aqui também trabalhamos com somente duas vias, só que neste caso
uma é destinada ao clock (CK) e a outra aos dados (DT). Desta forma, os dados devem trafegar em uma única via, impossibilitando a transmissão e recepção simultâneas. É o mesmo conceito utilizado no padrão I2C. Essa comunicação é chamada de Half Duplex e pode ser utilizada para a troca de dados com outros microcontroladores ou diversos periféricos existentes no mercado, tais como A/Ds, D/As, memórias, etc.
Quanto à forma em que a informação trafega na linha, é bem mais simples que o padrão I2C, não possuindo o sistema de endereçamento e parecendo-se mais com o formato SPI. Para cada pulso (borda de descida) é transmitido um bit.
Este modo também opera com Mestre e Escravo, sendo o clock sempre gerenciado pelo Mestre. Para o Mestre, a via CK é sempre uma saída e para o Escravo ela é sempre uma entrada. Quanto à via de dados, ela muda constantemente de sentido, hora para a transmissão, hora para a recepção. Assim sendo, para qualquer uma das pontas, a via DT é saída para a transmissão e entrada para recepção.
O tempo de duração de um bit também define o Baud Rate, do mesmo modo descrito na comunicação assíncrona.
Neste caso, o nível baixo (0) é o padrão para a via CK. Para a via DT, não existe um padrão obriga-tório, pois ela não opera sem CK. Porém, recomendamos mantê-la também em nível baixo quando não está sendo usada.
Observe que, neste padrão, também o bit menos significativo (bit 0) é enviado primeiro. Para o modo síncrono também é aceita a comunicação com 9 bits e, neste caso, o Mestre sempre
gerará pacotes de nove pulsos de clock. O importante é que ambos os lados da comunicação (Mestre e Escravo) estejam configurados para operar com 9 bits.
Curso Módulo 4 –Família 18F e MpLab C18 127
Recursos do PIC
Vamos estudar agora como operar com a USART do PIC 18F4520. Para continuarmos com a mesma linha de raciocínio, dividiremos mais uma vez parte do assunto entre os dois modos de operação, mas antes podemos explicar a maioria dos conceitos que são compartilhados por ambos.
Os pinos da comunicação (RC6/TX/CK) e (RC7/RX/DT) são os mesmos nos dois modos e são controlados diretamente pelo sistema da USART. Somente para evitarmos possíveis conflitos, caso a USART seja desabilitada, é recomendável que ambos sejam ajustados como entrada através do TRISC.
A definição entre os modos de operação é feita através de TXSTA<SYNC>:
SYNC Descrição 0 Modo Assíncrono 1 Modo Síncrono
O ajuste do Baud Rate (BR) é feito por meio de um registrador denominado SPBRG e do bit
TXSTA<BRGH>. A combinação desses parâmetros e do modo de operação definem o cálculo do BR:
BRGH Descrição 0 Ajuste para baixa velocidade 1 Ajuste para alta velocidade
SYNC BRGH=0 BRGH=1
0 BR = FOSC/(64x(SPBRG+1))
BR = FOSC/(16x(SPBRG+1))
1 BR = FOSC/(4x(SPBRG+1)) Não válido.
Primeiramente observe que o ajuste de BRGH não tem efeito quando estamos trabalhando no modo
síncrono. Neste caso, o tempo de cada bit (TBIT) será múltiplo do tempo de ciclo de máquina (TCY). Para o menor ajuste possível (SPBRG=0), teremos TBIT = TCY. Nesta situação, a parte alta do clock é gerada nos subtempos Q4 e Q1, ficando Q2 e Q3 para a parte baixa.
Por exemplo, para uma FOSC de 4MHz, teremos: BRMÁX = 1.000.000bps BRMÍN = 3.906bps Para o modo síncrono, BRGH surte efeito alterando a fórmula para o cálculo de BR. Observe que,
quando BRGH=0, TBIT será múltiplos de 64 TCY. Isso diminui a velocidade máxima e aumenta o erro para nos aproximarmos das velocidades padronizadas. Para o segundo caso (BRGH=1), teremos um ajuste mais preciso, pois TBIT será múltiplos de 16 TCY. Agora teremos um erro menor e um aumento da velocidade máxima. Por outro lado, perdemos na velocidade mínima.
Peguemos o mesmo exemplo dado para o caso do modo síncrono (FOSC = 4MHz), primeiro para BRGH=0:
BRMÁX = 62.500bps BRMÍN = 244bps
128
E agora para BRGH=1: BRMÁX = 250.000bps BRMÍN = 976bps Vamos, agora, calcular o valor de SPBRG para uma velocidade de 9.600bps, com cristal de 4MHz e
BRGH=0:
BR = FOSC /(64x(SPBRG+1))
SPBRG = (FOSC /(64 x BR)) - 1
SPBRG = (4.000.000 / (64 x 9.600)) - 1
SPBRG = 5,510 -> 5 Como houve um arredondamento, existirá um erro que deve ser calculado:
BR = 4.000.000 / (64 x (5+1))
BR = 10.416bps
Erro = (10.416 - 9.600) / 9.600
Erro = 8,5% (muito elevado) Vejamos o mesmo caso para BRGH=1:
BR = FOSC /(16x(SPBRG+1))
SPBRG = (FOSC /(16 x BR)) - 1
SPBRG = (4.000.000 / (16 x 9600)) - 1
SPBRG = 25,042 -> 25
BR = 4.000.000 / (16 x (25+1))
BR = 9.615bps
Erro = (9.615 - 9.600) / 9.600
Erro = 0,16% (muito bom!) Devido à grande diferença de erro, devemos adotar o segundo cálculo. Existem casos, princi-palmente
quando BR é elevado, que para melhorarmos a situação será necessário alterarmos o valor do oscilador (FOSC), tentando chegar em números mais precisos. Não esqueça de que existe ainda a tolerância de funcionamento do sistema de oscilação. O uso de cristais pode também ser obrigatório para as velocidades mais altas.
Para ativar o sistema da USART, configurando os pinos corretamente, deve-se ajustar o bit RCSTA<SPEN>:
SPEN Descrição
0 USART desabilitada. Pinos como I/Os convencionais.
1 USART habilitada. Pinos controlados automaticamente.
A partir deste ponto o usuário deve escolher entre as operações de transmissão e/ou recepção, mas,
como elas são bem diferentes para os modos assíncronos e síncronos, serão explicadas na divisão dos tópicos. Por enquanto, vejamos somente quais são e os bits que as controlam:
Curso Módulo 4 –Família 18F e MpLab C18 129
Operação Bit Observações Transmissão TXSTA<TXEN> Ativa o sistema de transmissão de bytes.
Recepção unitária RCSTA<SREN> Ativa o sistema de recepção de somente 1 byte. Somente
para modo síncrono. Recepção contínua RCSTA<CREN> Ativa o sistema de recepção contínua.
O que devemos explicar, que ainda é comum aos modos de operação, diz respeito ao dado que será
transmitido e/ou recebido. Primeiramente devemos optar pela comunicação com 8 ou 9 bits. Isso é feito separadamente para a
transmissão e para a recepção, através dos bits TXSTA<TX9> e RCSTA<RX9>:
TX9 Descrição 0 Transmissão feita em 8 bits 1 Transmissão feita em 9 bits
RX9 Descrição
0 Recepção feita em 8 bits 1 Recepção feita em 9 bits
Este 9º bit deve ser escrito em TXSTA<TX9D> para a transmissão e será recebido em RCSTA<RX9D>
do lado da recepção. O PIC não possui sistema automático para implementação desse bit como sendo a paridade. Se o uso da paridade for necessária, essa implementação terá de ser feita através do software. Existe um auxílio para uso desse bit como endereçamento, mas isso será visto posteriormente.
Quanto ao dado propriamente dito, para o caso da transmissão, ele deve ser escrito no registrador TXREG. Caso o sistema de transmissão esteja ligado, a simples escrita nesse registrador irá iniciá-la. Este dado será, então, transferido a um registrador interno denominado TSR (sem acesso pelo programa) para que seja enviado pela porta serial. Neste momento, TXREG fica vazio e liberado para uma nova escrita. Desta forma, o nosso buffer de saída é duplo, podendo haver um byte em TXREG e outro em TSR. O segundo byte só será passado de TXREG para TSR quando o último bit (7 ou 8) do byte anterior for transmitido, deixando TSR vazio.
Toda vez que o valor for passado de TXREG para TSR, o bit PIR1<TXIF> será setado, podendo gerar a interrupção. O interessante é que esse bit não precisa ser limpo manualmente como os demais flags de interrupção. Ele será limpo automaticamente sempre que TXREG for escrito. Isso serve para sabermos quando TXREG está liberado para uma nova escrita. O problema é que, se não desejamos transmitir nada, não escreveremos em TXREG e não limparemos TXIF. Isso irá travar o programa entrando sem parar na interrupção. A solução, para este caso, é desligar a interrupção ou mesmo a transmissão.
O bit TXIF não pode ser utilizado para sabermos que uma transmissão já foi completada, pois, depois de TXREG ser colocado em TSR, o dado ainda precisará ser enviado pela porta serial. Para saber quando realmente a transmissão foi finalizada, devemos saber quando TSR está vazio. O bit TXSTA<TRMT> possui esta função:
TRMT Descrição
0 Transmissão finalizada. TSR liberado. 1 Transmissão em curso. TSR ocupado.
Para a recepção, o dado recebido é primeiramente armazenado em um registrador interno não acessível
(RSR). Quando esse registrador está completo, a informação é então passada para o registrador RCREG. O mais interessante é que este registrador possui dois níveis de pilha, podendo ser escrito e lido duas vezes. Com isso nosso buffer de entrada é triplo, podendo haver um dado sendo recebido em RSR e mais dois já recebidos em RCREG. O registrador RCREG trabalha com o sistema FIFO (First In / First Out), isto é, o primeiro a ser recebido será o primeiro a ser lido.
Cada vez que o valor de RSR é transferido para RCREG o bit PIR1<RCIF> é setado, podendo gerar a interrupção. Como no caso da transmissão, esse bit é limpo automaticamente pelo hardware sempre que RCREG estiver vazio. Isso significa que, se foram recebidos 2 bytes antes de ser efetuada uma conferência, RCREG terá de ser lido duas vezes (pilha) para que o bit RCIF seja limpo. Caso o terceiro byte seja completado antes da leitura dos duas já existentes em RCREG, um erro de overflow irá acontecer e o bit RCSTA<OERR> será setado. O 3º byte que se encontra em RSR será perdido. Este bit deve ser limpo manualmente. Isso também pode ser conseguido desativando-se o modo de recepção.
Para trabalhar com o 9º bit, é necessário que ele seja escrito (TX9D) antes do dado em TXREG, possibilitando a atualização correta do TSR.
130
O sistema de recepção checa também o Stop Bit, mas não toma nenhuma atitude automática em caso de erro. A situação do Stop Bit é armazenada em RCSTA<FERR>:
FERR Descrição
0 O Stop Bit foi recebido corretamente (Stop Bit = 1). 1 O Stop Bit foi recebido errado (Stop Bit = 0).
Para que esse bit possa ser checado pelo programa, assim como o 9º (quando usado), uma leitura deve
ser feita antes em RCREG para que os mesmos sejam atualizados. Por último devemos comentar sobre o sistema de endereçamento. Estes sistema só funciona quando
ajustada a comunicação para 9 bits que é controlado através do bit RCSTA<ADDEN>:
ADDEN Descrição 0 Sistema de endereçamento desativado.1 Sistema de endereçamento ativado.
Quando esse sistema está ativo, o dado recebido só é transferido de RSR para RCREG quando o 9º for
1, podendo então gerar a interrupção. Caso seja recebido uma informação com o 9º em 0, ela será reconhecida como dado e será descartada. Para que o processo funcione corretamente é necessário, então, começarmos com o endereçamento ativo para recebermos um endereço que será tratado e comparado como o endereço da própria unidade. O endereço será o byte recebido, possibilitando 256 valores diferentes. Caso o endereço recebido corresponda à unidade em questão, o sistema de endere-çamento deve então ser desativado para que o próximo valor possa ser recebido como um dado válido.
Vejamos agora as particularidades e observações do roteiro de trabalho de cada modo.
Modo Assíncrono
Para o modo assíncrono, quando habilitamos a USART através do bit RCSTA<SPEN>, o pino TX é transformado em saída com nível alto (1) e o pino RX é transformado em entrada.
Para ativar o sistema de recepção, basta tornar RCSTA<CREN>=1. Para esse modo não é possível escolher o sistema de recepção unitária e, por isso, RCSTA<SREN> não possui função.
A partir deste momento cada byte recebido será colocado em RCREG e a interrupção será ativada através de RCIF. A lógica do sistema será, então, tratar das informações recebidas. É recomendável também a implementação da checagem do Stop Bit (FERR) e do estouro de recepção (OERR).
Para ativar o sistema de transmissão, basta tornar TXSTA<TXEN>=1. O importante é lembrar de que os sistemas de transmissão e recepção são totalmente independentes (só compartilham o mesmo Baud Rate) e, por isso, cada um deles pode ser ativado e desativado, conforme a necessidade.
Depois da ativação, basta escrever um valor em TXREG e o mesmo será enviado automaticamente pela porta serial, com a geração do Start Bit e do Stop Bit. O importante é a checagem do bit TXIF para que o sistema não sobre escreva TXREG antes de TSR ser atualizado.
Quando um dos sistemas é desativado (Transmissão ou Recepção), o pino relacionado a ele é mantido como entrada.
Este modo não pode operar em SLEEP.
Resumo dos registradores associados à USART Assíncrona
Endereço Nome Bit 7 Bit 6 Bit 5 Bit 4 Bit 3 Bit 2 Bit 1 Bit 0 0Bh ... INTCON GIE PEIE T0IE INTE RBIE T0IF INTF RBIF OCh PIR1 PSPIF ADIF RCIF TXIF SSPIF CCP1IF TMR2IF TMR1IF8Ch PIE1 PSPIE ADIE RCIE TXIE SSPIE CCP1IE TMR2IE TMR1IE98h TXSTA CSRC TX9 TXEN SYNC - BRGH TRMT TX9D 18h RCSTA SPEN RX9 SREN CREN ADDEN FERR OERR RX9D 19h TXREG Buffer de transmissão 1Ah RCREG Buffer de recepção 99h SPBRG Acerto do Baud Rate
Curso Módulo 4 –Família 18F e MpLab C18 131
Modo Síncrono Neste modo existe mais um bit de configuração relacionado ao sistema Master/Slave. Trata-se do
TXSTA<CSRC>:
CSRC Descrição 0 Slave (CK como entrada). 1 Master (CK como saída).
Com essa seleção, o sistema configura corretamente o estado do pino de clock (CK). O estado do pino de dados (DT) será variável, conforme o sistema se encontre em recepção ou transmissão. Devemos, então, escolher qual operação desejamos efetuar:
Operação Bit Observações
Transmissão TXSTA<TXEN>
O pino DT será colocado como saída e o sistema está pronto para transmitir o dado escrito em TXREG. A escolha de um modo de recepção tem prioridade sobre este modo.
Recepção unitária RCSTA<SREN> O pino DT é colocado como entrada e o sistema de
recepção de somente 1 byte é ativado. Recepção contínua RCSTA<CREN> O pino DT é colocado como entrada e o sistema de
recepção contínua é ativado.
Transmissão No caso do Master, o sistema funciona de forma muito parecida com o descrito anteriormente para o
modo assíncrono, operando da mesma forma com TXREG, TXIF e TRMT. A única diferença é que, quando escrevemos algum dado em TXREG, o mesmo será transmitido por DT, com pulsos sendo gerados em CK e sem a presença de Start ou Stop bit. CK pode gerar oito ou nove pulsos, dependendo do estado de TXSTA<TX9>.
Ao término da transmissão, o sistema ficará em stand-by, aguardando um novo dado em TXREG. Para o Slave, a diferença é que, depois de escrevermos em TXREG, o sistema ficará aguardando os
clocks enviados pelo Master.
Recepção A recepção para o Master e para o Slave também é idêntica, exceto pela geração do clock. Quando
ativada uma operação de recepção, a operação de transmissão é desligada imediatamente e o pino DT torna-se entrada.
A recepção contínua manterá a unidade recebendo dados até que o bit RCSTA<CREN> seja limpo manualmente. Já a recepção unitária (RCSTA<SREN>=1) receberá somente um byte e depois desliga- -se (RCSTA<SREN>=0). Se por acaso forem ligados as duas maneiras de recepção, o modo contínuo terá prioridade. No modo contínuo, o clock não é desativado, gerando pulsos constantes. Isso torna a operação nesse sistema mais difícil.
Ao receber um dado, o mesmo será enviado a RCREG e o flag PIR1<RCIF> será ativado, podendo gerar a interrupção.
Operação em SLEEP Somente as unidades Slaves do modo síncrono podem operar em SLEEP. Quando um dado é recebido durante o SLEEP, ao final da recepção a informação é transportada de
RSR para RCREG, ativando o flag de interrupção PIR1<RCXIF>. Deixando essa interrupção habilitada, o PIC será acordado e o dado recebido poderá ser tratado.
Uma transmissão também é possível durante o SLEEP. O primeiro dado escrito em TXREG será imediatamente escrito em TSR. O segundo byte (se houver) será mantido em TXREG. O sistema é, então, colocado em SLEEP. Quando chegar o primeiro pacote de pulsos, o dado de TSR será transmitido. Ao final, o segundo dado será colocado em TSR e o flag TXIF será setado. Se a interrupção estiver habilitada, o PIC acordará.
132
Resumo dos registradores associados à USART Síncrona
Endereço Nome Bit 7 Bit 6 Bit 5 Bit 4 Bit 3 Bit 2 Bit 1 Bit 0 0Bh ... INTCON GIE PEIE T0IE INTE RBIE T0IF INTF RBIF OCh PIR1 PSPIF ADIF RCIF TXIF SSPIF CCP1IF TMR2IF TMR1IF 8Ch PIE1 PSPIE ADIE RCIE TXIE SSPIE CCP1IE TMR2IE TMR1IE98h TXSTA CSRC TX9 TXEN SYNC - BRGH TRMT TX9D 18h RCSTA SPEN RX9 SREN CREN ADDEN FERR OERR RX9D 19h TXREG Buffer de transmissão 1Ah RCREG Buffer de recepção 99h SPBRG Acerto do Baud Rate
Curso Módulo 4 –Família 18F e MpLab C18 133
Capítulo 15 - Variáveis Organizadas
Introdução
A linguagem C, possui um método para organizar os tipos de variáveis simples, que já conhecemos. Elas podem ser organizadas de três formas:
• Homogêneas: variáveis de mesmo tipo, organizada na forma de vetor ou matriz. • Heterogêneas: variáveis de tipos diferentes, organizadas em forma de estruturas. • Sobrepostas: variáveis de mesmo tipo ou tipos diferentes organizadas em forma de uniões.
Matriz
Chamamos de vetor, uma matriz unidimensional. Como já foi falado ele é formado por variáveis de mesmo tipo.
Podemos ter matrizes bidimensionais, a quantidade de dimensões de uma matriz é limitada pelo compilador C utilizado.
O MPLAB C18 nos permite criar matrizes de até 5 dimensões, quando estamos falando de matrizes em memória RAM, estas podem gastar no máximo 256 bytes. Cada banco de RAM possui 256 bytes, uma matriz não pode ultrapassar a capacidade de um banco de RAM.
Criando um Vetor Os vetores podem ser criados na memória de programa ou na memória RAM. A quantidade de memória usada em um vetor depende do tipo de variável e do tamanho do vetor,
(quantidade de células). Para criar um vetor devemos informar: tipo de variável, nome do vetor e tamanho. Podemos utilizar todos os tipos de variáveis da linguagem C para criar um vetor, para dar um nome a um
vetor, devemos seguir as mesmas regras adotadas para variáveis, o tamanho define a quantidade de células de memória do nosso vetor.
Criando um vetor na memória RAM Vejamos como criar um vetor na memória RAM: tipo_de_variável nome_da_variável [tamanho]; Exemplo: unsigned char vetor_teste [200]; No exemplo acima foram utilizados 200 bytes de memória RAM, o vetor_teste possui duzentas células, o
tipo de variável é de 1 byte, logo 1 byte x 200 células = 200 bytes.
Iniciando um Vetor Os vetores que forem criados na memória RAM podem ser inicializados, com valores já pré –
determinados. Os valores de inicialização devem ser colocados entre chaves e separados por virgula. Veja como é
feito: tipo_de_variável nome_da_variável [tamanho] = ...,...,...,...; Exemplo: unsigned char vetor_teste [5] = 10,20,30,40,50;
Criando um vetor na memória de programa Para criar um vetor na memória de programa devemos utilizar os qualificadores const e rom, logo a
sintaxe de um vetor na memória de programa será: const rom tipo_de_variável nome_da_variável [tamanho];
134
Exemplo: const rom unsigned char tabela[16] = 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16;
Este tipo de vetor pode ser utilizado, para criar tabelas na memória de programa. Desta forma podemos criar tabelas, para linearizar a curva de resposta de um sensor, para criar tabelas
de conversão do tipo binário para sete segmentos, entre outros tipos de tabela, enfim poderemos utilizar as tabelas de constantes em varias aplicações
Leitura e Escrita de um vetor Um vetor na memória RAM nos permite modificar o conteúdo de qualquer célula, a qualquer momento.
Da mesma forma acontece com a leitura podemos ler qualquer posição do vetor a qualquer momento. Um dos cuidados ao trabalhar com vetores é na utilização do índice do vetor, o índice é inserido no
campo tamanho do vetor. Ele será utilizado para informar qual posição do vetor desejamos ler ou escrever.
Leitura de uma célula do Vetor Para que possamos ler uma posição de um vetor, devemos informar através do índice a posição
desejada. Devemos tomar cuidado no momento de informar o valor do índice, este pode nos causar problemas se
apontar para uma posição que vai alem do tamanho do nosso vetor. Portanto o máximo valor do índice, deve ser o tamanho menos 1. Veja para um vetor de quatro células, teremos o índice máximo igual a três e o mínimo igual a zero.
unsigned char tabela[4] = 10, 20, 30, 40
32
10 Índice do vetor
Logo se efetuarmos está operação: auxiliar = tabela [1]; Vamos carregar no registrador auxiliar o valor da posição um do vetor tabela, portanto teremos auxiliar =
20. Para leitura de uma posição (célula) de um vetor podemos ter como índice uma constante como foi visto
no exemplo ou qualquer tipo de variável, até mesmo um vetor ou matriz.
1020304014
21243227282
0x2000x2010x2020x2030x2040x2050x2060x2070x2080x209
Vetortabela
auxiliar
102030402021243227282
0x2000x2010x2020x2030x2040x2050x2060x2070x2080x209
Vetortabela
auxiliar
Antes depois
Neste exemplo usaremos uma variável para indexar nossa tabela, o programa roda indefinidamente, a cada volta o PORTB é carregado com um dos valores da tabela.
unsigned char indice = 0; unsigned char tempo = 20; const rom unsigned char tabela [5] = 89, 97, 109, 08, 225;
Curso Módulo 4 –Família 18F e MpLab C18 135
void main () while (1) //inicio do programa. PORTB = tabela[indice++]; //carrega o PORTB com o valor da tabela
//indexado pela variável indice. //1o indexa depois incrementa a variável indice
delay10TCYx(tempo); //delay de 200 ciclos de máquina. If (indice = 5) indice = 0; //indice igual ao valor máximo? //sim, zera a variável indice. //não, volta para o inicio do programa. Então para cada volta do programa teremos: Variável 1a volta 2a volta 3a volta 4a volta 5a volta 6a volta Índice = 0 1 2 3 4 5 → 0
PORTB = 89 97 109 08 225 89
Criando uma matriz multidimensional Uma matriz multidimensional é formada por uma associação de vetores (matriz unidimensional). Assim
sendo teremos um campo tamanho para cada matriz unidimensional da nossa matriz multidimensional. A sintaxe de uma matriz bidimensional ficará assim: tipo_de_variável nome_da_matriz [tamanho1] [tamanho2]; Exemplo: a)Matriz bidimensional #pragma idata MY_DATA2=0x400 // banco de RAM onde será criada a matriz unsigned char tabela_a[2][3] = 1,2,3,4,5,6;
Podemos assumir que tamanho1 representa a quantidade de linhas da matriz e tamanho2 a quantidade
de colunas. Contamos as linhas de 0 a 1 e as colunas de 0 a 2, logo temos uma matriz de 2 linhas e 3 colunas. A leitura das posições desta matriz será feita desta forma:
reg = tabela_a[0][0]; // Lê linha 0, coluna 0, reg = 1 reg = tabela_a[0][1]; // Lê linha 0, coluna 1, reg = 2 reg = tabela_a[0][2]; // Lê linha 0, coluna 2, reg = 3 reg = tabela_a[1][0]; // Lê linha 1, coluna 0, reg = 4 reg = tabela_a[1][1]; // Lê linha 1, coluna 1, reg = 5 reg = tabela_a[1][2]; // Lê linha 1, coluna 2, reg = 6
Matriz tridimensional Uma matriz tridimensional é formada por matrizes bidimensionais.A sintaxe para está matriz é: tipo_de_variável nome_da_matriz [tamanho1][ tamanho2][ tamanho3]; O campo de indicação de tamanho a matriz nos diz o seguinte: tamanho1 indica a quantidade de
matrizes bidimensionais, o tamanho2 a quantidade de linha e tamanho3 a quantidade de colunas da matriz.
No nosso exemplo temos uma matriz tridimensional formada por 3 matrizes bidimensionais, veja:
136
int matriz [3][2][3]= 10,20,30, // matriz 0 40,50,60,
70,80,90, // matriz 1 100,110,120, 130,140,150, // matriz 2 160,170,180 ;
A leitura de cada célula desta matriz pode ser realizada desta forma: reg=matriz[0][0][0]; // matriz 0, linha 0, coluna 0, reg = 10; reg=matriz[0][0][1]; // matriz 0, linha 0, coluna 1, reg = 20; reg=matriz[0][0][2]; // matriz 0, linha 0, coluna 2, reg = 30; reg=matriz[0][1][0]; // matriz 0, linha 1, coluna 0, reg = 40; reg=matriz[0][1][1]; // matriz 0, linha 1, coluna 1, reg = 50; reg=matriz[0][1][2]; // matriz 0, linha 1, coluna 2, reg = 60; reg=matriz[1][0][0]; // matriz 1, linha 0, coluna 0, reg = 70; reg=matriz[1][0][1]; // matriz 1, linha 0, coluna 1, reg = 80; reg=matriz[1][0][2]; // matriz 1, linha 0, coluna 2, reg = 90; reg=matriz[1][1][0]; // matriz 1, linha 1, coluna 0, reg = 100; reg=matriz[1][1][1]; // matriz 1, linha 1, coluna 1, reg = 110; reg=matriz[1][1][2]; // matriz 1, linha 1, coluna 2, reg = 120; reg=matriz[2][0][0]; // matriz 1, linha 0, coluna 0, reg = 130; reg=matriz[2][0][1]; // matriz 1, linha 0, coluna 1, reg = 140; reg=matriz[2][0][2]; // matriz 1, linha 0, coluna 2, reg = 150; reg=matriz[2][1][0]; // matriz 1, linha 1, coluna 0, reg = 160; reg=matriz[2][1][1]; // matriz 1, linha 1, coluna 1, reg = 170; reg=matriz[2][1][2]; // matriz 1, linha 1, coluna 2, reg = 180;
Para criar matrizes com mais de três dimensões devemos seguir o mesmo principio. Veja para criar uma matriz de quatro dimensões precisaremos de duas matrizes tridimensionais. Cada campo adicional de dimensão, irá informar a quantidade de matrizes necessárias, para formar uma
matriz de maior dimensão.
Strings Uma string é um conjunto de caractere, representado entre aspas, por exemplo “Grupo Mosaico”,e toda
string é finalizada por \0 (nulo). Uma das aplicações dos vetores (matriz unidimensional) é a criação de vetores de strings. Devemos
sempre lembrar que um vetor deve ter capacidade de armazenar os caracteres desejados mais o caractere \0.
A biblioteca do MPLAB C18, possui diversas funções de manipulação de strings. Portanto nada de realizar operações relacionais entre strings, utilize as funções do MPLAB C18 para isso.
Estrutura de dados
As estruturas de dados são formadas por tipos de variáveis diferentes, podemos utilizar as variáveis de uma estrutura em conjunto ou individualmente.
Para criar uma estrutura usamos o comando struct. Podemos ter dois tipos de estrutura de dados, uma de variáveis e outra de bit. A estrutura de variáveis nos permitirá criar uma variável do tipo “struct”, formada por variáveis de
diversos tipos. O segundo tipo de struct, nos permitirá criar estruturas que serão formadas por variáveis com quantidade de bits diferentes, chamados de campo de bits.
Criando uma struct de variáveis Uma estrutura pode ser formada por vários tipos de variáveis, estas variáveis chamaremos de
“elementos de estrutura”.
Curso Módulo 4 –Família 18F e MpLab C18 137
Uma das aplicações de uma estrutura de dados, é utiliza-la como um banco de dados. Imagine uma agenda eletrônica. Para podermos inserir, telefone, endereço, e-mail e etc, são necessários
campos de tamanhos diferentes, para a entrada destas informações. Vejamos então como criar uma struct: • Criando uma struct: struct nome_da_estrutura
declaração da variável; : : ;
• Criando uma struct e definindo uma variável deste novo tipo: struct nome_da_estrutura
declaração da variável; : : nome_da_variável;
A identificação nome_da_estrutura, pode ser utilizada para criar outras estruturadas com o mesmo
formato. Exemplo:
a) Vamos criar uma estrutura de lista de contatos: struct lista_de_contato // tipo de variável char nome[50]; char DDD [2]; char telefone [8]; ;
Para criar uma outra variável do tipo lista_de_contato, basta utilizar esta sintaxe:
struct lista_de_contato lista_de_amigos;
b) Outra maneira de criar uma estrutura é apresentada a seguir:
struct lista_de_contato // tipo de variável char nome[50]; char DDD [2]; char telefone [8]; lista_de_amigos; // variável estruturada
A estrutura lista_de_amigos terá o mesmo formato da struct lista_de_contato. O que não quer dizer que
ocuparão as mesmas posições de RAM, cada estrutura ocupara a quantidade de memória necessária. Podemos dizer que lista_de_contato é um novo tipo de variável.
Acessando elementos de uma estrutura Como nós já sabemos uma estrutura é formada por variáveis de diversos tipos, agora como faremos
para ler ou escrever em um elemento desta estrutura? A resposta para está pergunta é simples utilizaremos o operador ‘ponto’, “.”, com o auxilio deste
operador poderemos acessar qualquer elemento de uma estrutura.
138
Escrevendo nos elementos de uma estrutura Para escrever em um elemento de estrutura devemos utilizar o operador “.”, será usado como separador
de elementos. Com o uso deste operador poderemos escrever em qualquer elemento, basta para isso indicar a
estrutura e o elemento da estrutura e igualar ao valor desejado. Sintaxe: nome_da_estrutura.elemento = valor desejado. Veja que na estrutura lista_de_amigos temos o campo DDD, para inserirmos um dado neste elemento
faremos assim:
struct lista_de_contato char nome[50]; char DDD [2]; char telefone [8]; lista_de_amigos; lista_de_amigos.DDD[0] = '2'; lista_de_amigos.DDD[1] = '3';
O elemento DDD é um vetor de duas posições, na posição zero do vetor foi carregado o caractere 2.
Leitura de um elemento de estrutura Com a mesma simplicidade que realizamos uma escrita em um elemento de estrutura, podemos ler os
elementos. Para está tarefa também utilizaremos o operador “.”, a leitura pode ser feita em qualquer elemento da estrutura a qualquer instante, veja:
unsigned char reg; struct lista_de_contato char nome[50]; char DDD [2]; char telefone [8]; lista_de_amigos; reg = lista_de_amigos.DDD[0];
Operação entre estruturas
Quando trabalhamos com estruturas ficamos tentados a realizar operações lógicas ou aritméticas entre estruturas, veja isto não é possível o que podemos fazer é igualar duas estruturas.
Qualquer operação que você deseje realizar deverá ser feita entre os elementos da estrutura. Exemplo: static struct relogio unsigned char horas; unsigned char minutos; unsigned char segundos; horario; struct relogio alarme; reg = (horario.horas == alarme.horas);
No exemplo realizamos uma operação de comparação entre o elemento horas da estrutura horário e o
elemento horas da estrutura alarme.
Curso Módulo 4 –Família 18F e MpLab C18 139
Inicialização de uma estrutura Além de criar uma estrutura também é possível fazer a inicialização da mesma, veja como: #pragma idata MY_DATA2=0x400 struct lista_de_contato
char nome[50]; char DDD [2]; char telefone [8];
; struct lista_de_contato lista_de_amigos = "Rinaldo",'1','1','4','9','9','2','8','7','7','5'; struct lista_de_contato lista_de_empresas = "Grupo Mosaico",'1','1','4','9','9','2','8','7','7','5';
Matrizes de estruturas Uma matriz é a associação de variáveis do mesmo tipo, como uma estrutura é um tipo de variável
podemos então criar matrizes de estruturas. Sintaxe: struct nome_da_estrutura tipo variavel; ; struct nome_da_estrutura matriz[tamanho];
Vejamos um exemplo que irá criar na memória RAM um banco de dados para armazenar 12 nomes de
contato. A matriz agenda será formada por 12 variáveis do tipo lista_de_contato. Vamos utilizar neste exemplo 240 bytes de RAM. Exemplo: #pragma idata MY_DATA2=0x400// define banco de RAM struct lista_de_contato char nome[10]; char DDD [2]; char telefone [8]; ; struct lista_de_contato agenda[12]; // Matriz para armazenar 12 contatos.
Estrutura de Bits O MPLAB C18 não possui um tipo dado para criar variáveis de um bit. Sendo assim não é possível criar
flags. O que é um flag? Um flag nada mais é do que um sinalizador, uma bandeira, utilizaremos um bit para indicar 0 ou 1, assim
economizaremos memória RAM, como já é sabido cada posição de RAM possui 8 bits, logo com uma variável poderemos criar 8 flags. Podemos criar variáveis que com mais de um bit.
Pelo padrão ANSI uma estrutura de bit não pode ser maior do que o tamanho de um int. No entanto no MPLAB C18 podemos utilizar uma variável do tipo char e inserir quantos bits precisarmos,
o que ultrapassar a capacidade de 8 bits será alocado na posição de RAM seguinte. Se não existe este tipo de dado como faremos então? Nossa resposta está na estrutura de bits ou como
também é chamado, campo de bits. Com uma struct de bits poderemos utilizar os registradores da memória RAM e criamos os flags
necessários para o nosso programa. Com uma estrutura de bits podemos criar variáveis e definir a quantidade de bits utilizada por esta
variável, os bits são ordenados da direita para a esquerda. Observe a sintaxe do campo de bits.
140
Sintaxe: struct nome_da_estrutura
variável: quantidade de bits da variável; bit 7 variável: quantidade de bits da variável; bit 6 : : : : : : variável: quantidade de bits da variável; bit 0
; struct nome_da_estrutura nome_da_varialvel struct nome_da_estrutura
tipo da variável variável: quantidade de bits da variável; tipo da variável variável: quantidade de bits da variável;
nome_da_variável;
Acessando uma estrutura de bits Da mesma maneira que lemos ou escrevemos em uma variável de uma estrutura, faremos com um bit
de um campo de bits. Veja no exemplo: #pragma idata MY_DATA2=0x300 struct campo_de_bits char flag15 :1; //bit 15 char flag14 :1; //bit 14 char flag13 :1; //bit 13 char flag12 :1; //bit 12 char flag11 :1; //bit 11 char flag10 :1; //bit 10 char flag9 :1; //bit 09 char flag8 :1; //bit 08 char flag7 :1; //bit 07 char flag6 :1; //bit 06 char flag5 :1; //bit 05 char flag4 :1; //bit 04 char flag3 :1; //bit 03 char flag2 :1; //bit 02 char flag1 :1; //bit 01 char flag0 :1; //bit 00 ; struct campo_de_bits reg_flags = 0x11111111; // só é possível inicializar os 8 primeiros bits do campo de bits. #define emergencia reg_flags.flag15 //O bit flags15 recebe o nome emergência. void main (void) while(1) // rotina principal reg_flags.flag0 = 0; // escrita de um bit if (reg_flags.flag1 == 1)// O bit é igual a um? reg_flags.flag2 = 0; // sim, reg_flags.flag3 = 0; reg_flags.flag4 = 0; emergencia = 1; PORTBbits.RB0 = reg_flags.flag5; // leitura do flag5 PORTC = reg_flags;
Curso Módulo 4 –Família 18F e MpLab C18 141
Uniões
Uma union permite compartilhar a mesma posição de memória RAM, com outras variáveis. E uma forma de economizar memória RAM.
Já que podemos economizar espaço na memória, como definimos a quantidade de RAM usada por uma union? Já que estamos compartilhando as mesmas posições de memória entre as variáveis da union, basta saber qual é a maior variável desta union, este é define a quantidade de memória necessária para a union.
Como criamos uma union? Uma union pode ter variáveis de mesmo tipo ou tipos diferentes. Para criarmos uma union basta utilizar o
comando union, escolher um nome e definir as suas variáveis, observe sua sintaxe. Sintaxe: union nome_da_union tipo variável; tipo variável; ; ou union nome_da_union tipo variável; tipo variável; variável;
Exemplo de union.
union teste unsigned char reg1; // variável de 8 bits
unsigned int reg2; // variável de 16 bits unsigned short reg3; // variável de 24 bits
unsigned long reg4; // variável de 32 bits unsigned char reg5[4]; // matriz de 4 bytes, 32 bits
variável;
Área da memória RAM utilizada pela Union
Registradores 1o byte 2o byte 3o byte 4o byte reg1 reg2 reg3 reg4 reg5
Neste nosso exemplo utilizamos 4 bytes de RAM para 5 variáveis, obviamente uma union compartilha os
4 bytes para as 5 variáveis. Qualquer variável que tiver o seu conteúdo modificado, modificará o conteúdo do byte correspondente da
union. Veja, através da matriz reg5, teremos acesso a qualquer posição de memória da matriz.
Acessando os elementos de uma union Da mesma maneira que lemos ou escrevemos em uma variável de uma estrutura, faremos com uma
union. Veja no exemplo:
union teste
unsigned char reg1; // variável de 8 bits unsigned int reg2; // variável de 16 bits unsigned short reg3; // variável de 24 bits unsigned long reg4; // variável de 32 bits unsigned char reg5[4]; // matriz de 4 bytes, 32 bits
registrador;
142
Escrevendo em um elemento de uma union Para realizar a escrita você deve escrever o nome da variável da union “ponto” o nome do elemento da
union e igualar ao valor desejado, pode ser uma constante ou variável. Veja: registrador.reg1 = 0x55; Podemos simplificar o trabalho, utilizando a diretiva define (substituição de texto). #define registrador_1 registrador.reg1 Agora podemos realizar a escrita em reg1 desta forma: Registrador_1 = 0x55;
Leitura de um elemento de uma union Para realizar a leitura você deve escrever o nome da variável que receberá o dado da variável da union,
igual a nome da variável da union “ponto” o nome do elemento da union. Veja: PORTB = registrador.reg1; Podemos simplificar o trabalho, utilizando a diretiva define (substituição de texto). #define registrador_1 registrador.reg1 Agora podemos realizar a escrita em reg1 desta forma: PORTB = Registrador_1;
Variáveis Enumeradas
Uma enumeração é um conjunto de constantes inteiras que utilizaremos para carregar a variável enumerada.
Pense na seguinte situação: Você está desenvolvendo um projeto que utiliza um display de LCD ,16X2 por exemplo, e este sistema terá pelo menos 20 telas de ajuste.
Para a seleção das telas você está utilizando uma variável, e de acordo com o valor da variável você sabe qual é a tela que deve ser carregada. Onde entra as enumerações neste assunto?
Ao invés de utilizar números você utilizará nomes para determinar qual é a tela que devemos carregar. Vejamos como fica a sintaxe deste tipo de variável.
enum tipo_de_enumeração
constante_1, constante_2, constante_3, constante_n variável _enumerada;
O valor de cada constante pode ser definido, podemos utilizar o valor padrão. Utilizando o valor padrão a primeira constante equivale a zero, a segunda equivale a um e assim por
diante. Veja no exemplo:
Exemplo: enum controle_de_tela
tela_configuracao; tela_setup, tela_alarme, tela_supervisorio selecao_de_tela;
Veja na tabela abaixo o valor de cada constante enemerada.
Constante enumerada Valor da constante tela_configuracao 0
tela_setup 1 tela_alarme 2
tela_supervisorio 3
Curso Módulo 4 –Família 18F e MpLab C18 143
Agora em se querendo definir o valor da constante, devemos tomar o seguinte procedimento: Sintaxe: enum tipo_de_enumeração
constante_1 = valor da constante, constante_2, constante_3, constante_n variável _enumerada; Exemplo: enum controle_de_tela
tela_configuracao = 2; tela_setup, tela_alarme, tela_supervisorio selecao_de_tela; Agora veja na tabela o valor de cada constante.
Constante enumerada Valor da constante tela_configuracao 2
tela_setup 3 tela_alarme 4
tela_supervisorio 5
Operações com variáveis enumeradas Como estamos falando em variável, com uma variável enumerada podemos realizar qualquer operação
lógica ou aritmética. Desta forma podemos utilizar estas variáveis em testes condicionais, para tomada de decisão.
Ponteiros
Os ponteiros são utilizados para acesso indireto a memória RAM, são muito utilizados para acessar variáveis dinâmicas, para leitura de matrizes mais rápido e para que funções possam modificar o valor de argumento de rotinas que fizeram a chamada da função.
Definindo um ponteiro Os ponteiros são criados para apontar para um tipo especifico de variável. Para variáveis do tipo char,
criaremos um ponteiro do tipo char, para uma variável do tipo short long criaremos um ponteiro de mesmo tipo, o mesmo é valido para os outros tipos de variáveis.
Um ponteiro é definido pelo caractere “*”, assim quando estivermos criando um ponteiro o nome da variável deve vir precedido do asterisco, “*”.
A sintaxe ficará assim: tipo *nome_do_ponteiro; Exemplo: char *ponteiro_char; int * ponteiro_int; Todo ponteiro deve ser iniciado com um endereço de um local conhecido, desta forma evitamos
problemas com acesso a posições de memória indesejada.
Carregando um endereço no ponteiro Com um ponteiro já definido, devemos carregar o endereço que desejamos acessar. Esta operação será realizada através do operador “&”, aplicando este operador a uma variável teremos
como retorno o endereço da variável. Sintaxe: ponteiro = &nome_da_variável;
144
Como podemos observar o ponteiro foi iniciado com o endereço da variável. Agora podemos acessar de forma indireta o conteúdo de uma determinado posição de memória. No exemplo que veremos, vamos carregar na variável ponteiro o endereço da variável auxiliar e através
do ponteiro, copiar o conteúdo da variável auxiliar para a variável contador. Exemplo: #pragma idata MY_DATA2=0x200 char *ponteiro; char auxiliar = 0x10; char contador = 0x00; ponteiro = &auxiliar; contador = *ponteiro;
0x000x100x300x40
0x2000x2010x2020x2030x204
auxiliarcontador
Antes ponteiro = &auxiliar;ponteiro
contador = *ponteiro;
0x40
0x2010x100x300x40
0x2000x2010x2020x2030x204
auxiliarcontador
ponteiro
0x40
0x2100x100x100x40
0x2000x2010x2020x2030x204
auxiliarcontador
ponteiro
0x40
Operações com ponteiros Aos realizar operações com ponteiros, devemos ter cuidado, pois carregando um endereço errado no
ponteiro, estaremos apontando para uma posição de memória indesejada, este erro de programação pode nos custar horas de analise para encontrar o problema.
As operações de incremento e decremento são muito utilizadas em ponteiros. Temos de tomar cuidado com quem está sendo realizada a operação, com o ponteiro ou com a posição de memória apontada por ele.
Veja o exemplo a seguir: #pragma idata MY_DATA2=0x200 char *ponteiro; char auxiliar = 0x10; char contador = 0x00; char tempo = 0x40; ponteiro = &auxiliar; *ponteiro++; contador = *ponteiro; ponteiro++; tempo = *ponteiro;
Acompanhe pelo desenho o que acontece com a memória RAM.
Curso Módulo 4 –Família 18F e MpLab C18 145
0x000x100x300x40
0x2000x2010x2020x2030x204
auxiliarcontador
Antes ponteiro = &auxiliar;ponteiro
*ponteiro++;
0x40
0x2010x100x300x40
0x2000x2010x2020x2030x204
auxiliarcontador
ponteiro
0x40
0x2100x110x300x40
0x2000x2010x2020x2030x204
auxiliarcontador
ponteiro
0x40
tempo
tempo
tempo
contador = *ponteiro;0x2100x110x110x40
0x2000x2010x2020x2030x204
auxiliarcontador
ponteiro
0x40tempo
ponteiro++;0x2020x110x110x40
0x2000x2010x2020x2030x204
auxiliarcontador
ponteiro
0x40tempo
tempo = *ponteiro;0x2100x110x110x11
0x2000x2010x2020x2030x204
auxiliarcontador
ponteiro
0x40tempo
Veja na operação *ponteiro++, foi incrementado o conteúdo da posição de memória apontado, resultou em auxiliar = 11.
Agora na linha de comando ponteiro++, tivemos o conteúdo da posição de memória ponteiro incrementado, o que fez o ponteiro apontar para o endereço 0x202.
146
Capítulo 16 - McMaster – Desenvolvimento de Sistemas com Microcontroladores PIC
Introdução
O McMaster é um equipamento para desenvolvimento de sistemas completo para o estudo da tecnologia de microcontroladores Microchip e em particular ao estudo dos microcontroladores PIC16F877A, PIC18F452, PIC18F4520 e compativeis. Na verdade, este sistema serve para muito mais que simplesmente o aperfeiçoamento dos conhecimentos da família PIC. Com ele o usuário é capaz de criar projetos completos, colocando em teste também a eficiência de seus conceitos e algoritmos.
Tudo isso é possível porque este sistema foi criado e desenvolvido pensando na didática de ensino e nos problemas mais comuns do mercado em relação ao uso de microcontroladores.
Visão Macro do Sistema
Nesta seção será abordado através de uma visão macro o conceito do sistema utilizado no McMaster. Ele é composto de um gravador para o microcontrolador, o microcontrolador PIC central, os periféricos ligados
ao microcontrolador, aos quais daremos o nome de periféricos padrão e um conector de expansão para experiências onde novos periféricos, aos quais daremos o nome de periféricos adicionais, poderão ser ligados.
Um dos periféricos padrão do MCMASTER é o módulo de comunicação serial RS232. Como o gravador também utiliza comunicação serial RS232 para se comunicar com o MPLAB e no MCMASTER existe apenas uma saída serial, este recurso deve ser compartilhado para que tanto o gravador como o módulo RS232 do sistema possam utilizar a mesma saída. Desta forma, o usuário deverá escolher, através do botão localizado acima do microcontrolador, onde a serial deverá ser aplicada, no gravador ou no microcontrolador.
Todos os I/Os do microcontrolador estão disponíveis no conector de expansão para experiências. Com exceção dos pinos RB6 e RB7 que são utilizados pela gravação in-circuit, todos os outros I/Os estão ligados diretamente ao conector, ou seja, sem nenhum tipo de proteção. Apenas os pinos RB6 e RB7 foram isolados. Por este motivo, é muito importante que o usuário configure corretamente os I/Os do microcontrolador quando for utilizar o conector de expansão, pois neste caso, uma ligação errada pode danificar o microcontrolador. Se o usuário utilizar o conector de expansão apenas com placas oficiais de experiências a preocupação com a direção dos I/Os do microcontrolador não precisa ser tomada, uma vez que as placas de experiências e todo o MCMASTER foram projetados a fim de evitar que uma configuração errada do microcontrolador coloque o sistema em risco. Portanto, mesmo que um pino do microcontrolador seja configurado como saída quando o correto seria entrada a integridade do sistema está garantida. É claro que este erro pode acarretar num mau funcionamento do sistema projetado, porém nunca existirá risco ao MCMASTER e às placas de experiências, desde que as mesmas sejam oficiais e/ou homologadas pelo fabricante.
Para evitar que módulos padrão do MCMASTER venham a atrapalhar o correto funcionamento de uma eventual placa de experiências optou-se pela utilização de jumpers de configuração para que pontos importantes do circuito possam ser desabilitados e as vias do microcontrolador possam ser utilizadas apenas pelas placas de experiências e não mais nos módulos padrão. Desta forma, foram criados 7 jumpers. Começando de cima para baixo os jumpers são:
• Comunicação TX (RC6) – Este jumper desliga o pino RC6 (TX da USART do PIC – utilizado para
comunicação padrão RS232) do microcontrolador deixando-o disponível apenas no conector de expansão. • Comunicação RX (RC7) – Da mesma forma que o jumper anterior, este jumper desliga o pino RC7 (RX da
USART do PIC – utilizado para comunicação padrão RS232) do microcontrolador deixando-o disponível apenas no conector de expansão.
• Data I2C (RC4) – Este jumper desliga o pino RC4 (via de dados para comunicação I2C) do microcontrolador deixando-o disponível apenas no conector de expansão.
• Clock I2C (RC3) – Este jumper desliga o pino RC3 (via de clock para comunicação I2C) do microcontrolador deixando-o disponível apenas no conector de expansão.
• Coluna 1 (RB0) – Este jumper desliga o pino RB0 utilizado para ler os botões da coluna 1 do teclado matricial deixando-o disponível apenas no conector de expansão.
• Linha 1 / Display Milhar (RB4) – Este jumper desliga o pino RB4 utilizado para ativar a linha 1 do teclado matricial e o display do milhar deixando-o disponível apenas no conector de expansão.
Curso Módulo 4 –Família 18F e MpLab C18 147
• Leds Especiais (RC0, RC1 e RC2) – Este jumper desabilita os leds ligados aos pinos RC0, RC1 e RC2 utilizados pelos módulos CPP e TIMER1 do microcontrolador.
Módulos Padrão
Nesta seção serão abordados cada um dos módulos padrão do MCMASTER.
Microcontrolador O sistema pode utilizar os microcontroladores, PIC16F877A, ou PIC 18F452, ou ainda o PIC 18F4520, como
centro de todo o hardware. Este microcontrolador está ligado a todos os periféricos disponíveis, possibilitando o estudo de práticamente todas as suas funções. Devido também ao grande poder de recursos deste modelo de PIC, é possível, junto aos demais recursos da placa, o desenvolvimento de projetos simples e/ou complexos, como por exemplo um controlador de temperatura com algoritmo de controle PID.
LCD alfanumérico Nos dias de hoje, qualquer programador sabe da importância da interface com o usuário dentro de um sistema
qualquer. Por isso, é muito importante o aprendizado de operação de um display do tipo LCD. No caso do MCMASTER, este display possui 2 linhas de 16 caracteres cada, sendo um padrão de mercado atual. Possui um chip de controle próprio, com o qual é realizada a interface com o microcontrolador. Com este periférico os sistemas desenvolvidos no MCMASTER poderão possuir telas explicativas, informações claras e menus de navegação.
A comunicação com o LCD é paralela com 8 vias de dados. Além destas, mais duas vias são utilizadas para controlar o LCD, uma denominada de ENABLE e a outra de RS.
A comunicação com o LCD é somente de escrita, desta forma, o pino de R/W do LCD está diretamente ligado ao terra (GND), não permitindo a leitura do mesmo.
As 8 vias de dados do LCD estão ligadas ao PORTD do microcontrolador, de RD0 (LSB) até RD7 (MSB). O pino de ENABLE está conectado ao pino RE1 do PIC e o pino RS do LCD ao pino RE0 do microcontrolador.
Assim, o esquema de ligação segue a tabela abaixo:
PIC LCD RD0...RD7 D0...D7
RE0 RS RE1 ENABLE
Terra (GND) R/W Para maiores informações a respeito do LCD pode-se consultar o data sheet contido no CD que acompanha o
MCMASTER.
Displays de leds com 7 segmentos Como já visto, o LCD é uma ótima ferramenta de informação ao usuário, porém, muitas vezes ele ainda é
inviável. Pode-se comentar alguns motivos desta inviabilidade: custos, capacidade de visualização, iluminação, etc. Por isso, em muitos projetos, os velhos e práticos displays de leds ainda são a melhor alternativa. No MCMASTER optou-se pela utilização de displays de 7 segmentos, que são numéricos, mas que permitem a visualização de diversas letras através da combinação específica destes segmentos. Optou-se também por 4 dígitos, todos com os segmentos interligados e os controles (comum) independentes, possibilitando a operação por varredura.
Atualmente, é muito comum encontrar em produtos do mercado, a combinação de ambos os tipos de display, para uma visualização mais completa e eficiente. Com o MCMASTER esta combinação também é possível.
A conexão dos displays com o microcontrolador segue a tabela abaixo: PIC Segmento RD0 A RD1 B RD2 C RD3 D RD4 E RD5 F RD6 G RD7 DP
148
E as vias de seleção de cada um dos displays, a tabela seguir:
PIC Display RB4 Milhar RB5 Centena RB6 Dezena RB7 Unidade
Para a ativação dos displays deve-se selecionar nível lógico 1 nos pinos de seleção. Os segmentos também
são ativados com nível lógico 1.
Leds O MCMASTER possui um grupo de 8 leds que compartilha o mesmo barramento que os displays de 7
segmentos e o display LCD. Desta forma, o seu acionamento deve ser feito via varredura sendo que os leds estão conectados ao PORTD e
a seleção é feita pelo pino RA4. Da mesma forma que os displays, os leds são ativados com nível lógico 1, tanto na via de seleção (RA4) como individualmente (PORTD).
Teclado matricial A maioria dos sistemas desenvolvidos atualmente utilizam algum tipo de teclado para a entrada de dados pelo
usuário. O MCMASTER está provido de um teclado matricial de 4 linhas e 4 colunas, totalizando 16 teclas. O acionamento das linhas do teclado é feito simultaneamente com os comuns dos displays de 7 segmentos. Desta forma, ao acionar o display da unidade aciona-se também a linha 4 do teclado matricial. Junto com o display da dezena a linha 3 e assim por diante.
A tabela abaixo mostra esta relação:
Pino PIC Estado Teclado Matricial Display de 7 segmentos 1 linha 4 ativada unidade ativada
RB7 0 linha 4 desativada unidade desativada
1 linha 3 ativada dezena ativada RB6
0 linha 3 desativada dezena desativada
1 linha 2 ativada centena ativada RB5
0 linha 2 desativada centena desativada
1 linha 1 ativada milhar ativada RB4
0 linha 1 desativada milhar desativada
As colunas podem ser lidas através dos pinos RB0, RB1, RB2 e RB3, conforme a tabela a seguir:
Pino PIC Estado Teclado Matricial 1 Alguma tecla pressionada na coluna 1
RB0 0 Nenhuma tecla pressionada na coluna 1
1 Alguma tecla pressionada na coluna 2 RB1
0 Nenhuma tecla pressionada na coluna 2
1 Alguma tecla pressionada na coluna 3 RB2
0 Nenhuma tecla pressionada na coluna 3
1 Alguma tecla pressionada na coluna 4 RB3
0 Nenhuma tecla pressionada na coluna 4
Vale observar que para o correto funcionamento do teclado os jumpers relacionados com os pinos RB0 e RB4
devem estar configurados na posição ON.
Curso Módulo 4 –Família 18F e MpLab C18 149
Buzzer Para chamar a atenção do usuário e confirmar certas ações, cada vez mais os sistemas estão fazendo uso de
técnicas sonoras, seja através de simples bips ou por complexas melodias. Para que os usuários não fiquem sem o uso deste recurso, disponibilizou-se também um buzzer piezoelétrico com oscilação comandada diretamente pelo PIC, tornando possível a criação de sons diversos.
O microcontrolador deve então gerar (através do software) uma onda quadrada capaz de excitar o buzzer. Para gerar um simples beep pode-se utilizar uma onda quadrada com freqüência em torno de 650Hz e duração aproximada de 100ms.
O buzzer está conectado ao pino RE2 do microcontrolador.
Memória E2PROM externa Além da memória E2PROM interna do PIC, o MCMASTER possui ainda uma memória externa do tipo serial,
modelo 24LC256 com 32Kbytes disponíveis para uso. Esta memória está soquetada na placa, possibilitando a sua substituição por outros modelos compatíveis, com maior ou menor capacidade.
A comunicação com esta memória é do tipo I²C, estando diretamente ligada os pinos do PIC responsáveis por este padrão de comunicação.
Assim: PIC Memória
RC3 Clock (SCL) – pino 6
RC4 Data (SDA) – pino 5
Novamente os jumpers de configurações relacionados devem estar habilitados para a utilização da memória serial externa.
Como a memória serial compartilha o mesmo barramento I2C do relógio de tempo real (PCF8583P) se faz necessária a utilização de endereços diferentes para que o barramento seja compatível com os dois periféricos. Sendo assim, escolheu-se o endereço 7h (111b) para a memória serial. Para maiores informações sobre o protocolo de comunicação da memória serial 24LC256 pode-se consultar o data sheet disponível no CD.
Relógio de tempo real (RTC) Utilizando o mesmo barramento I2C da memória serial, o MCMASTER possui um relógio de tempo real,
modelo PCF8583P. Com este componente o usuário poderá criar sistemas que contenham informações como a hora e a data atual. O relógio utilizado é completo, ou seja, é capaz de contar dia, mês, ano (inclusive bissexto), semana, hora, minuto, segundo e milésimo de segundo. Além de poder ser configurado de formas diferentes. O data sheet deste componente está disponível no CD que acompanha o MCMASTER.
Pelo mesmo motivo já comentado na memória serial, o relógio foi configurado para utilizar o endereço 0h (000b) a fim de poder compartilhar o mesmo barramento I2C.
Como no caso da memória, os pinos responsáveis pela comunicação são:
PIC Relógio RTC RC3 Clock (SCL) – pino 6
RC4 Data (SDA) – pino 5
Comunicação serial RS-232 Quem não deseja que seu projeto se comunique com um computador atualmente? Esta é outra tendência de
mercado que os profissionais não podem deixar de lado. Seja para a configuração de parâmetros, para a coleta de dados ou a visualização gráfica de informações, a interligação entre o kit e o computador é um recurso que não poderia ser deixado de lado.
Optou-se pela comunicação serial, padrão RS-232 através de um conector DB-9. A interface e ajuste de tensões necessárias a este padrão, em relação à operação do PIC (TTL) é feita por um CI dedicado. Internamente, as vias de TX e RX podem ser ligadas aos pinos da USART do PIC, possibilitando o uso deste recurso.
Para habilitar este recurso é necessário que os jumpers apropriados estejam na posição ON. A ligação ao microcontrolador segue a tabela abaixo.
150
PIC COM.
RC6 TX (saída)
RC7 RX (entrada)
Como a porta de comunicação é compartilhada com o gravador é necessário também selecionar a serial para
o PIC através do botão de modo de utilização. Faz parte também do módulo de comunicação serial, o conector DB9 fêmea que segue a pinagem abaixo:
Pino Função
1 -
2 TX (saída)
3 RX (entrada)
4 -
5 Terra (GND)
6 -
7 CTS (utilizado apenas pelo gravador)
8 RTS (utilizado apenas pelo gravador)
9 -
Conversão analógica / digital (A/D) É verdade que estamos considerando o mundo cada vez mais digital, principalmente nos dias de hoje, onde
vemos bilhões de informações trafegando por fibras ópticas e imagens de computador recriando o mundo real. Mas não podemos esquecer que a natureza é completamente analógica, e qualquer sistema que se baseie ou utilize informações deste meio externo precisará de um sistema de conversão para poder se comunicar. É por isso que, hoje e sempre, a conversão A/D é tão necessária.
Com o MCMASTER poderemos realizar estas conversões de duas maneiras. A primeira é através do conversor interno do PIC e a segunda é através de um pseudoconversor fundamentado no tempo de carga de um circuito RC.
Dentre os módulos padrão existem dois sistemas para trabalhar com o conversor A/D e para qualquer um deles, as duas formas de aquisição podem ser aplicadas, ou seja, tanto via A/D convencional como via RC. O primeiro sistema consiste num potenciômetro e o segundo num conjunto de jumpers que podem ser configurados como divisor resistivo ou circuito RC.
Potenciômetro O sistema com o potenciômetro segue o esquema elétrico representado a seguir.
+-
+5V
RA0
LM358
10K
4,7K 330R
1uF
A tensão de entrada presente no pino RA0 do microcontrolador pode ser ajustada entre 0 e 5V. Caso se utilize
o conversor A/D interno do PIC, o capacitor de 1uF e o resistor de 4K7 formam um filtro passa baixa útil para filtrar ruídos e deixar o sinal de entrada mais estável.
Caso se utilize o sistema de leitura via RC o conjunto de resistores e capacitores assume outra função. Neste sistema, para realizar a conversão deve-se executar as etapas a seguir:
• Inicialmente, através do software, deve-se descarregar o capacitor, colocando o pino do microcontrolador como saída em nível lógico 0. O capacitor se descarregará pelo resistor de 330R;
Curso Módulo 4 –Família 18F e MpLab C18 151
• Após o capacitor estar descarregado, coloca-se o pino do microcontrolador em entrada e começa-se a contar o tempo que o capacitor leva para se carregar (através do resistor de 4K7), ou seja, quanto tempo o capacitor leva para atingir nível lógico 1;
• Como tempo de carga é inversamente proporcional à tensão aplicada pelo potenciômetro, sabendo-se o tempo de carga pode-se estimar a tensão aplicada.
Jumpers O sistema de jumpers está ligado ao pino RA5 do microcontrolador e segue o esquema elétrico representado a
seguir.
+5V
3 2 1JP4
Divisor R
esistivo
Divisor C
apacitivo
1 2 3
JP3
1 2 3
JP2
1 2 3
JP1
22K
33K
47K330R
10K100nF
Jumpers RA5
Posição 1
Posição 2
Posição 3
ON OFF
RA5
Se configurarmos o sistema para divisor resistivo, basta ler com o conversor A/D do PIC a tensão presente no
pino RA5 para estimar a posição do jumper. Se configurarmos o sistema para resistor/capacitor, devemos seguir a mesma metodologia explicada no caso
do potenciômetro, ou seja: • Inicialmente descarregar o capacitor através do resistor de 330R colocando o pino do microcontrolador
como saída em nível lógico 0. • Após o capacitor estar descarregado, colocar o pino do microcontrolador em entrada e começar a contar o
tempo que o capacitor leva para se carregar, ou seja, quanto tempo o capacitor leva para atingir nível lógico 1. • Este tempo de carga é proporcional ao valor do circuito RC e portanto, pode ser utilizado para determinar a
posição do jumper.
152
Periféricos Adicionais
A seguir serão explanados os periféricos adicionais contidos na placa de experiências EXP01 que acompanha o kit MCMASTER.
Placa de experiências EXP01 Entre outras funções a placa de experiências EXP01 possui um sistema completo para monitoramento e
controle de temperatura, com um sensor e dois atuadores. Desta forma, tem-se um sensor de temperatura, um atuador de aquecimento (resistência controlada por PWM) e um atuador de resfriamento (ventilador controlado por PWM). Além disso, um sistema óptico ligado às hélices do ventilador é capaz de criar um tacógrafo, para monitoramento e controle de rotação.
Possui também uma lâmpada incandescente além de gerar uma tensão de referência estável em 2,5V que pode ser utilizada como referência para o conversor A/D.
Com tudo isso pode-se criar experimentos e projetos complexos de controle, começando em um simples controle ON/OFF até um avançado controlador PID.
Sensor de temperatura A placa possui um circuito que utiliza um diodo de sinal como elemento sensor do medidor de temperatura
ambiente. O sinal analógico proporcional à temperatura ambiente está presente no pino RA1 do microcontrolador e varia entre 0 e 5V.
Deve-se evitar que a temperatura ultrapasse 90ºC a fim de evitar que o sensor seja danificado.
Aquecedor O aquecedor consiste numa resistência de 68Ω com 5W de dissipação. Pode ser acionada através do pino
RC2 do microcontrolador. Veja que este pino pode ser configurado como PWM, e portanto, a potência de aquecimento pode ser regulada através deste recurso.
Ventilador O sistema de ventilação consiste num cooler de PC que pode ser ativado através do pino RC1 do
microcontrolador. Assim como no caso do aquecedor, este pino pode ser configurado como PWM, desta forma, pode-se modular a velocidade do ventilador utilizando este recurso do microcontrolador.
Tacômetro Junto ao ventilador existe um sistema formado por um transmissor e um receptor de infravermelho. Este
sistema é utilizado para medir a velocidade de rotação do ventilador. Quando não temos a passagem de luz, ou seja, quando a luz está interrompida por uma das palhetas do ventilador, o sistema de tacômetro apresentará na saída nível lógico 1. Quando se tem a passagem de luz, a saída do sistema de tacômetro será 0. O tacômetro está conectado ao pino RC0 (entrada de contador do TMR1) do microcontrolador.
Lâmpada incandescente Consiste numa lâmpada incandescente de 12V que pode ser acionada através do pino RC5 do
microcontrolador. Com nível lógico 1 a lâmpada acende e com nível lógico 0 a lâmpada apaga.
Tensão de referência O circuito medidor de temperatura ambiente utiliza uma tensão de referência fixa e estável em 2,5V e como
este recurso já estava presente na placa de experiências EXP01 resolveu-se também disponibilizar este recurso ao usuário. Assim, a tensão de referência de 2,5V foi conectada ao pino RA3 do PIC que pode ser configurado para utilizar este pino como entrada de referência externa do conversor A/D. Isto permite que o conversor A/D possa trabalhar em outra faixa de conversão e conseqüentemente com outra resolução.
Gravador
Para utilizar o gravador presente no MCMASTER basta selecionar corretamente a saída serial e utilizar o software de desenvolvimento MPLAB da Microchip. Por se tratar de um gravador in-circuit o microcontrolador não precisa ser retirado da placa.
Ao habilitar o gravador no MPLAB o software atual do PIC16F877A ou do PIC18F452 será paralisado e instantes após o final da gravação do novo software, o microcontrolador será automaticamente inicializado.
Curso Módulo 4 –Família 18F e MpLab C18 153
Capítulo 17 - Experiências
Experiência 1 - Leitura de uma tecla e acionamento de um led
Objetivo
O objetivo desta experiência é ensinar ao aluno os primeiros passos sobre o microcontrolador. É apresentado o modo de configuração dos pinos de I/Os e os primeiros comandos utilizadas para testar condições nos pinos de entrada e alterações de estado nos pinos de saída, além de comandos para controle do fluxo do programa.
Descrição
Sistema muito simples para representar o estado de um botão através de um led. Com o botão pressionado o led é ligado e com o botão solto o led é apagado.
O software inicia configurando os pinos de I/Os através dos registradores TRIS e dos registradores de periféricos pertinentes. Em seguida, o software habilita a linha 4 do teclado matricial e o grupo de leds ligados ao PORTD. A partir daí, o software entra num loop infinito onde o botão da linha 1 coluna 4 é testado e seu estado reproduzido no led 0 ligado ao pino RD0.
154
Esquema Elétrico
Curso Módulo 4 –Família 18F e MpLab C18 155
Fluxograma
HABILITA LEDS
ACENDE LED
Não
Sim
ATIVA LINHA 4 DO TECLADO MATRICIAL
VETOR DE RESET PULA P/ INÍCIO DO
PROGRAMA
CONFIGURAÇÕES INICIAIS PORTAS, TIMERS,
INTERRUPÇÕES, OPTION, ADs.
BOTÃO PRESS.?
INÍCIO
APAGA LED
156
Código /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Programação em C18 - Recursos Básicos de programação * * Exemplo 1 * * * * CENTRO DE TREINAMENTO - MOSAICO ENGENHARIA * * * * TEL: (0XX11) 4992-8775 SITE: www.mosaico-eng.com.br * * E-MAIL: [email protected] * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * VERSÃO : 1.0 * * DATA : 23/02/2005 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Descrição geral * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ // Este software está preparado para demonstrar o controle dos pinos de I/O // este primeiro programa demonstrará o estado de um botão por meio de um led. /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * DEFINIÇÃO PIC * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ #include <p18F452.h> // Register definitions /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * INCLUDES DAS FUNÇÕES DE PERIFÉRICOS DO PIC * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ #include <pwm.h> //PWM library functions #include <adc.h> //ADC library functions #include <timers.h> //Timer library functions #include <delays.h> //Delay library functions #include <i2c.h> //I2C library functions #include <stdlib.h> //Library functions #include <usart.h> //USART library functions /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Configurações para gravação * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ //#pragma config OSC = XT //#pragma config WDT = ON //#pragma config LVP = OFF /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Definição e inicialização das variáveis Globais * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ //Neste bloco estão definidas as variáveis globais do programa. //Este programa não utiliza nenhuma variável de usuário /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Constantes internas * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ //A definição de constantes facilita a programação e a manutenção. //Este programa não utiliza nenhuma constante de usuário /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Declaração dos flags de software * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ //A definição de flags ajuda na programação e economiza memória RAM. //Este programa não utiliza nenhum flag de usuário /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Definição e inicialização dos port's * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * PROTOTIPAGEM DE FUNÇÕES * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
Curso Módulo 4 –Família 18F e MpLab C18 157
* ENTRADAS * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ // As entradas devem ser associadas a nomes para facilitar a programação e //futuras alterações do hardware. #define BOTAO PORTBbits.RB0 //PORTA DO BOTÃO //1 -> PRESSIONADO //0 -> LIBERADO /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * SAÍDAS * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ // AS SAÍDAS DEVEM SER ASSOCIADAS A NOMES PARA FACILITAR A PROGRAMAÇÃO E //FUTURAS ALTERAÇÕES DO HARDWARE. #define LED PORTDbits.RD0 //PORTA DO LED //0 -> APAGADO //1 -> ACESO #define C_LEDS PORTAbits.RA4 //PINO PARA ATIVAR GRUPO DE 8 LEDS //1 -> LEDS ATIVADOS //0 -> LEDS DESATIVADOS #define LINHA_4 PORTBbits.RB7 //PINO PARA ATIVAR LINHA 4 DO TECLADO //MATRICIAL //1 -> LINHA ATIVADA //0 -> LINHA DESATIVADA /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Função Principal * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ void main () PORTA = 0X00; //Clear PORTA PORTB = 0X00; //Clear PORTB PORTC = 0X00; //Clear PORTC PORTD = 0X00; //Clear PORTD PORTE = 0x00; //Clear PORTE LATA = 0X00; //Clear PORTA LATB = 0X00; //Clear PORTB LATC = 0X00; //Clear PORTC LATD = 0X00; //Clear PORTD LATE = 0x00; //Clear PORTE TRISA = 0b00101111; //CONFIG DIREÇÃO DOS PINOS PORTA TRISB = 0b00001111; //CONFIG DIREÇÃO DOS PINOS PORTB TRISC = 0b10011001; //CONFIG DIREÇÃO DOS PINOS PORTC TRISD = 0b00000000; //CONFIG DIREÇÃO DOS PINOS PORTD TRISE = 0b00000000; //CONFIG DIREÇÃO DOS PINOS PORTE ADCON1 = 0b00000111; //DESLIGA CONVERSORES A/D /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Inicialização do Sistema * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ C_LEDS = 1; //ATIVA LEDS LIGADOS AO PORTD LINHA_4 = 1; //ATIVA LINHA 4 DO TECLADO MATRICIAL /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Rotina Principal * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ while(1) ClrWdt(); if (BOTAO) LED = 1; // testa botão. Se botão = 0, então led = 1 else LED = 0; // caso contrário, led = 0 // FIM DO PROGRAMA
158
Exercícios Propostos
1. Altere a lógica do sistema, ou seja, com o botão pressionado o led deve permanecer apagado e com o botão liberado o led deve permanecer acesso.
2. Altere o software a fim de trocar a tecla ativa, passando por exemplo a utilizar a tecla da linha 4 coluna 2.
3. Altere o software para ligar/desligar outro led, por exemplo, o led ligado ao pino RD3.
Curso Módulo 4 –Família 18F e MpLab C18 159
Experiência 2 – Pisca - Pisca
Objetivo
O objetivo desta experiência é ensinar ao aluno com utilizar as funções de delays além de apresentar uma técnica simples utilizada para inverter o estado de um bit.
Descrição
O software desta experiência utiliza um dos displays de 7 segmentos para implementar um pisca-pisca sendo que a freqüência é controlada através do uso da função de delay.
A função de delay é genérica é pode ser utilizada para gerar delays entre 1ms e 256ms. Na realidade, a rotina recebe um argumento passado pelo WORK para determinar o atraso que deve ser gerado, sempre em múltiplos de 1ms. Como o argumento é de 8 bits existem 256 possíveis delays, indo de 1ms até 256ms. Basta portanto, carregar o WORK com o delay desejado e chamar a rotina.
O pisca-pisca é visualizado no display de 7 segmentos na posição da unidade. Sempre que o delay é finalizado o PORTD deve ser invertido e para inverter o estado destes bits utilizou-se a operação lógica boleana XOR. Conforme a tabela verdade apresentada a seguir.
A B XOR 0 0 0 0 1 1 1 0 1 1 1 0
Pode-se verificar que sempre que a operação é realizada quando os bits da coluna A estão em 1 o resultado
fica invertido em relação à coluna B e sempre que os bits da coluna A estão em 0 o resultado se mantém em relação à coluna B. Assim, sempre que ser desejar inverter um bit, basta fazer uma operação XOR entre um bit em 1 e o bit que se deseja inverter.
Esta é uma técnica simples de inverter o estado de um bit sem testar o estado original. No software, pode-se utilizar as teclas da linha 4 colunas 1 e 2 para alterar o tempo do delay e
conseqüentemente a freqüência das piscadas do display. A tecla da coluna 1 incrementa o valor do delay enquanto a tecla da coluna 2 decrementa.
160
Esquema Elétrico
Curso Módulo 4 –Família 18F e MpLab C18 161
Fluxograma
Sim
1 Sim
Não
Não
Não
BOTÃO 2 PRESS.?
2
FIM DO MULTIPLICADOR?
HABILITA DISPLAY DA UNIDADE E LINHA 4 DO TECLADO MATRICIAL
INVERTE LEDS DO DISPLAY
Sim
CARREGA MULTIPLICADOR PARA O DELAY
VETOR DE RESET PULA P/ INÍCIO DO
PROGRAMA
CONFIGURAÇÕES INICIAIS PORTAS, TIMERS,
INTERRUPÇÕES, OPTION, ADs.
BOTÃO 1 PRESS.?
INÍCIO
DELAY PROPORCIONAL AO VALOR DO
CONTADOR
CARREGA CONTADOR COM VALOR MÍNIMO
ATUALIZA DISPLAY3
162
Sim
Não
Sim
Não CONTADOR<MÍN?
CONTADOR = CONTADOR - STEP
1
CONTADOR = MÍNIMO
BOTÃO 1 SOLTO?
3
Sim
Não
Sim
Não CONTADOR>MÁX?
CONTADOR = CONTADOR + STEP
2
CONTADOR = MÁXIMO
BOTÃO 2 SOLTO?
3
Curso Módulo 4 –Família 18F e MpLab C18 163
Código /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Programação em C18 - Recursos Básicos de programação * * Exemplo 2 * * * * CENTRO DE TREINAMENTO - MOSAICO ENGENHARIA * * * * TEL: (0XX11) 4992-8775 SITE: www.mosaico-eng.com.br * * E-MAIL: [email protected] * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * VERSÃO : 1.0 * * DATA : 23/02/2005 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Descrição geral * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ //PISCA-PISCA VARIÁVEL PARA DEMONSTRAR A IMPLEMENTAÇÃO DE DELAYS E A INVERSÃO //DE PORTAS. APENAS OS BOTÕES DA LINHA 4 ESTÃO ATIVADOS SENDO QUE O DA //COLUNA 1 É UTILIZADO PARA INCREMENTAR O TEMPO ENTRE AS PISCADAS. //O BOTÃO DA COLUNA 2 É UTILIZADO PARA DIMINUIR O TEMPO ENTRE AS PISCADAS. /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * DEFINIÇÃO PIC * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ #include <p18F452.h> // Register definitions /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * INCLUDES DAS FUNÇÕES DE PERIFÉRICOS DO PIC * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ #include <pwm.h> //PWM library functions #include <adc.h> //ADC library functions #include <timers.h> //Timer library functions #include <delays.h> //Delay library functions #include <i2c.h> //I2C library functions #include <stdlib.h> //Library functions #include <usart.h> //USART library functions /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Configurações para gravação * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ #pragma config OSC = XT #pragma config LVP = OFF /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Definição e inicialização das variáveis Globais * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ //Neste bloco estão definidas as variáveis globais do programa. //Este programa não utiliza nenhuma variável de usuário unsigned char CONTADOR; //BASE DE TEMPO PARA A PISCADA unsigned char TEMPO1; //REGISTRADORES AUXILIARES DE TEMPO /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Constantes internas * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ //A definição de constantes facilita a programação e a manutenção. #define MIN 10 #define MAX 240 #define STEP 5 #define MULTIPLO 5
164
//A CONSTANTE DISPLAY REPRESENTA O SÍMBOLO QUE APARECERÁ PISCANDO NO //DISPLAY. 1=LED LIGADO E 0=LED DESLIGADO. A RELAÇÃO ENTRE BITS E //SEGMENTOS É A SEGUINTE: '.GFEDCBA' // a // ********** // * * // f * * b // * g * // ********** // * * // e * * c // * d * // ********** *. #define DISPLAY 0b01110110 //(LETRA H) /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Declaração dos flags de software * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ //A definição de flags ajuda na programação e economiza memória RAM. //Este programa não utiliza nenhum flag de usuário /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Definição e inicialização dos port's * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * PROTOTIPAGEM DE FUNÇÕES * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * ENTRADAS * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ // As entradas devem ser associadas a nomes para facilitar a programação e //futuras alterações do hardware. #define BT1 PORTBbits.RB0 //PORTA DO BOTÃO //1 -> PRESSIONADO //0 -> LIBERADO #define BT2 PORTBbits.RB1 //PORTA DO BOTÃO //1 -> PRESSIONADO //0 -> LIBERADO /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * SAÍDAS * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ // AS SAÍDAS DEVEM SER ASSOCIADAS A NOMES PARA FACILITAR A PROGRAMAÇÃO E //FUTURAS ALTERAÇÕES DO HARDWARE. #define DSP_UNIDADE PORTBbits.RB7 //PORTA DO LED //0 -> APAGADO //1 -> ACESO #define LINHA_4 PORTBbits.RB7 //PINO PARA ATIVAR LINHA 4 DO TECLADO //MATRICIAL //1 -> LINHA ATIVADA //0 -> LINHA DESATIVADA /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Função Principal * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ void main () PORTA = 0X00; //Clear PORTA PORTB = 0X00; //Clear PORTB PORTC = 0X00; //Clear PORTC PORTD = 0X00; //Clear PORTD PORTE = 0x00; //Clear PORTE
Curso Módulo 4 –Família 18F e MpLab C18 165
LATA = 0X00; //Clear PORTA LATB = 0X00; //Clear PORTB LATC = 0X00; //Clear PORTC LATD = 0X00; //Clear PORTD LATE = 0x00; //Clear PORTE TRISA = 0b00101111; //CONFIG DIREÇÃO DOS PINOS PORTA TRISB = 0b00001111; //CONFIG DIREÇÃO DOS PINOS PORTB TRISC = 0b10011001; //CONFIG DIREÇÃO DOS PINOS PORTC TRISD = 0b00000000; //CONFIG DIREÇÃO DOS PINOS PORTD TRISE = 0b00000000; //CONFIG DIREÇÃO DOS PINOS PORTE ADCON1 = 0b00000111; //DESLIGA CONVERSORES A/D /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Inicialização do Sistema * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ DSP_UNIDADE = 1; PORTD = DISPLAY; //ACENDE O VALOR CERTO NO DISPLAY CONTADOR = MIN; //INICIA CONTADOR COM VALOR MIN. /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Rotina Principal * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ while(1) ClrWdt(); TEMPO1 = MULTIPLO; do Delay1KTCYx(CONTADOR); if (BT1) if (CONTADOR != MAX) CONTADOR = CONTADOR + STEP; //INC O CONTADOR EM STEP if (BT2) if (CONTADOR != MIN) CONTADOR = CONTADOR - STEP;//DEC. O CONTADOR EM STEP while (--TEMPO1); //VOLTA PARA O INICIO ENQUANTO //TEMPO1 DIFERENTE DE ZERO. PORTD = PORTD ^ DISPLAY; //INVERTE O PORTD // FIM DO PROGRAMA
166
Exercícios Propostos
1. Alterar a rotina de delay para gerar um delay fundamental de 100us. 2. Alterar a rotina de delay para gerar um delay fundamental de 10ms. 3. Incluir o tratamento de debounce nas teclas
Curso Módulo 4 –Família 18F e MpLab C18 167
Experiência 3 - Dimmer
Objetivo
O objetivo desta experiência é ensinar ao aluno como regular a potência fornecida a pequenas cargas através do microcontrolador. Para isso, é proposto um dimmer para uma lâmpada incandescente onde a potência de lâmpada é regulada através do uso de um PWM criado no software.
Descrição
Software que utiliza dois botões para incrementar e decrementar a intensidade da lâmpada. A interrupção de tmr0 é utilizada para controlar o PWM que aciona a lâmpada. A intensidade também é mostrada no display da unidade.
Os botões ativos são os da linha 4. O botão da coluna 1 incrementa a intensidade da lâmpada e o da coluna 2 decrementa a intensidade.
Foi criado um PWM com 4 bits de resolução de forma que existem 16 intensidades diferentes para a lâmpada. Para sua implementação, utilizou-se a interrupção de TMR0 sendo que a cada interrupção uma variável é incrementada. Esta variável é utilizada para subdividir o PWM em 16 intervalos. Comparando esta variável com o duty cycle ajustado pelo usuário, o software coloca o pino da lâmpada em nível lógico 1 ou 0, conforme a necessidade. A cada 16 interrupções o ciclo se repete e portanto o período do PWM é 16 vezes maior do que o período de uma interrupção. Este procedimento pode ser facilmente analisado pelo fluxograma da experiência.
168
Esquema Elétrico
Curso Módulo 4 –Família 18F e MpLab C18 169
Fluxograma
1 Sim
Sim
3
Não
BOTÃO 1 PRESS.?
RECARREGA FILTRO DE DEBOUNCE
HABILITA DISPLAY DA UNIDADE E LINHA 4 DO TECLADO MATRICIAL
VETOR DE RESET PULA P/ INÍCIO DO
PROGRAMA
CONFIGURA I/Os
INÍCIO
CARREGA INTENSIDADE COM VALOR MÍNIMO
Não
BOTÃO 2 PRESS.? 2
4
PRESCALER TMR0 -> 1:4
HABILITA INTERRUPÇÃO DE TMR0
ATUALIZA DISPLAY
170
Sim
Sim
Não
Não
INTENSIDADE = MÍNIMO ?
FIM FILTRO DEBOUNCE? 4
DECREMENTA INTENSIDADE
1
3
3
Sim
Sim
Não
Não
INTENSIDADE = MÁXIMO ?
FIM FILTRO DEBOUNCE? 4
INCREMENTA INTENSIDADE
1
3
3
Curso Módulo 4 –Família 18F e MpLab C18 171
Não
Sim
período de 1ms
Não
INT. TMR0
Sim
Sim
Não
INTENSIDADE = 15 ?
TEMP0 = 16 ?
RECARREGA TMR0 COM 6d
DESLIGA LÂMPADA
ZERA TEMP0
LIGA LÂMPADA
LIMPA FLAG DA INTERRUPÇÃO
SALVA CONTEXTO
SAI DA INTERRUPÇÃO
INTENSIDADE > TEMP0 ?
RECUPERA CONTEXTO
INCREMENTA TEMP0
172
Código /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Programação em C18 - Recursos Básicos de programação * * Exemplo 3 * * * * CENTRO DE TREINAMENTO - MOSAICO ENGENHARIA * * * * TEL: (0XX11) 4992-8775 SITE: www.mosaico-eng.com.br * * E-MAIL: [email protected] * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * VERSÃO : 1.0 * * DATA : 23/02/2005 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Descrição geral * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ //SOFTWARE QUE UTILIZA DOIS BOTÕES PARA INCREMENTAR E DECREMENTAR A //INTENSIDADE DA LÂMPADA. A INTERRUPÇÃO DE TMR0H É UTILIZADA PARA CONTROLAR //O PWM QUE ACIONA A LÂMPADA. A INTENSIDADE TAMBÉM É MOSTRADA NO DISPLAY //DA UNIDADE. //OS BOTÕES ATIVOS SÃO O DA LINHA 4 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * DEFINIÇÃO PIC * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ #include <p18F452.h> // Register definitions /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * INCLUDES DAS FUNÇÕES DE PERIFÉRICOS DO PIC * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ #include <pwm.h> //PWM library functions #include <adc.h> //ADC library functions #include <timers.h> //Timer library functions #include <delays.h> //Delay library functions #include <i2c.h> //I2C library functions #include <stdlib.h> //Library functions #include <usart.h> //USART library functions /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Configurações para gravação * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ #pragma config OSC = XT #pragma config LVP = OFF /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Definição e inicialização das variáveis Globais * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ //Neste bloco estão definidas as variáveis globais do programa. //Este programa não utiliza nenhuma variável de usuário unsigned char INTENSIDADE; //ARMAZENA O VALOR DA CONTAGEM unsigned char FILTRO11; //FILTRAGEM 1 PARA O BOTÃO 1 unsigned char FILTRO12; //FILTRAGEM 2 PARA O BOTÃO 1 unsigned char FILTRO21; //FILTRAGEM 1 PARA O BOTÃO 2 unsigned char FILTRO22; //FILTRAGEM 2 PARA O BOTÃO 2 unsigned char TEMPO; //INTERVALOS DE 1 MS /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Constantes internas * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ //A definição de constantes facilita a programação e a manutenção. #define MIN 0 #define MAX 15 #define T_FILTRO 25 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Declaração dos flags de software * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
Curso Módulo 4 –Família 18F e MpLab C18 173
//A definição de flags ajuda na programação e economiza memória RAM. //Este programa não utiliza nenhum flag de usuário struct unsigned BIT0:1; unsigned BIT1:1; unsigned BIT2:1; unsigned BIT3:1; unsigned BIT4:1; unsigned BIT5:1; unsigned BIT6:1; unsigned BIT7:1; FLAGSbits; //ARMAZENA OS FLAGS DE CONTROLE #define ST_BT1 FLAGSbits.BIT0 //STATUS DO BOTÃO 1 #define ST_BT2 FLAGSbits.BIT1 //STATUS DO BOTÃO 2 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * PROTOTIPAGEM DE FUNÇÕES * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ void TRATA_INT_TIMER0 (void); /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * ENTRADAS * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ // As entradas devem ser associadas a nomes para facilitar a programação e //futuras alterações do hardware. #define BT1 PORTBbits.RB0 //PORTA DO BOTÃO //1 -> PRESSIONADO //0 -> LIBERADO #define BT2 PORTBbits.RB1 //PORTA DO BOTÃO //1 -> PRESSIONADO //0 -> LIBERADO /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * SAÍDAS * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ // AS SAÍDAS DEVEM SER ASSOCIADAS A NOMES PARA FACILITAR A PROGRAMAÇÃO E //FUTURAS ALTERAÇÕES DO HARDWARE. #define DSP_UNIDADE PORTBbits.RB7 //PORTA DO LED //0 -> APAGADO
//1 -> ACESO #define LINHA_4 PORTBbits.RB7 //PINO PARA ATIVAR LINHA 4 DO TECLADO //MATRICIAL //1 -> LINHA ATIVADA //0 -> LINHA DESATIVADA #define LAMPADA PORTCbits.RC5 //DEFINE PINO DA LAMPADA //0 -> LÂMPADA APAGADA //1 -> LÂMPADA ACESA // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // * ROTINA DE TRATAMENTO DE INT DE TIMER0 * // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * #pragma code TIMER0_interrupt = 0x8 void TIMER0_int (void) _asm goto TRATA_INT_TIMER0 _endasm #pragma code #pragma interrupt TRATA_INT_TIMER0 void TRATA_INT_TIMER0 (void) INTCONbits.TMR0IF = 0; //LIMPA FLAG DE INTERRUPÇÃO WriteTimer0(256 - 250); TEMPO++; if (TEMPO == 16)TEMPO = 0; if (TEMPO >= INTENSIDADE) LAMPADA = 0;
174
else LAMPADA = 1; // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // * ROTINA DE CONVERSÃO BINÁRIO -> DISPLAY * // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // ESTA ROTINA IRÁ RETORNAR EM W, O SIMBOLO CORRETO QUE DEVE SER // MOSTRADO NO DISPLAY PARA CADA VALOR DE INTENSIDADE. O RETORNO JÁ ESTÁ // FORMATADO PARA AS CONDIÇÕES DE LIGAÇÃO DO DISPLAY AO PORTD. // a // ********** // * * // f * * b // * g * // ********** // * * // e * * c // * d * // ********** *. const rom unsigned char CONVERTE[16] = // .GFEDCBA POSIÇÃO CORRETA DOS SEGMENTOS 0b00111111, // 00 - RETORNA SÍMBOLO CORRETO 0 0b00000110, // 01 - RETORNA SÍMBOLO CORRETO 1 0b01011011, // 02 - RETORNA SÍMBOLO CORRETO 2 0b01001111, // 03 - RETORNA SÍMBOLO CORRETO 3 0b01100110, // 04 - RETORNA SÍMBOLO CORRETO 4 0b01101101, // 05 - RETORNA SÍMBOLO CORRETO 5 0b01111101, // 06 - RETORNA SÍMBOLO CORRETO 6 0b00000111, // 07 - RETORNA SÍMBOLO CORRETO 7 0b01111111, // 08 - RETORNA SÍMBOLO CORRETO 8 0b01101111, // 09 - RETORNA SÍMBOLO CORRETO 9 0b01110111, // 10 - RETORNA SÍMBOLO CORRETO A 0b01111100, // 11 - RETORNA SÍMBOLO CORRETO b 0b00111001, // 12 - RETORNA SÍMBOLO CORRETO C 0b01011110, // 13 - RETORNA SÍMBOLO CORRETO d 0b01111001, // 14 - RETORNA SÍMBOLO CORRETO E 0b01110001; // 15 - RETORNA SÍMBOLO CORRETO F /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Função Principal * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ void main () PORTA = 0X00; //Clear PORTA PORTB = 0X00; //Clear PORTB PORTC = 0X00; //Clear PORTC PORTD = 0X00; //Clear PORTD PORTE = 0x00; //Clear PORTE LATA = 0X00; //Clear PORTA LATB = 0X00; //Clear PORTB LATC = 0X00; //Clear PORTC LATD = 0X00; //Clear PORTD LATE = 0x00; //Clear PORTE TRISA = 0b00101111; //CONFIG DIREÇÃO DOS PINOS PORTA TRISB = 0b00001111; //CONFIG DIREÇÃO DOS PINOS PORTB TRISC = 0b10011001; //CONFIG DIREÇÃO DOS PINOS PORTC TRISD = 0b00000000; //CONFIG DIREÇÃO DOS PINOS PORTD TRISE = 0b00000000; //CONFIG DIREÇÃO DOS PINOS PORTE ADCON1 = 0b00000111; //DESLIGA CONVERSORES A/D /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Inicialização do Sistema * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ OpenTimer0(TIMER_INT_ON & T0_8BIT & T0_SOURCE_INT & T0_PS_1_4); DSP_UNIDADE = 1; ST_BT1 = 0; ST_BT2 = 0;
Curso Módulo 4 –Família 18F e MpLab C18 175
INTENSIDADE = MIN; //INICIA INTENSIDADE = MIN WriteTimer0(256 - 250); //REINICIA TMR0H, SETA TIMER P/ 1ms PORTD = CONVERTE[INTENSIDADE]; //ATUALIZA O DISPLAY INICIALMENTE RCON = 0X00; INTCONbits.TMR0IE = 1; INTCONbits.GIEH = 1; /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Rotina Principal * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ while(1) ClrWdt(); FILTRO11 = 0; FILTRO21 = 0; FILTRO12 = T_FILTRO; FILTRO22 = T_FILTRO; while (BT1) if (--FILTRO11 == 0) FILTRO12--; if (FILTRO12 == 0) if (INTENSIDADE != MIN) if (!ST_BT1) INTENSIDADE--; PORTD = CONVERTE[INTENSIDADE]; ST_BT1 = 1; ST_BT1 = 0; // BOTÃO LIBERADO, LIMPA O FLAG while (BT2) if (--FILTRO21 == 0) FILTRO22--; if (FILTRO22 == 0) if (INTENSIDADE != MAX) if (!ST_BT2) INTENSIDADE++; PORTD = CONVERTE[INTENSIDADE]; ST_BT2 = 1; ST_BT2 = 0; // BOTÃO LIBERADO, LIMPA O FLAG // FIM DO PROGRAMA
176
Dicas e Comentários
A técnica utilizada nesta experiência para implementar o PWM apresenta como inconveniente resultar em PWMs de baixa freqüência. Por exemplo, no caso desta experiência a interrupção ocorre a cada 1ms e como o PWM têm 4 bits de resolução, temos um período total de 16ms para o PWM. Isto significa que a freqüência é de 62,5Hz, ou seja, o PWM implementado via software apresenta baixa freqüência. Além disso, se ele fosse de por exemplo 8 bits, o período passaria para 256ms e, portanto, a freqüência cairia para aproximadamente 4Hz, ou seja, extremamente baixa. Note que quanto maior a resolução desejada maior é o período do PWM.
Exercícios Propostos
1. Altere a freqüência do PWM alterando a base de tempo do TMR0 para 100us. 2. Altere o PWM para trabalhar com apenas 2 bits de resolução. 3. Acrescente as rotinas de leitura e escrita na EEPROM para salvar e recuperar a intensidade da
lâmpada mesmo na falta de energia.
Curso Módulo 4 –Família 18F e MpLab C18 177
Experiência 4 – Botões, Leds e Buzzer
Objetivo
O objetivo desta experiência é dar continuidade ao aprendizado das interrupções e em particular à interrupção de TMR2 utilizada nesta experiência para excitar o buzzer.
Descrição
O buzzer utilizado no MCMASTER não é do tipo auto-oscilante, ou seja, este tipo de buzzer precisa ser excitado externamente para emitir algum som. Assim, o microcontrolador deve ser responsável por gerar uma onda quadrada a fim de excitar o piezo-elétrico e fazer com que o buzzer emita algum som. Operar com este tipo de buzzer é muito mais trabalhoso do que com um buzzer auto-oscilante, já que não basta ligar-lo, é necessário aplicar uma onda quadrada para que o buzzer funcione. Porém, ele dá margem à criação de diferentes tons, pois, quem gera a freqüência da onda quadrada e conseqüentemente o tom com que o buzzer irá tocar é o microcontrolador e se este gerar freqüências adequadas, pode-se implementar até mesmo uma simples melodia.
Apesar desta experiência utilizar o TMR2 para excitar o buzzer com freqüência variável, o objetivo não é a implementação de notas musicais, embora isto seja possível.
Diferente dos outros timers do microcontrolador, o TMR2 estoura e pode gerar uma interrupção sempre que o seu valor ultrapassa o valor de um registrador especial, denominado PR2. Ou seja, o TMR2, diferentemente dos outros timers conta desde zero até o valor programado no registrador PR2 e não até 255 (a não ser que o valor do registrador PR2 seja 255).
Desta forma, o que o software faz é ler o estado das teclas da linha 4 e em função deste alterar o valor do registrador PR2. Conseqüentemente, a interrupção de TMR2 varia de período, ou seja, a freqüência com que a interrupção ocorre depende do estado das teclas da linha 4. Como é a interrupção a responsável por gerar a onda quadrada que excita o buzzer e como a freqüência pode ser alterada pelas teclas, dependendo da configuração de teclas pressionadas o buzzer emite som em diferentes tonalidades, criando, embora com freqüência erradas uma espécie de mini-teclado.
Os leds ligados ao PORTD são utilizados apenas para repetir o estado das teclas pressionadas a fim de criar um efeito visual para a experiência.
178
Esquema Elétrico
Curso Módulo 4 –Família 18F e MpLab C18 179
Fluxograma
INICIO
INICIALIZA VARIÁVEIS FILTRO DOS BOTÕES
LIGA AS INTERRUPÇÕES
ROTINA PRINCIPAL LIMPA WDT
VETOR DE RESET PULA P/ INICIO DO
PROGRAMA
CONFIGURAÇÕES INICIAS PORTAS, TIMERS,
INTERRUPÇÕES, OPTION, ADS.
HABILITA LEDS E LINHA 4 DO TECLADO MATRICIAL
180
Não
Sim
INTERRUPÇÕES
SAI DA INTERRUPÇÃO
Não
Sim
RECUPERA CONTEXTO
SALVA CONTEXTO
INT TMR2 ?
INT TMR0 ?
FIM INTERRUPÇÃO
TRATA TMR0
TRATA TMR2
LIMPA FLAG DA INTERRUPÇÃO
LIGA SAÍDA
FIM INTERRUPÇÃO
TRATA TMR2
SAÍDA BUZZER LIGADA ?
LIGA SAÍDA
Não
Sim
Curso Módulo 4 –Família 18F e MpLab C18 181
CHECA BOTÃO 0
LIMPA FLAG DA INTERRUPÇÃO
LIMPA FLAG DE BOTÃO
PRESSIONADO
TRATA TMR0
BOTÃO PRESS. ?
Não
Não
REINICIA FILTRO
MARCA FLAG DE BOTÃO PRESSIONADO
FILTRO TERMINOU ?
Sim
Sim
CHECA BOTÃO 1
CHECA BOTÃO 2
CHECA BOTÃO 3
1
182
ATUALIZA LEDS CONFORME FLAGS
DOS BOTÕES
CONSULTA TABELA DE FREQÜÊNCIA CONFORME BOTÕES PRESSIONADOS
INICIALIZA TMR2 COM 255d
1
ALGUM BOTÃO
PRESS. ?
Não
INICIALIZA TIMER 2
LIGA INTERRUPÇÃO DE TIMER 2
DESLIGA INTERRUPÇÃO DE
TMR2
Sim
FIM INTERRUPÇÃO
DESLIGA SAÍDA DO BUZZER
Curso Módulo 4 –Família 18F e MpLab C18 183
Código /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Programação em C18 - Recursos Básicos de programação * * Exemplo 4 * * * * CENTRO DE TREINAMENTO - MOSAICO ENGENHARIA * * * * TEL: (0XX11) 4992-8775 SITE: www.mosaico-eng.com.br * * E-MAIL: [email protected] * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * VERSÃO : 1.0 * * DATA : 10/08/2005 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Descrição geral * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ /* ESTE SOFTWARE ESTÁ PREPARADO PARA LER QUATRO BOTÕES E TOCAR O BUZZER COM DURAÇÃO VERIÁVEL CONFORME TECLA PRESSIONADA. */ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * DEFINIÇÃO PIC * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ #include <p18F452.h> // Register definitions /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * INCLUDES DAS FUNÇÕES DE PERIFÉRICOS DO PIC * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ #include <pwm.h> //PWM library functions #include <adc.h> //ADC library functions #include <timers.h> //Timer library functions #include <delays.h> //Delay library functions #include <i2c.h> //I2C library functions #include <stdlib.h> //Library functions #include <usart.h> //USART library functions /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Configurações para gravação * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ #pragma config OSC = XT #pragma config WDT = ON #pragma config LVP = OFF /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Definição e inicialização das variáveis Globais * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ //Neste bloco estão definidas as variáveis globais do programa. //Este programa não utiliza nenhuma variável de usuário unsigned char filtro1; unsigned char filtro2; unsigned char filtro3; unsigned char filtro4; unsigned char status_leds; // atualiza leds conforme o botão pressionado const rom unsigned char tabela[16] = 255,16,32,48,64,80,96,112,128,144,160,176,192,208,224,240; /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Constantes internas * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ //A definição de constantes facilita a programação e a manutenção. #define MIN 0 #define MAX 15
184
#define t_filtro 25 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Declaração dos flags de software * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ //A definição de flags ajuda na programação e economiza memória RAM. //Este programa não utiliza nenhum flag de usuário /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Definição e inicialização dos port's * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * PROTOTIPAGEM DE FUNÇÕES * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ void TRATA_HIGH_INT(void); void TRATA_INT_TIMER0(void); void TRATA_INT_TIMER2(void); /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * ENTRADAS * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ // As entradas devem ser associadas a nomes para facilitar a programação e //futuras alterações do hardware. #define botao1 PORTBbits.RB0 //PORTA DO BOTÃO //1 -> PRESSIONADO //0 -> LIBERADO #define botao2 PORTBbits.RB1 //PORTA DO BOTÃO //1 -> PRESSIONADO //0 -> LIBERADO #define botao3 PORTBbits.RB2 //PORTA DO BOTÃO //1 -> PRESSIONADO //0 -> LIBERADO #define botao4 PORTBbits.RB3 //PORTA DO BOTÃO //1 -> PRESSIONADO //0 -> LIBERADO /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * SAÍDAS * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ // AS SAÍDAS DEVEM SER ASSOCIADAS A NOMES PARA FACILITAR A PROGRAMAÇÃO E //FUTURAS ALTERAÇÕES DO HARDWARE. #define C_LEDS PORTAbits.RA4 //PINO PARA ATIVAR GRUPO DE 8 LEDS //1 -> LEDS ATIVADOS //0 -> LEDS DESATIVADOS #define LINHA_4 PORTBbits.RB7 //PINO PARA ATIVAR LINHA 4 DO TECLADO //MATRICIAL //1 -> LINHA ATIVADA //0 -> LINHA DESATIVADA #define buzzer PORTEbits.RE2 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // * ROTINA DE TRATAMENTO DE INT DE ALTA PRIORIDADE * // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * #pragma code VETOR_HIGH_PRIORITY = 0x0008 void HIGH_int (void) _asm goto TRATA_HIGH_INT _endasm #pragma code #pragma interrupt TRATA_HIGH_INT void TRATA_HIGH_INT(void)
Curso Módulo 4 –Família 18F e MpLab C18 185
if(PIR1bits.TMR2IF) TRATA_INT_TIMER2(); if(INTCONbits.TMR0IF) TRATA_INT_TIMER0(); void TRATA_INT_TIMER2(void) PIR1bits.TMR2IF = 0; //LIMPA FLAG DE INTERRUPÇÃO if (buzzer != 0) // o buzzer está ligado ? buzzer = 0; // sim, então desliga else buzzer = 1; // não, então liga void TRATA_INT_TIMER0(void) INTCONbits.TMR0IF = 0; //LIMPA FLAG DE INTERRUPÇÃO /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Tratamento do Botão 1 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ if (botao1) // testa botão 1 // botão 1 está pressionado ? filtro1--; // Sim, então decrementa o filtro if (filtro1 == 0) // acabou o filtro do botão 1 ? status_leds = status_leds | 0b00000001; // marca que o botão está pressionado else // botão 1 liberado filtro1 = t_filtro; // inicia o filtro do botão 1 status_leds = status_leds & 0b11111110; // marca que o botão foi liberado /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Tratamento do Botão 2 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ if (botao2) // testa botão 2 // botão 2 está pressionado ? filtro2--; // Sim, então decrementa o filtro if (filtro2 == 0) // fim do filtro do botão 2 ? status_leds = status_leds | 0b00000010; // marca que o botão está pressionado else // botão 2 liberado filtro2 = t_filtro; // inicia o filtro do botão 2 status_leds = status_leds & 0b11111101; // marca que o botão foi liberado /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Tratamento do Botão 3 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ if (botao3) // testa botão 3 // botão 3 está pressionado ? filtro3--; // Sim, então decrementa o filtro if (filtro3 == 0) // fim do filtro do botão 3 ?
186
status_leds = status_leds | 0b00000100; // marca que o botão está pressionado else // botão 3 liberado filtro3 = t_filtro; // inicia o filtro do botão 3 status_leds = status_leds & 0b11111011; // marca que o botão foi liberado /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Tratamento do Botão 4 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ if (botao4) // testa botão 4 // botão 4 está pressionado ? filtro4--; // Sim, então decrementa o filtro if (filtro4 == 0) // fim do filtro do botão 4 ? status_leds = status_leds | 0b00001000; // marca que o botão está pressionado else // botão 4 liberado filtro4 = t_filtro; // inicia o filtro do botão 4 status_leds = status_leds & 0b11110111; // marca que o botão foi liberado PORTD = status_leds; if (status_leds == 0) PR2 = 0xff; CloseTimer2(); buzzer = 0; else PR2 = tabela[status_leds]; // consulta tabela e inicializa timer2. OpenTimer2(TIMER_INT_ON & T2_PS_1_1 & T2_POST_1_3); /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Função Principal * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ void main () PORTA = 0X00; //Clear PORTA PORTB = 0X00; //Clear PORTB PORTC = 0X00; //Clear PORTC PORTD = 0X00; //Clear PORTD PORTE = 0x00; //Clear PORTE LATA = 0X00; //Clear PORTA LATB = 0X00; //Clear PORTB LATC = 0X00; //Clear PORTC LATD = 0X00; //Clear PORTD LATE = 0x00; //Clear PORTE TRISA = 0b00101111; //CONFIG DIREÇÃO DOS PINOS PORTA TRISB = 0b00001111; //CONFIG DIREÇÃO DOS PINOS PORTB
Curso Módulo 4 –Família 18F e MpLab C18 187
TRISC = 0b10011001; //CONFIG DIREÇÃO DOS PINOS PORTC TRISD = 0b00000000; //CONFIG DIREÇÃO DOS PINOS PORTD TRISE = 0b00000000; //CONFIG DIREÇÃO DOS PINOS PORTE ADCON1 = 0b00000111; //DESLIGA CONVERSORES A/D /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Inicialização do Sistema * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ OpenTimer0(TIMER_INT_ON & T0_8BIT & T0_SOURCE_INT & T0_PS_1_4); PR2 = 0xff; OpenTimer2(TIMER_INT_OFF & T2_PS_1_1 & T2_POST_1_3); RCON = 0X00; C_LEDS = 1; //ATIVA LEDS LIGADOS AO PORTD LINHA_4 = 1; //ATIVA LINHA 4 DO TECLADO MATRICIAL status_leds = 0; filtro1 = t_filtro; // inicia filtro do botao1 filtro2 = t_filtro; // inicia filtro do botao2 filtro3 = t_filtro; // inicia filtro do botao3 filtro4 = t_filtro; // inicia filtro do botao4 INTCONbits.TMR0IE = 1; INTCONbits.PEIE = 1; PIE1bits.TMR2IE = 1; INTCONbits.GIEH = 1; /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Rotina Principal * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ while(1) ClrWdt(); // FIM DO PROGRAMA
188
Dicas e Comentários
Notar que nesta experiência foi utilizada uma tabela para retornar em função do estado das teclas pressionadas o valor que deve ser carregado no registrador PR2. Ou seja, conforme comentado na experiência 4, a utilização de tabelas não se limita apenas à conversão de BCD para display de 7 segmentos. Prova disso é que nesta experiência a tabela foi útil para exercer função completamente diferente.
O software desta experiência apresenta uma particularidade. Veja que não existe nenhuma instrução, a não ser o CLRWDT, no loop principal do programa. Assim, todas as instruções do software e toda a sua lógica está implementada apenas dentro das interrupções, sendo parte na interrupção de TMR0 e parte na interrupção de TMR2.
Exercícios Propostos
1. Alterar a configuração do TMR2 para que o buzzer seja excitado em outra faixa de freqüências. 2. Utilizar a instrução boleana XOR para inverter o estado do pino do buzzer. 3. Inverter a escala de freqüências, trocando a mais alta pela mais baixa.
Curso Módulo 4 –Família 18F e MpLab C18 189
Experiência 5 – Varredura de displays e utilização do TIMER 1
Objetivo
O objetivo desta experiência é o aprendizado do sistema de varredura comumente utilizado para varrer displays de 7 segmentos. Além disso, é visto o TMR1 fechando assim o estudo sobre os três times do PIC16F877A ou do PIC18F452.
Descrição
Cada display é formado por um conjunto de 8 leds, sendo 7 para a caracterização do dígito e 1 para o ponto decimal. Desta forma, precisaremos de um pino do PIC para controlar cada um dos leds e, portanto, serão necessários 8 pinos para acionar os 7 segmentos e mais o ponto decimal. Porém, o MCMASTER não possui apenas um display e sim quatro. Seriam necessários então 32 pinos do microcontrolador para acionar os quatro displays? Não, existe uma saída para isso. O segredo para minimizar a quantidade de pinos utilizados é o emprego de um conceito denominado varredura.
Para isso, interligam-se todos os displays, juntando todos os pinos de um mesmo segmento numa única via, de forma a criar um barramento de dados com as vias de A até Dp. Em seguida, utiliza-se um pino para controlar o comum de cada um dos displays (total de 4 pinos). Assim, quando se deseja escrever em um dos displays, bastar informar os segmentos a serem acionados nas vias de dados e ligar o comum do display desejado.
Utilizando o hardware desta forma é fácil notar que apenas um dos displays poderá ser acionado de cada vez. Porém, se acionarmos os displays continuamente, um após o outro e de forma rápida, nossos olhos não conseguiram perceber que apenas um display está acionado por vez dando a impressão de que todos os displays estão acionados o tempo todo.
Assim, empregando-se a técnica de varredura, consegue-se controlar os 4 displays utilizando apenas 12 pinos
do microcontrolador. O exemplo desenvolvido para esta experiência faz muito mais que simplesmente implementar a varredura dos
displays. Trata-se de um contador regressivo de segundos, ou seja, um temporizador capaz de contar até 9.999 segundos.
As teclas habilitadas são as da linha 4 e seguem as funções descritas na tabela.
Coluna Descrição 1 nenhuma função 2 Incrementa o valor inicial em 1 segundo 3 Decrementa o valor inicial em 1 segundo 4 Inicia e paralisa o temporizador
Para a contagem do tempo utilizou-se a interrupção de TMR1, configurada conforme a tabela a seguir.
A B C D E F G Dp
Display 1 Display 2 Display 3 Display 4
Via
s de
Dad
os
190
Ciclo de Maq. Prescale Conta TMR1 Auxiliar Período Freqüência1 µs 8 62500 2 1.000.000 µs 1 Hz
Configurou-se o prescale do TMR1 em 1:8 e o contador foi inicializado com o valor total menos o desejado
para a contagem (65.536 – 62.500). Desta maneira a interrupção ocorre a cada 0,5 segundo. A fim de criar a base de tempo de 1 segundo foi utilizada uma variável auxiliar que é decrementada a cada ocorrência da interrupção.
Curso Módulo 4 –Família 18F e MpLab C18 191
Esquema Elétrico
192
Fluxograma
Não
INICIALIZA VARIÁVEIS
LIGA AS INTERRUPÇÕES
LIMPA WDT
VETOR DE RESET PULA P/ INÍCIO DO
PROGRAMA
CONFIGURAÇÕES INICIAIS PORTAS, TIMERS,
INTERRUPÇÕES, OPTION, ADs.
1
INÍCIO
RECARREGA FILTRO E TURBO P/ TECLAS
UP PRESS.?
START/STOPPRESS.?
DOWN PRESS.?
Não
Não
TRATA BOTÃO UP
TRATA BOTÃO DOWN
TRATA BOTÃO START / STOP
Sim
Sim
Sim
Curso Módulo 4 –Família 18F e MpLab C18 193
Não
LINHA 4 HABILITADA ?
Sim
Não
INTERRUPÇÕES
SAI DA INTERRUPÇÃO
Sim
RECUPERA CONTEXTO
SALVA CONTEXTO
INT TMR0 ?
RECARREGA TEMPORIZADOR
DE 1seg.
LIMPA FLAG DE TIMER LIGADO
(LED)
Não
LIMPA TODOS OS SEGMENTOS (BLANK)
ACERTA PONTEIRO DO END. INDIRETO (FSR)
INCREMENTA PONTEIRO (INDICE_VARRE_DISPLAY)
DESLIGA TODOS OS DISPLAYS
CONSULTA TABELA P/ DISPLAY DE 7 SEGMENTOS
CONSULTA TABELA E ACIONA DISPLAY CORRETO
ATUALIZA PORT LIGADO AOS SEGMENTOS
LIMPA FLAG DA INTERRUPÇÃO DE TMR0
LIMPA FLAG DA INTERRUPÇÃO DE
TMR1
PARA CONTADOR DO TIMER 1
RECARREGA CONTADORES DO
TMR1
PASSOU 1seg. ?
Sim
DECREMENTA TIMER
Não
FIM DO TIMER ?
Sim
SALVA O STATUS DOS BOTÕES
194
INCREMENTA TIMER
TIMER LIGADO ?
Não
Não
RECARREGA TEMPORIZADOR P/ TURBO
FILTRO TERMINOU ?
Sim
Sim
TRATA BOTÃO UP
FIM DO TEMPORIZADOR
P/ TURBO ?
1
Não
Sim TIMER LIGADO ?
Não
Não
RECARREGA TEMPORIZADOR P/ TURBO
FILTRO TERMINOU ?
Sim
Sim
DECREMENTA TIMER
TRATA BOTÃO DOWN
FIM DO TEMPORIZADOR
P/ TURBO ?
1
Não
Sim
Curso Módulo 4 –Família 18F e MpLab C18 195
TRATA BOTÃO START / STOP
ACÃO JÁ FOI EXEC. ?
Não
Não
LIMPA FLAG DE TIMER LIGADO (LED)
FILTRO TERMINOU ?
Sim
Sim
TIMER LIGADO ?
1
Não
Sim
PARA CONTADOR TMR1
TIMER ZERADO ?
SETA FLAG DE TIMER LIGADO (LED)
CARREGA CONTADORES DO TIMER 1
CARREGA TEMPORIZADOR P/ CONTAGEM DE 1s.
LIGA CONTADOR DO TIMER 1
1
Não
Sim
196
INCREMENTA UNIDADE
RETURN
INCREMENTA TIMER
ZERA UNIDADE INCREMENTA DEZENA
UNIDADE= 10 ? Não
Sim
ZERA DEZENA INCREMENTA CENTENA
DEZENA = 10 ? Não
Sim
ZERA CENTENA INCREMENTA MILHAR
CENTENA = 10 ? Não
Sim
ZERA MILHAR
MILHAR = 10 ? Não
Sim
DECREMENTA UNIDADE
RETURN
DECREMENTA TIMER
UNIDADE = 9 DECREMENTA DEZENA
UNIDADE= 0xFF ? Não
Sim
DEZENA = 9 DECREMENTA CENTENA
DEZENA = 0xFF ? Não
Sim
CENTENA = 9 DECREMENTA MILHAR
CENTENA = 0xFF ? Não
Sim
MILHAR = 9
MILHAR = 0xFF ? Não
Sim
Curso Módulo 4 –Família 18F e MpLab C18 197
Código /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Programação em C18 - Recursos Básicos de programação * * Exemplo 5 * * * * CENTRO DE TREINAMENTO - MOSAICO ENGENHARIA * * * * TEL: (0XX11) 4992-8775 SITE: www.mosaico-eng.com.br * * E-MAIL: [email protected] * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * VERSÃO : 1.0 * * DATA : 10/08/2005 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Descrição geral * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ /* ESTE EXEMPLO FOI PREPARADO PARA DEMOSNTRAR O FUNCIONAMENTO DO TIMER DE 16 BITS DO PIC (TMR1) E DA VARREDURA DE DISPLAYS MAIS BARRA DE LEDS. CONSISTE NUM CONTADOR DE SEGUNDOS. DOIS BOTÕES FORAM UTILIZADOS PARA PROGRAMAR O TEMPO DA CONTAGEM. UM OUTRO BOTÃO FOI UTILIZADO PARA DISPARAR O CONTADOR. O TEMPORIZADOR CONSEGUE CONTAR ATÉ 9999 SEGUNDOS, DE FORMA QUE OS 4 DISPLAYS DE 7 SEGMENTOS FORAM NECESSÁRIOS. A CONTAGEM É REGRESSIVA. UMA BARRA DE LEDS INDICA QUE O TEMPORIZADOR ESTÁ OPERANDO. QUANDO O SISTEMA CHEGA A 0000 A BARRA DE LEDS É DESLIGADA AUTOMATICAMENTE. */ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * DEFINIÇÃO PIC * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ #include <p18F452.h> // Register definitions /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * INCLUDES DAS FUNÇÕES DE PERIFÉRICOS DO PIC * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ #include <pwm.h> //PWM library functions #include <adc.h> //ADC library functions #include <timers.h> //Timer library functions #include <delays.h> //Delay library functions #include <i2c.h> //I2C library functions #include <stdlib.h> //Library functions #include <usart.h> //USART library functions /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Configurações para gravação * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ #pragma config OSC = XT #pragma config WDT = ON #pragma config WDTPS = 128 #pragma config LVP = OFF /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Constantes internas * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ //A definição de constantes facilita a programação e a manutenção. #define t_filtro 250 // tamanho do filtro #define turbo_tecla 60 #define delta_timer1 (65536 - 62500) /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Definição e inicialização das variáveis Globais * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ //Neste bloco estão definidas as variáveis globais do programa. //Este programa não utiliza nenhuma variável de usuário unsigned char display = 0 ; // atualiza os displays unsigned char contador = 2; // contador de interrupções de timer1
198
unsigned char filtro = t_filtro; // inicia filtro dos botões unsigned char filtro1 = t_filtro; // inicia filtro dos botões unsigned char filtro2= t_filtro; // inicia filtro dos botões unsigned char filtro3 = t_filtro; // inicia filtro dos botões unsigned char filtro4 = t_filtro; // inicia filtro dos botões unsigned char turbo = 1; // inicia turbo das teclas unsigned char unidade = 9; // variável unidade do timer de 1 seg unsigned char dezena = 9; // variável dezena do timer de 1 seg unsigned char centena = 9; // variável centena do timer de 1 seg unsigned char milhar = 9; // variável milhar do timer de 1 seg unsigned char botoes = 0; // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // * ROTINA DE CONVERSÃO BINÁRIO -> DISPLAY * // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // ESTA ROTINA IRÁ RETORNAR EM W, O SIMBOLO CORRETO QUE DEVE SER // MOSTRADO NO DISPLAY PARA CADA VALOR DE INTENSIDADE. O RETORNO JÁ ESTÁ // FORMATADO PARA AS CONDIÇÕES DE LIGAÇÃO DO DISPLAY AO PORTD. // a // ********** // * * // f * * b // * g * // ********** // * * // e * * c // * d * // ********** *. const rom unsigned char converte[] = // .GFEDCBA POSIÇÃO CORRETA DOS SEGMENTOS 0b00111111, // 00 - RETORNA SÍMBOLO CORRETO 0 0b00000110, // 01 - RETORNA SÍMBOLO CORRETO 1 0b01011011, // 02 - RETORNA SÍMBOLO CORRETO 2 0b01001111, // 03 - RETORNA SÍMBOLO CORRETO 3 0b01100110, // 04 - RETORNA SÍMBOLO CORRETO 4 0b01101101, // 05 - RETORNA SÍMBOLO CORRETO 5 0b01111101, // 06 - RETORNA SÍMBOLO CORRETO 6 0b00000111, // 07 - RETORNA SÍMBOLO CORRETO 7 0b01111111, // 08 - RETORNA SÍMBOLO CORRETO 8 0b01101111, // 09 - RETORNA SÍMBOLO CORRETO 9 0b00000000; // BLANK /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Declaração dos flags de software * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ //A definição de flags ajuda na programação e economiza memória RAM. //Este programa não utiliza nenhum flag de usuário struct unsigned BIT0:1; unsigned BIT1:1; unsigned BIT2:1; unsigned BIT3:1; unsigned BIT4:1; unsigned BIT5:1; unsigned BIT6:1; unsigned BIT7:1; FLAGSbits; //ARMAZENA OS FLAGS DE CONTROLE #define estado_timer FLAGSbits.BIT0 //STATUS DO TIMER 1 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Definição e inicialização dos port's * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * PROTOTIPAGEM DE FUNÇÕES * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ void TRATA_HIGH_INT(void);
Curso Módulo 4 –Família 18F e MpLab C18 199
void TRATA_INT_TIMER0(void); void TRATA_INT_TIMER1(void); void decrementa_timer(void); void incrementa_timer(void); /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * ENTRADAS * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ // As entradas devem ser associadas a nomes para facilitar a programação e //futuras alterações do hardware. /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * SAÍDAS * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ // AS SAÍDAS DEVEM SER ASSOCIADAS A NOMES PARA FACILITAR A PROGRAMAÇÃO E //FUTURAS ALTERAÇÕES DO HARDWARE. #define C_LEDS PORTAbits.RA4 //PINO PARA ATIVAR GRUPO DE 8 LEDS //1 -> LEDS ATIVADOS //0 -> LEDS DESATIVADOS #define LINHA_4 PORTBbits.RB4 //PINO PARA ATIVAR LINHA 4 DO TECLADO //MATRICIAL //1 -> LINHA ATIVADA //0 -> LINHA DESATIVADA #define disp0 PORTBbits.RB7 // seleção do display unidade (0) #define disp1 PORTBbits.RB6 // seleção do display dezena (1) #define disp2 PORTBbits.RB5 // seleção do display centena (2) #define disp3 PORTBbits.RB4 // seleção do display milhar (3) /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Função de decremento do Timer * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ void decrementa_timer(void) unidade --; if (unidade == 0xff) unidade = 9; dezena --; if (dezena == 0xff) dezena = 9; centena --; if (centena == 0xff) centena = 9; milhar --; if (milhar == 0xff) milhar = 9; /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Função de incremento do Timer * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ void incrementa_timer(void) unidade ++; if (unidade == 10) unidade = 0; dezena ++; if (dezena == 10) dezena = 0;
200
centena ++; if (centena == 10) centena = 0; milhar ++; if (milhar == 10) milhar = 0; /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Função de tratamento de interrupção de Timer1 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ // Esta interrupcao ocorrera a cada 0,5 segundos, a rotina de tratamento e executada a // cada 2 interrupcoes. void TRATA_INT_TIMER1(void) WriteTimer1(delta_timer1); // carraga timer1 PIR1bits.TMR1IF = 0; // limpa flag de interrupção contador --; // decrementa contador de interrupções if (contador == 0) WriteTimer1(delta_timer1); // carraga timer1 contador = 2; // carrega contador de int decrementa_timer(); if ((unidade == 0)&&(dezena == 0)&&(centena == 0)&&(milhar == 0))// timer está zerado? estado_timer = 0; // sim, apaga o led e CloseTimer1(); /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Rotina de Tratamento de interrupção de TMR0 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ // Esta interrupção ocorrerá a cada 256us. void TRATA_INT_TIMER0(void) INTCONbits.TMR0IF = 0; //LIMPA FLAG DE INTERRUPÇÃO switch(display) // início da varredura dos display´s case 0: display++; // incrementa a variável de varredura C_LEDS = 0; // liga o display 3 PORTD = converte[unidade]; // atualiza o portd disp0 = 1; // liga o display 0 Delay1TCY(); botoes = PORTB & 0b00001111;//FOTOGRAFA O ESTADO DOS BOTÕES break; // sai case 1: display++; // incrementa a variável de varredura disp0 = 0; // desliga o display 0 PORTD = converte[dezena]; // atualiza o portd disp1 = 1; // liga o display 1 break; // sai case 2: display++; // incrementa a variável de varredura disp1 = 0; // desliga o display 1 PORTD = converte[centena]; // atualiza o portd disp2 = 1; // liga o display 2 break; // sai
Curso Módulo 4 –Família 18F e MpLab C18 201
case 3: display++; // inicia a variável de varredura disp2 = 0; // desliga o display 2 PORTD = converte[milhar]; // atualiza o portd disp3 = 1; // liga o display 3 break; // sai case 4: display = 0; // inicia a variável de varredura disp3 = 0; // desliga o display 2 if(estado_timer) PORTD = 0xff; // atualiza o portd else PORTD = 0; // atualiza o portd C_LEDS = 1; // liga o display 3 break; // sai /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Função Principal * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ void main () PORTA = 0x00; //Clear PORTA PORTB = 0x00; //Clear PORTB PORTC = 0x00; //Clear PORTC PORTD = 0x00; //Clear PORTD PORTE = 0x00; //Clear PORTE LATA = 0x00; //Clear PORTA LATB = 0x00; //Clear PORTB LATC = 0x00; //Clear PORTC LATD = 0x00; //Clear PORTD LATE = 0x00; //Clear PORTE TRISA = 0b00101111; //CONFIG DIREÇÃO DOS PINOS PORTA TRISB = 0b00001111; //CONFIG DIREÇÃO DOS PINOS PORTB TRISC = 0b00000000; //CONFIG DIREÇÃO DOS PINOS PORTC TRISD = 0b00000000; //CONFIG DIREÇÃO DOS PINOS PORTD TRISE = 0b00000000; //CONFIG DIREÇÃO DOS PINOS PORTE PORTA = 0x00; //Clear PORTA PORTB = 0x00; //Clear PORTB PORTC = 0x00; //Clear PORTC PORTD = 0x00; //Clear PORTD PORTE = 0x00; //Clear PORTE ADCON1 = 0b00000111; //DESLIGA CONVERSORES A/D /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Inicialização do Sistema * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ OpenTimer0(TIMER_INT_ON & T0_8BIT & T0_SOURCE_INT & T0_PS_1_4); OpenTimer1(TIMER_INT_OFF & T1_SOURCE_INT & T1_8BIT_RW & T1_PS_1_8); RCON = 0X00; CloseTimer1(); // desliga tratamento de interrupção de timer1 INTCONbits.GIEH = 1; /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Rotina Principal * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Loop principal * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
202
loop: while(1) ClrWdt(); /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Tratamento do Botão 1 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ if ((botoes & 0b00000001)==1) // testa botão 1 // botão 1 está pressionado ? goto trata_up; // desvia para a rotina de decremento do timer /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Tratamento do Botão 2 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ if ((botoes & 0b00000010)==2) // testa botão 2 // botão 2 está pressionado ? goto trata_dowm; // desvia para a rotina de decremento do timer /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Tratamento do Botão 3 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ if ((botoes & 0b00000100)==4) // testa botão 3 // botão 3 está pressionado ? goto trata_start_stop;// desvia para a rotina de inicio e parada do timer filtro = t_filtro; turbo = 1; trata_up: if (estado_timer == 0) // o timer está parado? filtro --; // decrementa o filtro if (filtro == 0) // fim do filtro do botão? turbo --; // decrementa o turbo da tecla if (turbo == 0) // sim, fim do turbo do botão ? turbo = turbo_tecla; // carrega o turbo incrementa_timer(); // incrementa o timer goto loop; //* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * trata_dowm: if (estado_timer == 0) // o timer está parado? filtro --; // decrementa o filtro if (filtro == 0) // fim do filtro do botão? turbo --; // decrementa o turbo da tecla if (turbo == 0) // sim, fim do turbo do botão ? turbo = turbo_tecla; // carrega o turbo decrementa_timer(); // decrementa o timer goto loop; //* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * trata_start_stop: if (filtro != 0) // o timer está parado?
Curso Módulo 4 –Família 18F e MpLab C18 203
filtro --; // decrementa o filtro if (filtro == 0) // fim do filtro do botão? if (estado_timer != 0) // sim, o timer está ligado ? estado_timer = 0; // indica timer parado CloseTimer1(); // desliga o tratamento de interrupção de timer1 else if ((unidade!=0)||(dezena!=0)||(centena!=0)||(milhar!=0))// timer está zerado? estado_timer = 1; // não, indica que o timer está contando contador = 2; // carrega contador auxiliar WriteTimer1(delta_timer1); // carraga timer1 INTCONbits.TMR0IF = 0; // limpa flag de int tmr1 OpenTimer1(TIMER_INT_OFF & T1_SOURCE_INT & T1_8BIT_RW & T1_PS_1_8); // liga o tratamento de interrupção de timer1 goto loop; // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // * ROTINA DE TRATAMENTO DE INT DE ALTA PRIORIDADE * // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * #pragma code VETOR_HIGH_PRIORITY = 0x0008 void HIGH_int (void) _asm goto TRATA_HIGH_INT _endasm #pragma code #pragma interrupt TRATA_HIGH_INT void TRATA_HIGH_INT(void) if(INTCONbits.TMR0IF) TRATA_INT_TIMER0(); if(PIR1bits.TMR1IF) TRATA_INT_TIMER1();
204
Dicas e Comentários
Observar que nesta experiência, ao entrar no tratamento das interrupções, a operação de salvar contexto é maior que nas experiências anteriores. Isto por que agora salva-se também os valores de FSR e PCLATH pois os mesmos podem ser alterados dentro da interrupção.
Exercícios Propostos
1. Implemente o quarto botão, para resetar o temporizador (voltar a zero). 2. Implemente uma segunda velocidade para os botões de incremento e decremento, de forma que facilite o
ajuste de valores maiores. 3. Em vez de fazer um timer somente de segundos, utilize os dois dígitos da esquerda para mostrar o tempo
em minutos e os da direita para mostrar o tempo em segundos. O ponto do display da centena pode ser usado para marcar a separação. Não se esqueça que agora os displays da direita devem contar somente de 0 a 59 e não mais de 0 a 99.
Curso Módulo 4 –Família 18F e MpLab C18 205
Experiência 6 – Display de cristal líquido LCD
Objetivo
O objetivo desta experiência é o aprendizado da utilização de display de cristal líquido.
Descrição
Esta experiência foi elaborada para explicar o funcionamento do display LCD e o exemplo de software proposto é bastante reduzido. Simplesmente utilizou-se o LCD para informar ao usuário qual tecla está pressionada.
Para isso elaborou-se uma rotina chamada ESCREVE que envia a informação passada pelo Work ao display. Esta rotina pode ser utilizada tanto para enviar comandos quanto dados. Foi criada também, a rotina de inicialização do LCD. Esta rotina configura o sistema para comunicação com 8 vias, 2 linhas, sem cursor visível e com movimento automático do cursor para a direita. Além disso, ela já limpa a tela e posiciona o cursor na primeira linha, primeiro caractere da esquerda.
Para cada botão pressionado, posicionou-se o cursor em um local diferente da tela e escreveu-se o número do botão em questão. Após a liberação, uma tela padrão é visualizada.
206
Esquema Elétrico
Curso Módulo 4 –Família 18F e MpLab C18 207
Fluxograma
INICIALIZA VARIÁVEIS
ENVIA COMANDO 0x30 P/ DISPLAY
VETOR DE RESET PULA P/ INICIO DO
PROGRAMA
CONFIGURAÇÕES INICIAS PORTAS, TIMERS,
INTERRUPÇÕES, OPTION, ADS.
1
INICIO
DELAY DE 4ms
ENVIA COMANDO 0x38 P/ DISPLAY
ENVIA COMANDO 0x30 P/ DISPLAY
INICIALIZAÇÃO COM 4 VIAS DE DADOS
ENVIA COMANDO 0x30 P/ DISPLAY
INICIALIZA DISPLAY P/ 8 VIAS DE DADOS
ENVIA COMANDO 0x01 P/ DISPLAY
DELAY DE 1ms
ENVIA COMANDO 0x06 P/ DISPLAY
ENVIA COMANDO 0x0C P/ DISPLAY DISPLAY S/ CURSOR
DESLOCAR CURSOR À ESQUERDA
INICIALIZAÇÃO DISPLAY
LIMPA DISPLAY
208
Não
LIMPA WDT
1
RECARREGA FILTRO
BOTÃO 0 PRESS.?
Não
Não
TRATA BOTÃO 0
TRATA BOTÃO 1
Sim
Sim
Sim
BOTÃO 3 PRESS.?
BOTÃO 2 PRESS.?
BOTÃO 1 PRESS.?
TRATA BOTÃO 3
TRATA BOTÃO 2
Sim
Não
MOSTRAR TELA
PRINCIPAL.?
MOSTRA TELA PRINCIPAL
Não
Sim
HABILITA LINHA 4
DESABILITA LINHA 4
Curso Módulo 4 –Família 18F e MpLab C18 209
TRATA BOTÃO 1
ACÃO JÁ FOI EXEC. ?
Não
POSICIONA CURSOR NA LINHA 0 COLUNA 8
FILTRO TERMINOU ?
Sim
1
Não
Sim
SELECIONA DISPLAY PARA DADOS
ENVIA COMANDO P/ LIMPAR DISPLAY
ESCREVE TECLA 1 NO DISPLAY
LIMPA BARRAMENTO DE DADOS
SETA FLAG PARA MOSTRAR TELA
PRINCIPAL
TRATA BOTÃO 0
ACÃO JÁ FOI EXEC. ?
Não
POSICIONA CURSOR NA LINHA 0 COLUNA 0
FILTRO TERMINOU ?
Sim
1
Não
Sim
SELECIONA DISPLAY PARA DADOS
ENVIA COMANDO P/ LIMPAR DISPLAY
ESCREVE TECLA 0 NO DISPLAY
LIMPA BARRAMENTO DE DADOS
SETA FLAG PARA MOSTRAR TELA
PRINCIPAL
DESABILITA TECLADO MATRICIAL
DESABILITA TECLADO MATRICIAL
210
TRATA BOTÃO 3
ACÃO JÁ FOI EXEC. ?
Não
POSICIONA CURSOR NA LINHA 1 COLUNA 8
FILTRO TERMINOU ?
Sim
1
Não
Sim
SELECIONA DISPLAY PARA DADOS
ENVIA COMANDO P/ LIMPAR DISPLAY
ESCREVE TECLA 3 NO DISPLAY
LIMPA BARRAMENTO DE DADOS
SETA FLAG PARA MOSTRAR TELA
PRINCIPAL
TRATA BOTÃO 2
ACÃO JÁ FOI EXEC. ?
Não
POSICIONA CURSOR NA LINHA 1 COLUNA 0
FILTRO TERMINOU ?
Sim
1
Não
Sim
SELECIONA DISPLAY PARA DADOS
ENVIA COMANDO P/ LIMPAR DISPLAY
ESCREVE TECLA 2 NO DISPLAY
LIMPA BARRAMENTO DE DADOS
SETA FLAG PARA MOSTRAR TELA
PRINCIPAL
DESABILITA TECLADO MATRICIAL
DESABILITA TECLADO MATRICIAL
Curso Módulo 4 –Família 18F e MpLab C18 211
LIMPA FLAG DE MOSTRAR TELA
PRINCIPAL
RETURN
MOSTRA TELA PRINCIPAL
ENVIA COMANDO P/ LIMPAR DISPLAY
SELECIONA DISPLAY PARA DADOS
ESCREVE “Sistema Didatico”
SELECIONA DISPLAY PARA COMANDOS
POSICIONA CURSOR NA LINHA 1 COLUNA 6
ESCREVE “MCMASTER”
SELECIONA DISPLAY PARA DADOS
LIMPA BARRAMENTO DE DADOS
CARREGA ARGUMENTO PASSADO PELO Work NO BARRAMENTO DE DADOS
DO DISPLAY
ESCREVE
ENVIA PULSO DE ENABLE AO LCD
DELAY DE 1ms
RETURN
[3us]
Não
Sim
Não
1ms
Sim
RETURN
CARREGA ARGUMENTO PASSADO PELO Work EM
TEMPO1
NOP [1us]
INICIALIZA TEMPO0 COM 250d
FIM DO TEMPO1 ?
DELAY_MS
FIM DO TEMPO0 ?
212
Código /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Programação em C18 - Recursos Básicos de programação * * Exemplo 6 * * * * CENTRO DE TREINAMENTO - MOSAICO ENGENHARIA * * * * TEL: (0XX11) 4992-8775 SITE: www.mosaico-eng.com.br * * E-MAIL: [email protected] * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * VERSÃO : 1.0 * * DATA : 23/02/2005 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Descrição geral * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ /* ESTE EXEMPLO FOI ELABORADO PARA EXPLICAR O FUNCIONAMENTO DO MÓDULO DE LCD. FOI CRIADA UMA ROTINA PARA ESCREVER COMANDOS OU CACACTRES NO LCD. EXISTE TAMBÉM UMA ROTINA DE INICIALIZAÇÃO NECESSÁRIA PARA A CORRETA CONFIGURAÇÃO DO LCD. OS BOTÕES CONTINUAM SENDO MONITORADOS. UMA MENSAGEM É ESCRITA NO LCD PARA CADA UM DOS BOTÕES, QUANDO O MESMO É PRESSIONADO. */ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * DEFINIÇÃO PIC * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ #include <p18F452.h> // Register definitions /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * INCLUDES DAS FUNÇÕES DE PERIFÉRICOS DO PIC * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ #include <pwm.h> //PWM library functions #include <adc.h> //ADC library functions #include <timers.h> //Timer library functions #include <delays.h> //Delay library functions #include <i2c.h> //I2C library functions #include <stdlib.h> //Library functions #include <usart.h> //USART library functions /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Configurações para gravação * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ #pragma config OSC = XT #pragma config WDT = ON #pragma config LVP = OFF /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Definição e inicialização das variáveis Globais * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ //Neste bloco estão definidas as variáveis globais do programa. //Este programa não utiliza nenhuma variável de usuário unsigned char FILTRO11; //FILTRAGEM 1 PARA O BOTÃO 1 unsigned char FILTRO12; //FILTRAGEM 2 PARA O BOTÃO 1 unsigned char FILTRO21; //FILTRAGEM 1 PARA O BOTÃO 2 unsigned char FILTRO22; //FILTRAGEM 2 PARA O BOTÃO 2 unsigned char FILTRO31; //FILTRAGEM 1 PARA O BOTÃO 3 unsigned char FILTRO32; //FILTRAGEM 2 PARA O BOTÃO 3 unsigned char FILTRO41; //FILTRAGEM 1 PARA O BOTÃO 4 unsigned char FILTRO42; //FILTRAGEM 2 PARA O BOTÃO 4 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Constantes internas * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ //A definição de constantes facilita a programação e a manutenção. #define T_FILTRO 25
Curso Módulo 4 –Família 18F e MpLab C18 213
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Declaração dos flags de software * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ //A definição de flags ajuda na programação e economiza memória RAM. //Este programa não utiliza nenhum flag de usuário struct unsigned BIT0:1; unsigned BIT1:1; unsigned BIT2:1; unsigned BIT3:1; unsigned BIT4:1; unsigned BIT5:1; unsigned BIT6:1; unsigned BIT7:1; FLAGSbits; //ARMAZENA OS FLAGS DE CONTROLE #define ST_BT1 FLAGSbits.BIT0 //STATUS DO BOTÃO 1 #define ST_BT2 FLAGSbits.BIT1 //STATUS DO BOTÃO 2 #define ST_BT3 FLAGSbits.BIT2 //STATUS DO BOTÃO 3 #define ST_BT4 FLAGSbits.BIT3 //STATUS DO BOTÃO 4 #define ST_NO_BOTS FLAGSbits.BIT4 //STATUS DOS BOTÕES LIBERADOS /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Definição e inicialização dos port's * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * PROTOTIPAGEM DE FUNÇÕES * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ void comando_lcd(unsigned char caracter); void escreve_lcd(unsigned char caracter); void limpa_lcd(void); void inicializa_lcd(void); void tela_principal(void); /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * ENTRADAS * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ // As entradas devem ser associadas a nomes para facilitar a programação e //futuras alterações do hardware. #define BT1 PORTBbits.RB0 //PORTA DO BOTÃO //1 -> PRESSIONADO //0 -> LIBERADO #define BT2 PORTBbits.RB1 //PORTA DO BOTÃO //1 -> PRESSIONADO //0 -> LIBERADO #define BT3 PORTBbits.RB2 //PORTA DO BOTÃO //1 -> PRESSIONADO //0 -> LIBERADO #define BT4 PORTBbits.RB3 //PORTA DO BOTÃO //1 -> PRESSIONADO //0 -> LIBERADO /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * SAÍDAS * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ // AS SAÍDAS DEVEM SER ASSOCIADAS A NOMES PARA FACILITAR A PROGRAMAÇÃO E //FUTURAS ALTERAÇÕES DO HARDWARE. #define LINHA_4 PORTBbits.RB7 //PINO PARA ATIVAR LINHA 4 DO TECLADO //MATRICIAL //1 -> LINHA ATIVADA //0 -> LINHA
214
DESATIVADA #define rs PORTEbits.RE0 // via do lcd que sinaliza recepção de dados ou comando #define enable PORTEbits.RE1 // enable do lcd /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Rotina que envia um COMANDO para o LCD * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ void comando_lcd(unsigned char caracter) rs = 0; // seleciona o envio de um comando PORTD = caracter; // carrega o PORTD com o caracter enable = 1 ; // gera pulso no enable Delay10TCYx(1); // espera 3 microsegundos enable = 0; // desce o pino de enable Delay10TCYx(4); // espera mínimo 40 microsegundos /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Rotina que envia um DADO a ser escrito no LCD * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ void escreve_lcd(unsigned char caracter) rs = 1; // seleciona o envio de um comando PORTD = caracter; // carrega o PORTD com o caracter enable = 1; // gera pulso no enable Delay10TCYx(1); // espera 10 microsegundos enable = 0; // desce o pino de enable Delay10TCYx(4); // espera mínimo 40 microsegundos /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Função para limpar o LCD * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ void limpa_lcd(void) comando_lcd(0x01); // limpa lcd Delay1KTCYx(2); /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Inicialização do Display de LCD * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ void inicializa_lcd(void) comando_lcd(0x30); // envia comando para inicializar display Delay1KTCYx(4); // espera 4 milisengundos comando_lcd(0x30); // envia comando para inicializar display Delay10TCYx(10); // espera 100 microsengundos comando_lcd(0x30); // envia comando para inicializar display comando_lcd(0x38); // liga o display, sem cursor e sem blink limpa_lcd(); // limpa lcd comando_lcd(0x0c); // display sem cursor comando_lcd(0x06); // desloca cursor para a direita /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
Curso Módulo 4 –Família 18F e MpLab C18 215
* Tela Principal * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ void tela_principal(void) limpa_lcd(); // limpa lcd comando_lcd(0x80); // posiciona o cursor na linha 0, coluna 0 escreve_lcd ('S'); // imprime mensagem no lcd escreve_lcd ('i'); escreve_lcd ('s'); escreve_lcd ('t'); escreve_lcd ('e'); escreve_lcd ('m'); escreve_lcd ('a'); escreve_lcd (' '); escreve_lcd ('D'); escreve_lcd ('i'); escreve_lcd ('d'); escreve_lcd ('a'); escreve_lcd ('t'); escreve_lcd ('i'); escreve_lcd ('c'); escreve_lcd ('o'); comando_lcd(0xC4); // posiciona o cursor na linha 1, coluna 4 escreve_lcd ('M'); // imprime mensagem no lcd escreve_lcd ('C'); escreve_lcd ('M'); escreve_lcd ('A'); escreve_lcd ('S'); escreve_lcd ('T'); escreve_lcd ('E'); escreve_lcd ('R'); /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Tela Tecla 1 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ void tela_tecla_1(void) limpa_lcd(); // limpa lcd comando_lcd(0x80); // posiciona o cursor na linha 0, coluna 0 escreve_lcd ('T'); // imprime mensagem no lcd escreve_lcd ('E'); escreve_lcd ('C'); escreve_lcd ('L'); escreve_lcd ('A'); escreve_lcd (' '); escreve_lcd ('1'); /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Tela Tecla 2 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ void tela_tecla_2(void) limpa_lcd(); // limpa lcd comando_lcd(0x89); // posiciona o cursor na linha 0, coluna 0 escreve_lcd ('T'); // imprime mensagem no lcd escreve_lcd ('E'); escreve_lcd ('C'); escreve_lcd ('L'); escreve_lcd ('A'); escreve_lcd (' '); escreve_lcd ('2');
216
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Tela Tecla 3 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ void tela_tecla_3(void) limpa_lcd(); // limpa lcd comando_lcd(0xC0); // posiciona o cursor na linha 0, coluna 0 escreve_lcd ('T'); // imprime mensagem no lcd escreve_lcd ('E'); escreve_lcd ('C'); escreve_lcd ('L'); escreve_lcd ('A'); escreve_lcd (' '); escreve_lcd ('3'); /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Tela Tecla 4 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ void tela_tecla_4(void) limpa_lcd(); // limpa lcd comando_lcd(0xC9); // posiciona o cursor na linha 0, coluna 0 escreve_lcd ('T'); // imprime mensagem no lcd escreve_lcd ('E'); escreve_lcd ('C'); escreve_lcd ('L'); escreve_lcd ('A'); escreve_lcd (' '); escreve_lcd ('4'); /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Função Principal * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ void main () PORTA = 0x00; //Clear PORTA PORTB = 0x00; //Clear PORTB PORTC = 0x00; //Clear PORTC PORTD = 0x00; //Clear PORTD PORTE = 0x00; //Clear PORTE LATA = 0x00; //Clear PORTA LATB = 0x00; //Clear PORTB LATC = 0x00; //Clear PORTC LATD = 0x00; //Clear PORTD LATE = 0x00; //Clear PORTE TRISA = 0b00101111; //CONFIG DIREÇÃO DOS PINOS PORTA0 TRISB = 0b00001111; //CONFIG DIREÇÃO DOS PINOS PORTB TRISC = 0b10011001; //CONFIG DIREÇÃO DOS PINOS PORTC TRISD = 0b00000000; //CONFIG DIREÇÃO DOS PINOS PORTD TRISE = 0b00000000; //CONFIG DIREÇÃO DOS PINOS PORTE ADCON1 = 0b00000111; //DESLIGA CONVERSORES A/D /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Inicialização do Sistema * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ RCON = 0X00; LINHA_4 = 1; //ATIVA LINHA 4 DO TECLADO MATRICIAL
Curso Módulo 4 –Família 18F e MpLab C18 217
inicializa_lcd(); // configura o lcd /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Rotina Principal * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ while(1) ClrWdt(); FILTRO11 = 0; //BT1 FILTRO12 = T_FILTRO; //BT1 FILTRO21 = 0; //BT2 FILTRO22 = T_FILTRO; //BT2 FILTRO31 = 0; //BT3 FILTRO32 = T_FILTRO; //BT3 FILTRO41 = 0; //BT4 FILTRO42 = T_FILTRO; //BT4 if(!ST_NO_BOTS) tela_principal(); ST_NO_BOTS = 1; while(BT1) ClrWdt(); if (--FILTRO11 == 0) FILTRO12--; if (FILTRO12 == 0) if (!ST_BT1) tela_tecla_1(); ST_BT1 = 1; ST_NO_BOTS = 0; ST_BT1 = 0; // BOTÃO LIBERADO, LIMPA O FLAG while(BT2) ClrWdt(); if (--FILTRO21 == 0) FILTRO22--; if (FILTRO22 == 0) if (!ST_BT2) tela_tecla_2(); ST_BT2 = 1; ST_NO_BOTS = 0; ST_BT2 = 0; // BOTÃO LIBERADO, LIMPA O FLAG while(BT3) ClrWdt(); if (--FILTRO31 == 0)
218
FILTRO32--; if (FILTRO32 == 0) if (!ST_BT3) tela_tecla_3(); ST_BT3 = 1; ST_NO_BOTS = 0; ST_BT3 = 0; // BOTÃO LIBERADO, LIMPA O FLAG while(BT4) ClrWdt(); if (--FILTRO41 == 0) FILTRO42--; if (FILTRO42 == 0) if (!ST_BT4) tela_tecla_4(); ST_BT4 = 1; ST_NO_BOTS = 0; ST_BT4 = 0; // BOTÃO LIBERADO, LIMPA O FLAG // FIM LAÇO PRINCIPAL
Curso Módulo 4 –Família 18F e MpLab C18 219
Dicas e Comentários
Apesar da estrutura do sistema ficar muito simples com a implementação da rotina ESCREVE, nunca se deve esquecer de confirmar o estado da saída RS (define comando ou dado) antes de utilizá-la.
Notar que para enviar um caractere ao LCD deve-se utilizar o código ASCII do caractere. Apesar do sistema MCMASTER possuir ligação com o módulo de LCD através de 8 vias de dados é possível
utilizá-lo para testar e implementar a comunicação com 4 vias. Basta modificar a rotina de inicialização e a de escrita de um byte.
Exercícios Propostos
1. Altere a comunicação para 4 vias. 2. Mantenha a tela principal disponível somente quando o sistema é ligado. Após alguns segundos, mostre
uma tela com o nome das quatro teclas e indique a tecla pressionada através de um caractere de seta (←) ou outro qualquer.
220
Experiência 7 – Conversor A/D
Objetivo
Nesta experiência será estudado o módulo de conversão A/D interno do PIC16F877A ou do PIC18F452.
Descrição
Este exemplo foi elaborado para explicar o funcionamento do módulo de conversão analógico digital interno do PIC16F877A ou do PIC18F452. É convertido o valor analógico presente no pino RA0 do microcontrolador, sendo que este valor pode ser alterado através do potenciômetro presente na placa do sistema MCMASTER.
A conversão é feita diretamente no loop principal, sem a utilização de nenhuma interrupção, nem para checar o término da conversão nem para definir uma freqüência de amostragem. Desta forma, a conversão será feita uma após a outra, na freqüência definida pelo período do loop principal.
Uma vez terminada a conversão, descarta-se os 2 bits menos significativos e considera-se somente o resultado armazenado em ADRESH. Com isso já se está executando uma espécie de filtragem, evitando assim que o valor final fique oscilando.
Aplica-se então uma regra de 3 para converter o valor do A/D para a unidade desejada: Volts. Considerando-se que quando o A/D resulta em 0 (zero) a entrada possui 0,0V, e quando o A/D resulta em 255 a entrada é equivalente a 5,0V, aplica-se a regra de 3 é mostra-se o valor da tensão, já em Volts, no LCD.
Para a execução da regra de 3 foram utilizadas rotinas de multiplicação de 8x8 e divisão de 16x16 retiradas de Aplication Notes da própria Microchip.
Curso Módulo 4 –Família 18F e MpLab C18 221
Esquema Elétrico
222
Fluxograma
INÍCIO
INICIALIZA DISPLAY
VETOR DE RESET PULA P/ INÍCIO DO
PROGRAMA
CONFIGURAÇÕES INICIAIS PORTAS, TIMERS,
INTERRUPÇÕES, OPTION.
1
PREPARA TELA PRINCIPAL“ A/D Int. (RA0) ”
“ Volts ”
CONFIGURA REGISTRADOR ADCON1 RA0, RA1 e RA3 COMO ENTRADAS ANALÓGICAS
RA2, RA4 E RA5 COMO I/Os DIGITAIS PORTE COMO I/O DIGITAL
8 BITS EM ADRESH E 2 BITS EM ADRESL Vref+ = VDD (+5V) Vref- = GND ( 0V)
CONFIGURA REGISTRADOR ADCON0 MÓDULO A/D LIGADO FREQÜÊNCIA - Fosc/8
MUX SELECIONADO P/ CANAL1 (RA1)
Curso Módulo 4 –Família 18F e MpLab C18 223
Sim
Não
1
LIMPA WDT
INICIA CONVERSÃO (ADCON0,G0=1)
FIM CONVERSÃO?
MULTIPLICA VALOR DA CONVERSÃO POR 50d
DIVIDE RESULTADO POR 255d
AJUSTE DECIMAL
Aguarda que o bit GO do registrador
ADCON0 seja 0
POSICIONA CURSOR DO LCD
CONVERTE DEZENA EM ASCII
ENVIA “,” AO DISPLAY
ENVIA DEZENA AO DISPLAY
ENVIA UNIDADE AO DISPLAY
CONVERTE UNIDADE EM ASCII
FUNDO DE ESCALA = 5,0V ESTÃO SENDO
CONSIDERADOS APENAS OS 8 BITS MAIS
SIGNIFICATIVOS DA CONVERSÃO A/D
(ADRESH)
224
AJUSTE DECIMAL
AUX = 0 ?
Não
INCREMENTA UNIDADE
UNIDADE = 10d ?
Sim
Não
Sim
ZERA UNIDADE INCREMENTA DEZENA
DECREMENTA AUXILIAR
SALVA ARGUMENTO EM AUXZERA UNIDADE ZERA DEZENA
Testa fim da conversão
Sim
Não
RETURN
AUX = 0?
Testa se o valor a ser convertido é igual a zero
Curso Módulo 4 –Família 18F e MpLab C18 225
Código /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Programação em C - Recursos Avançados de programação * * Exemplo 7 * * * * CENTRO DE TREINAMENTO - MOSAICO ENGENHARIA * * * * TEL: (0XX11) 4992-8775 SITE: www.mosaico-eng.com.br * * E-MAIL: [email protected] * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * VERSÃO : 1.0 * * DATA : 10/08/2005 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Descrição geral * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ // Este exemplo foi elaborado para explicar o funcionamento do módulo de // conversão analógico digital interno do PIC. É convertido o valor analógico // presente no pino RA2 do microcontrolador, sendo que este valor pode ser // alterado através do potenciômetro P2 da placa MCLAB2. O valor da conversão // A/D é ajustado numa escala de 0 à 5V e mostrado no LCD. /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * DEFINIÇÃO DAS VARIÁVEIS INTERNAS DO PIC * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ // O arquivo de definições do pic utilizado deve ser referenciado para que //os nomes definidos pela Microchip possam ser utilizados, sem a necessidade //de redigitação. #include <p18F452.h> // Register definitions /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Configurações para gravação * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ #pragma config OSC = XT #pragma config WDT = ON #pragma config WDTPS = 128 #pragma config LVP = OFF #pragma config PWRT = ON #pragma config BORV = 42 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * INCLUDES DAS FUNÇÕES DE PERIFÉRICOS DO PIC * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ #include <pwm.h> //PWM library functions #include <adc.h> //ADC library functions #include <timers.h> //Timer library functions #include <delays.h> //Delay library functions #include <i2c.h> //I2C library functions #include <stdlib.h> //Library functions #include <usart.h> //USART library functions /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * PROTOTIPAGEM DE FUNÇÕES * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ void comando_lcd(unsigned char caracter); void escreve_lcd(unsigned char caracter); void limpa_lcd(void); void inicializa_lcd(void); void tela_principal(void); /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Constantes internas * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ //A definição de constantes facilita a programação e a manutenção. /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Definição e inicialização das variáveis *
226
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ //Neste bloco estão definidas as variáveis globais do programa. unsigned int conversao = 0; // armazena o resultado da conversão AD unsigned char conversao1 = 0; // armazena o resultado da conversão AD unsigned char unidade; unsigned char dezena; unsigned char centena; /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Declaração dos flags de software * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ //A definição de flags ajuda na programação e economiza memória RAM. /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * ENTRADAS * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ // As entradas devem ser associadas a nomes para facilitar a programação e //futuras alterações do hardware. /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * SAÍDAS * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ // As saídas devem ser associadas a nomes para facilitar a programação e //futuras alterações do hardware. #define rs PORTEbits.RE0 // via do lcd que sinaliza recepção de dados ou comando #define enable PORTEbits.RE1 // enable do lcd /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Rotina que envia um COMANDO para o LCD * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ void comando_lcd(unsigned char caracter) rs = 0; // seleciona o envio de um comando PORTD = caracter; // carrega o PORTD com o caracter enable = 1 ; // gera pulso no enable Delay10TCYx(1); // espera 3 microsegundos enable = 0; // desce o pino de enable Delay10TCYx(4); // espera mínimo 40 microsegundos /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Rotina que envia um DADO a ser escrito no LCD * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ void escreve_lcd(unsigned char caracter) rs = 1; // seleciona o envio de um comando PORTD = caracter; // carrega o PORTD com o caracter enable = 1; // gera pulso no enable Delay10TCYx(1); // espera 10 microsegundos enable = 0; // desce o pino de enable Delay10TCYx(4); // espera mínimo 40 microsegundos /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Função para limpar o LCD * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ void limpa_lcd(void) comando_lcd(0x01); // limpa lcd Delay1KTCYx(2); /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Inicialização do Display de LCD * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
Curso Módulo 4 –Família 18F e MpLab C18 227
void inicializa_lcd(void) comando_lcd(0x30); // envia comando para inicializar display Delay1KTCYx(4); // espera 4 milisengundos comando_lcd(0x30); // envia comando para inicializar display Delay10TCYx(10); // espera 100 microsengundos comando_lcd(0x30); // envia comando para inicializar display comando_lcd(0x38); // liga o display, sem cursor e sem blink limpa_lcd(); // limpa lcd comando_lcd(0x0c); // display sem cursor comando_lcd(0x06); // desloca cursor para a direita /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Tela Principal * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ void tela_principal(void) comando_lcd(0x83); // posiciona o cursor na linha 0, coluna 0 escreve_lcd ('V'); escreve_lcd ('o'); escreve_lcd ('l'); escreve_lcd ('t'); escreve_lcd ('i'); escreve_lcd ('m'); escreve_lcd ('e'); escreve_lcd ('t'); escreve_lcd ('r'); escreve_lcd ('o'); // imprime mensagem no lcd void converte_bcd(unsigned char aux) unidade = 0; dezena = 0; centena = 0; if (aux == 0)return; while(aux--) unidade++; if(unidade != 10)continue; unidade = 0; dezena++; if (dezena != 10)continue; dezena = 0; centena++; if (centena != 10)continue; centena = 0; /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Configurações do Pic * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ void main() // configura microcontrolador OpenADC(ADC_FOSC_8 & ADC_LEFT_JUST & ADC_3ANA_0REF, ADC_CH0 & ADC_INT_OFF); PORTA=0x00; // limpa porta
228
PORTB=0x00; // limpa portb PORTC=0x00; // limpa portc PORTD=0x00; // limpa PORTD PORTE=0x00; // limpa porte LATA=0x00; // limpa porta LATB=0x00; // limpa portb LATC=0x00; // limpa portc LATD=0x00; // limpa PORTD LATE=0x00; // limpa porte TRISA=(0b11111111); // configuração da direção dos pinos de I/O TRISB=(0b11111111); TRISC=(0b11111111); TRISD=(0b00000000); TRISE=(0b00000100); inicializa_lcd(); // configura o lcd tela_principal(); // imprime a tela principal no LCD /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Rotina principal * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ while(1) // rotina principal ClrWdt(); //Inicia o watch-dog timer ConvertADC(); //Inicia conversão AD while (BusyADC()); //Aguarda Fim da conversão AD conversao = ADRESH; //lê resultado da conversão AD conversao = (conversao * 50); //faz regra de 3 para converter o valor, conversao = (conversao / 255); //das unidades de AD em Volts. conversao1 = conversao; converte_bcd(conversao1); comando_lcd(0xC5); //posiciona o cursor na linha 1, coluna 2 escreve_lcd (dezena + 0x30); //escreve no display de LCD escreve_lcd (','); escreve_lcd (unidade + 0x30); //escreve no display de LCD escreve_lcd ('V');
Curso Módulo 4 –Família 18F e MpLab C18 229
Dicas e Comentários
Inicialmente notar que toda a estrutura e rotinas utilizadas para a escrita no LCD são as mesmas já aplicadas na experiência anterior.
Observar também que não foi utilizada nenhuma interrupção neste programa. Por isso, o programa permanece parado em um pequeno loop enquanto a conversão não termina. Isto é checado através do bit ADCON0<GO/DONE>.
Outra rotina bem interessante que aparece neste sistema é a de conversão de um número qualquer (limitado entre 0 e 99) em dois dígitos separados, facilitando assim a escrita no LCD. Esta rotina devolve os dígitos nas variáveis UNIDADE e DEZENA. Não esquecer que antes de transmitir um valor decimal ao LCD, deve-se converte-lo em um caractere ASCII.
Para facilitar as contas e não utilizarmos números fracionários, a conversão para Volts é feita considerando-se 50 no lugar de 5,0 de forma que ao enviar o valor final ao LCD é simplesmente colocada uma vírgula entre os dois dígitos.
Exercícios Propostos
1. Simule que a entrada analógica é um sensor de temperatura linear que deve marcar de 10 a 80°C; 2. Altere o exemplo para indicar a tensão entre 0 e 5,00V, utilizando 10 bits de resolução.
230
Experiência 8 – Modulo PWM
Objetivo
O objetivo desta experiência é ensinar ao aluno como utilizar o módulo PWM do microcontrolador PIC18F452.
Descrição
Este PIC possui 2 canais de PWMs (CCP1 e CCP2), cada um com resolução máxima de 10 bits. Isto significa que o duty cycle poderá ser regulado de 0 a 100% com uma resolução máxima de 1024 pontos. No entanto, dependendo da configuração adotada, esta resolução não será atingida.
O período do PWM é controlado diretamente pelo TMR2, através do registrador PR2. Como já foi visto, sempre que TMR2 = PR2, o timer é zerado e neste momento, um novo período do PWM é iniciado. Desta forma, pode-se definir o período e a freqüência do PWM pelas seguintes fórmulas:
T = [(PR2) + 1] x 4 x Tosc x (Prescale do TMR2)
PWMFreq = 1 / T
O duty cycle normalmente é definido em porcentagem, porém, o PIC não define o valor do duty cycle e sim o tempo do pulso em nível alto. Desta forma, o tempo do pulso pode ser calculado por:
tp = CCPRxL:CCPxCON<CCPxX:CCPxY> x Tosc x (Prescale do TMR2)
Repare que a largura do pulso é ajustada em dois registradores: CCPRxL que armazena os 8 bits mais
significativos e CCPxCON que armazena os dois bits menos significativos. Assim, obtêm-se os 10 bits que controlam o duty cycle do PWM.
Apesar do PIC não definir o duty cycle, ele pode ser calculado dividindo o tempo do pulso em nível alto pelo período total do PWM.
Verifica-se então que apesar do período e o do tempo de pulso dependerem do cristal (Tosc) e do ajuste do prescale do TMR2, o duty cycle depende única e exclusivamente dos valores ajustados nos registradores PR2, CCPRxL e CCPxCON (bits 5 e 4).
Veja que o registrador PR2 (8 bits) que controla o período do PWM é multiplicado por 4 para poder igualar-se aos 10 bits que controlam o duty cycle. É justamente este o problema da resolução máxima atingida. Se o registrador PR2 for ajustado com um valor menor que 8 bits, ou seja, menor do que 255, serão necessários menos do que 10 bits para atingir um PWM com 100% de duty cycle. Portanto, o número de pontos para ajuste do duty cycle é 4 vezes maior do que o valor ajustado em (PR2+1). Em termos de bits, podemos dizer que a resolução do duty cycle é 2 bits maior do que o número de bits que formam o valor ajustado em PR2. Repare também que caso PR2 seja ajustado com 255 nunca será atingido um duty cycle de 100%, pois o período atingirá o valor máximo de 1024 ([PR2+1]x4) enquanto o tempo do pulso em nível alto (<DCxB9:DCxB0>) será no máximo 1023.
No software da experiência ativou-se a saída do módulo CCP2 para controlar a rotação do ventilador que está ligado ao pino RC1.
tp =
[(PR2) + 1] x 4 x TOSC x (Prescale do TMR2)
CCPRxL:CCPxCON<DCxB1:DCxB0> x TOSC x (Prescale do TMR2)
T
tp =
[(PR2) + 1] X 4
CCPRxL:CCPxCON<DCxB1:DCxB0>
T
Curso Módulo 4 –Família 18F e MpLab C18 231
O registrador PR2 foi ajustado com valor máximo, ou seja, 255 e o prescale do timer foi configurado para 1:16. Com isso a freqüência do PWM ficou em 244,14Hz (PWMPeríodo = 4,096ms), considerando-se que o microcontrolador do sistema MCMASTER está trabalhando a 4MHz.
As teclas da linha 4 foram habilitadas e receberam as seguintes funções:
Coluna Duty Cycle 1 0% 2 50% 3 75% 4 100%
A fim de deixar o sistema mais interativo, utilizou-se o LCD para mostrar o valor atual ajustado para o PWM.
232
Esquema Elétrico
Curso Módulo 4 –Família 18F e MpLab C18 233
Fluxograma
INÍCIO
INICIALIZA DISPLAY
VETOR DE RESET PULA P/ INÍCIO DO
PROGRAMA
CONFIGURAÇÕES INICIAIS PORTAS, TIMERS,
INTERRUPÇÕES, OPTION, ADs.
1
PREPARA TELA PRINCIPAL“ MCMASTER ”
“ PWM: 0FF ”
CONFIGURA REGISTRADOR PR2 (PERIODO DO PWM)
T=((PR2)+1)*4*Tosc*TMR2 Prescale T=((255)+1)*4*250ns*16 T=4,096ms -> 244,14Hz
CONFIGURA REGISTRADOR T2CON TIMER 2 LIGADO
PRESCALER -> 1:16 POSTSCALE -> 1:1
CONFIGURAÇÕES CCP2CON P/ PWMZERA DUTY CYCLE (CCPR2L)
234
Não
LIMPA WDT
1
RECARREGA FILTRO
BOTÃO 0 PRESS.?
Não
Não
TRATA BOTÃO 0
TRATA BOTÃO 1
Sim
Sim
Sim
BOTÃO 3 PRESS.?
BOTÃO 2 PRESS.?
BOTÃO 1 PRESS.?
TRATA BOTÃO 3
TRATA BOTÃO 2
Sim
Não
Curso Módulo 4 –Família 18F e MpLab C18 235
ESCREVE “OFF” NO LCD
CCPR2L = 0x00 LIMPA BITS 4 E 5 DE CCP2CON tp = CCPR2L:CCPR2CON<5,4>*
*Tosc*TMR2 Prescale tp = 0*250ns*16
tp = 0ms PWM -> Duty Cycle = 0% -> OFF
Sim
TRATA BOTÃO 0
ACÃO JÁ FOI EXEC. ?
Não
FILTRO TERMINOU ?
Sim
1
Não
ESCREVE “50%” NO LCD
CCPR2L = 0x80 LIMPA BITS 4 E 5 DE CCP2CON tp = CCPR2L:CCPR2CON<5,4>*
*Tosc*TMR2 Prescale tp = 512*250ns*16
tp = 2,048ms PWM -> Duty Cycle = 50%
Sim
TRATA BOTÃO 1
ACÃO JÁ FOI EXEC. ?
Não
FILTRO TERMINOU ?
Sim
1
Não
ESCREVE “75%” NO LCD
CCPR2L = 0XC0 LIMPA BITS 4 E 5 DE CCP2CON tp = CCPR2L:CCPR2CON<5,4>*
*Tosc*TMR2 Prescale tp = 768*250ns*16
tp = 3,072ms PWM -> Duty Cycle = 75%
Sim
TRATA BOTÃO 2
ACÃO JÁ FOI EXEC. ?
Não
FILTRO TERMINOU ?
Sim
1
Não
ESCREVE “100%” NO LCD
CCPR2L = 0xFF SETA BITS 4 E 5 DE CCP2CON tp = CCPR2L:CCPR2CON<5,4>*
*Tosc*TMR2 Prescale tp = 1023*250ns*16
tp = 4,092ms PWM -> Duty Cycle = 99,90%
Sim
TRATA BOTÃO 3
ACÃO JÁ FOI EXEC. ?
Não
FILTRO TERMINOU ?
Sim
1
Não
236
Código /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Programação em C18 - Recursos Básicos de programação * * Exemplo 8 * * * * CENTRO DE TREINAMENTO - MOSAICO ENGENHARIA * * * * TEL: (0XX11) 4992-8775 SITE: www.mosaico-eng.com.br * * E-MAIL: [email protected] * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * VERSÃO : 1.0 * * DATA : 10/08/2005 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Descrição geral * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ /* ESTE EXEMPLO FOI ELABORADO PARA EXPLICAR O FUNCIONAMENTO DO MÓDULO PWM DO PIC18F452. ELE MONITORA OS QUATRO BOTÕES E CONFORME O BOTÃO SELECIONADO APLICA UM VALOR DIFERENTE NO PWM, FAZENDO ASSIM UM CONTROLE SOBRE A VELOCIDADE DO VENTILADOR. NO LCD É MOSTRADO O VALOR ATUAL DO DUTY CYCLE. */ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * DEFINIÇÃO PIC * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ #include <p18F452.h> // Register definitions /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * INCLUDES DAS FUNÇÕES DE PERIFÉRICOS DO PIC * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ #include <pwm.h> //PWM library functions #include <adc.h> //ADC library functions #include <timers.h> //Timer library functions #include <delays.h> //Delay library functions #include <i2c.h> //I2C library functions #include <stdlib.h> //Library functions #include <usart.h> //USART library functions /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Configurações para gravação * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ #pragma config OSC = XT #pragma config WDT = ON #pragma config LVP = OFF /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Definição e inicialização das variáveis Globais * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ //Neste bloco estão definidas as variáveis globais do programa. //Este programa não utiliza nenhuma variável de usuário unsigned char FILTRO11; //FILTRAGEM 1 PARA O BOTÃO 1 unsigned char FILTRO12; //FILTRAGEM 2 PARA O BOTÃO 1 unsigned char FILTRO21; //FILTRAGEM 1 PARA O BOTÃO 2 unsigned char FILTRO22; //FILTRAGEM 2 PARA O BOTÃO 2 unsigned char FILTRO31; //FILTRAGEM 1 PARA O BOTÃO 3 unsigned char FILTRO32; //FILTRAGEM 2 PARA O BOTÃO 3 unsigned char FILTRO41; //FILTRAGEM 1 PARA O BOTÃO 4 unsigned char FILTRO42; //FILTRAGEM 2 PARA O BOTÃO 4 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Constantes internas * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ //A definição de constantes facilita a programação e a manutenção. #define T_FILTRO 25 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
Curso Módulo 4 –Família 18F e MpLab C18 237
* Declaração dos flags de software * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ //A definição de flags ajuda na programação e economiza memória RAM. //Este programa não utiliza nenhum flag de usuário struct unsigned BIT0:1; unsigned BIT1:1; unsigned BIT2:1; unsigned BIT3:1; unsigned BIT4:1; unsigned BIT5:1; unsigned BIT6:1; unsigned BIT7:1; FLAGSbits; //ARMAZENA OS FLAGS DE CONTROLE #define ST_BT1 FLAGSbits.BIT0 //STATUS DO BOTÃO 1 #define ST_BT2 FLAGSbits.BIT1 //STATUS DO BOTÃO 2 #define ST_BT3 FLAGSbits.BIT2 //STATUS DO BOTÃO 3 #define ST_BT4 FLAGSbits.BIT3 //STATUS DO BOTÃO 4 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Definição e inicialização dos port's * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * PROTOTIPAGEM DE FUNÇÕES * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ void comando_lcd(unsigned char caracter); void escreve_lcd(unsigned char caracter); void limpa_lcd(void); void inicializa_lcd(void); void tela_principal(void); /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * ENTRADAS * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ // As entradas devem ser associadas a nomes para facilitar a programação e //futuras alterações do hardware. #define BT1 PORTBbits.RB0 //PORTA DO BOTÃO //1 -> PRESSIONADO //0 -> LIBERADO #define BT2 PORTBbits.RB1 //PORTA DO BOTÃO //1 -> PRESSIONADO //0 -> LIBERADO #define BT3 PORTBbits.RB2 //PORTA DO BOTÃO //1 -> PRESSIONADO //0 -> LIBERADO #define BT4 PORTBbits.RB3 //PORTA DO BOTÃO //1 -> PRESSIONADO //0 -> LIBERADO /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * SAÍDAS * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ // AS SAÍDAS DEVEM SER ASSOCIADAS A NOMES PARA FACILITAR A PROGRAMAÇÃO E //FUTURAS ALTERAÇÕES DO HARDWARE. #define LINHA_4 PORTBbits.RB7 //PINO PARA ATIVAR LINHA 4 DO TECLADO //MATRICIAL //1 -> LINHA ATIVADA //0 -> LINHA DESATIVADA #define rs PORTEbits.RE0 // via do lcd que sinaliza recepção de dados ou comando #define enable PORTEbits.RE1 // enable do lcd
238
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Rotina que envia um COMANDO para o LCD * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ void comando_lcd(unsigned char caracter) rs = 0; // seleciona o envio de um comando PORTD = caracter; // carrega o PORTD com o caracter enable = 1 ; // gera pulso no enable Delay10TCYx(1); // espera 3 microsegundos enable = 0; // desce o pino de enable Delay10TCYx(4); // espera mínimo 40 microsegundos /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Rotina que envia um DADO a ser escrito no LCD * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ void escreve_lcd(unsigned char caracter) rs = 1; // seleciona o envio de um comando PORTD = caracter; // carrega o PORTD com o caracter enable = 1; // gera pulso no enable Delay10TCYx(1); // espera 10 microsegundos enable = 0; // desce o pino de enable Delay10TCYx(4); // espera mínimo 40 microsegundos /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Função para limpar o LCD * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ void limpa_lcd(void) comando_lcd(0x01); // limpa lcd Delay1KTCYx(2); /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Inicialização do Display de LCD * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ void inicializa_lcd(void) comando_lcd(0x30); // envia comando para inicializar display Delay1KTCYx(4); // espera 4 milisengundos comando_lcd(0x30); // envia comando para inicializar display Delay10TCYx(10); // espera 100 microsengundos comando_lcd(0x30); // envia comando para inicializar display comando_lcd(0x38); // liga o display, sem cursor e sem blink limpa_lcd(); // limpa lcd comando_lcd(0x0c); // display sem cursor comando_lcd(0x06); // desloca cursor para a direita /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Tela Principal * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ void tela_principal(void)
Curso Módulo 4 –Família 18F e MpLab C18 239
limpa_lcd(); // limpa lcd comando_lcd(0x84); // posiciona o cursor na linha 0, coluna 0 escreve_lcd ('M'); // imprime mensagem no lcd escreve_lcd ('C'); escreve_lcd ('M'); escreve_lcd ('A'); escreve_lcd ('S'); escreve_lcd ('T'); escreve_lcd ('E'); escreve_lcd ('R'); comando_lcd(0xC4); // posiciona o cursor na linha 1, coluna 4 escreve_lcd ('P'); // imprime mensagem no lcd escreve_lcd ('W'); escreve_lcd ('M'); escreve_lcd (':'); escreve_lcd (' '); escreve_lcd ('O'); escreve_lcd ('F'); escreve_lcd ('F'); /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Tela Tecla 1 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ void tela_tecla_1(void) comando_lcd(0xC9); // posiciona o cursor na linha 0, coluna 0 escreve_lcd ('O'); // imprime mensagem no lcd escreve_lcd ('F'); escreve_lcd ('F'); escreve_lcd (' '); /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Tela Tecla 2 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ void tela_tecla_2(void) comando_lcd(0xC9); // posiciona o cursor na linha 0, coluna 0 escreve_lcd ('5'); // imprime mensagem no lcd escreve_lcd ('0'); escreve_lcd ('%'); escreve_lcd (' '); /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Tela Tecla 3 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ void tela_tecla_3(void) comando_lcd(0xC9); // posiciona o cursor na linha 0, coluna 0 escreve_lcd ('7'); // imprime mensagem no lcd escreve_lcd ('5'); escreve_lcd ('%'); escreve_lcd (' '); /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Tela Tecla 4 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ void tela_tecla_4(void)
240
comando_lcd(0xC9); // posiciona o cursor na linha 0, coluna 0 escreve_lcd ('1'); // imprime mensagem no lcd escreve_lcd ('0'); escreve_lcd ('0'); escreve_lcd ('%'); /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Função Principal * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ void main () PORTA = 0x00; //Clear PORTA PORTB = 0x00; //Clear PORTB PORTC = 0x00; //Clear PORTC PORTD = 0x00; //Clear PORTD PORTE = 0x00; //Clear PORTE LATA = 0x00; //Clear PORTA LATB = 0x00; //Clear PORTB LATC = 0x00; //Clear PORTC LATD = 0x00; //Clear PORTD LATE = 0x00; //Clear PORTE TRISA = 0b00101111; //CONFIG DIREÇÃO DOS PINOS PORTA0 TRISB = 0b00001111; //CONFIG DIREÇÃO DOS PINOS PORTB TRISC = 0b10011001; //CONFIG DIREÇÃO DOS PINOS PORTC TRISD = 0b00000000; //CONFIG DIREÇÃO DOS PINOS PORTD TRISE = 0b00000000; //CONFIG DIREÇÃO DOS PINOS PORTE ADCON1 = 0b00000111; //DESLIGA CONVERSORES A/D OpenPWM2(255); OpenTimer2(TIMER_INT_OFF & T2_PS_1_16 & T2_POST_1_1); SetDCPWM2(0); /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Inicialização do Sistema * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ RCON = 0X00; LINHA_4 = 1; //ATIVA LINHA 4 DO TECLADO MATRICIAL inicializa_lcd(); // configura o lcd tela_principal(); /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Rotina Principal * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ while(1) ClrWdt(); FILTRO11 = 0; //BT1 FILTRO12 = T_FILTRO; //BT1 FILTRO21 = 0; //BT2 FILTRO22 = T_FILTRO; //BT2 FILTRO31 = 0; //BT3 FILTRO32 = T_FILTRO; //BT3 FILTRO41 = 0; //BT4 FILTRO42 = T_FILTRO; //BT4
Curso Módulo 4 –Família 18F e MpLab C18 241
while(BT1) ClrWdt(); if (--FILTRO11 == 0) FILTRO12--; if (FILTRO12 == 0) if (!ST_BT1) tela_tecla_1(); SetDCPWM2(0); ST_BT1 = 1; ST_BT1 = 0; // BOTÃO LIBERADO, LIMPA O FLAG while(BT2) ClrWdt(); if (--FILTRO21 == 0) FILTRO22--; if (FILTRO22 == 0) if (!ST_BT2) tela_tecla_2(); SetDCPWM2(512); ST_BT2 = 1; ST_BT2 = 0; // BOTÃO LIBERADO, LIMPA O FLAG while(BT3) ClrWdt(); if (--FILTRO31 == 0) FILTRO32--; if (FILTRO32 == 0) if (!ST_BT3) tela_tecla_3(); SetDCPWM2(767); ST_BT3 = 1; ST_BT3 = 0; // BOTÃO LIBERADO, LIMPA O FLAG while(BT4) ClrWdt(); if (--FILTRO41 == 0) FILTRO42--; if (FILTRO42 == 0) if (!ST_BT4) tela_tecla_4(); SetDCPWM2(1023); ST_BT4 = 1;
242
ST_BT4 = 0; // BOTÃO LIBERADO, LIMPA O FLAG // FIM LAÇO PRINCIPAL
Curso Módulo 4 –Família 18F e MpLab C18 243
Dicas e Comentários
Para calcular o valor que deve ser carregado nos registradores que controlam o tempo do pulso em nível alto (duty cycle) a partir de um determinado valor de duty cycle expresso em porcentagem pode-se utilizar a formula a seguir:
CCPRx = [(PR2)+1] x 4 x Porcentagem desejada
Vejamos como exemplo o valor de 50% adotado nesta experiência. A porcentagem desejada é de 50% e o
valor de PR2 é 255, assim,
CCPRx = 256 * 4 * 0,5 CCPRx = 512
Ou seja, se carregarmos os registradores que controlam o tempo do pulso em nível alto com 512, obteremos
um duty cycle de 50%
Exercícios Propostos
1. Corrija o problema encontrado no nível 100%, evitando que a saída seja colocada em zero, mesmo que por um período de tempo muito curto.
2. Em vez de trabalhar com somente 4 níveis de PWM, altere o sistema para que um botão ligue e desligue a saída e outros dois botões incremente e decremente o PWM, de 50 a 100% com passos de 5%.
3. Ative as duas saídas PWMs ao mesmo tempo, uma para o ventilador e outra para a resistência. Utilize dois botões para controlar o ajuste de cada uma delas.
244
Experiência 9 – Master I2C
Objetivo
O objetivo desta experiência é mostrar ao aluno como acessar a memória de dados EEPROM externa (24LC256) utilizando os recursos de hardware do PIC para implementar o protocolo de comunicação I2C.
Descrição
Conforme comentado na descrição do hardware, está memória está mapeada no endereço 7h (111b) da rede de comunicação I2C a fim de evitar conflitos com o relógio de tempo real. Além disso, como a memória utilizada no MCMASTER é de 256Kbits, ou seja, 32Kbytes, são necessários 15 bits, ou seja, 2 bytes, para o correto endereçamento.
Levando-se isso em consideração, para a escrita de dados na memória o microcontrolador deverá enviar a seguinte seqüência de informações:
• Envia o byte de controle que deve incorporar o endereço de hardware da memória na rede I2C além
do bit de R/W, que neste caso deve ser enviado em 0 a fim de sinalizar uma operação de escrita. Assim, o byte de controle completo considerando o mapeamento adotado no MCMASTER é 10101110b;
• Envia um start bit; • Recebe o bit de acknowledge (ACK); • Envia a parte mais significativa do endereço onde o dado será gravado; • Recebe o bit de acknowledge (ACK); • Envia a parte menos significativa do endereço onde o dado será gravado; • Recebe o bit de acknowledge (ACK); • Envia o dado a ser gravado; • Recebe o bit de acknowledge (ACK); • Envia um stop bit.
Já para uma operação de leitura a seqüência deverá ser:
• Envia um start bit; • Envia o byte de controle que deve incorporar o endereço de hardware da memória na rede I2C além
do bit de R/W, que neste caso deve ser enviado em 0 a fim de sinalizar uma operação de escrita. Note que inicialmente o endereço que se deseja ler deve ser escrito na memória. Assim, o byte de controle completo considerando o mapeamento adotado no MCMASTER é 10101110b;
• Recebe o bit de acknowledge (ACK); • Envia a parte mais significativa do endereço de o dado será lido; • Recebe o bit de acknowledge (ACK); • Envia a parte menos significativa do endereço de onde o dado será lido; • Recebe o bit de acknowledge (ACK); • Envia outro start bit; • Envia novamente o byte de controle, porém agora alterando o bit R/W para sinalizar a operação de
leitura. Assim, o byte de controle completo fica 10101111b; • Recebe o bit de acknowledge (ACK); • Recebe o byte lido da memória; • Envia um bit em 1 sinalizando que deseja encerrar leitura (sinal de NACK); • Envia um stop bit.
Curso Módulo 4 –Família 18F e MpLab C18 245
O exemplo da experiência foi elaborado utilizando os procedimentos descritos acima. São utilizados os botões da linha 4 para manipular um valor de 8 bits mostrado no display LCD. Este valor pode ser salvo e lido na memória EEPROM. Os botões das colunas 1 e 2 são utilizados para incrementar e decrementar o valor mostrado no display. O botão da coluna 3 salva o valor do display na memória serial enquanto o botão da coluna 4 é utilizado para ler o valor salvo na memória serial.
246
Esquema Elétrico
Curso Módulo 4 –Família 18F e MpLab C18 247
Fluxograma
INÍCIO
INICIALIZA DISPLAY
Não
Sim
TRAVA O PROGRAMA AGUARDA ESTOURO
DO WDT
VETOR DE RESET PULA P/ INÍCIO DO
PROGRAMA
CONFIGURAÇÕES INICIAIS PORTAS, TIMERS,
INTERRUPÇÕES, OPTION, ADs.
RESET WDT ?
1
MONTA TELA PRINCIPAL DO LCD
CARREGA ENDEREÇO A SER LIDO DA MEMÓRIA EEPROM
I2C E2PROM READ
SALVA DADO LIDO NA VARIÁVEL CORRETA
(VALOR_DADOS)
CONFIGURA FREQUÊNCIA DO CLOCK DO BARRAMENTO I2C
EM 100kHz
HABILITA MODO MASTER I2C
248
Não
LIMPA WDT
1
BOTÃO 0 PRESS.?
Não
Não
TRATA BOTÃO 0
TRATA BOTÃO 1
Sim
Sim
Sim
BOTÃO 3 PRESS.?
BOTÃO 2 PRESS.?
BOTÃO 1 PRESS.?
TRATA BOTÃO 3
TRATA BOTÃO 2
Sim
Não
CARREGA ENDEREÇO ONDE O DADO SERÁ SALVO NA E2PROM
TRATA BOTÃO 2
1
CARREGA DADO A SER SALVO NA E2PROM
I2C EEPROM WRITE Não
Sim ERRO DE LEITURA?
CARREGA ENDEREÇO ONDE O DADO SERÁ
LIDO DA E2PROM
TRATA BOTÃO 3
1
CARREGA DADO LIDO DA E2PROM NA
VARIÁVEL CORRETA (VALOR_DADO)
I2C EEPROM READ
Curso Módulo 4 –Família 18F e MpLab C18 249
INCREMENTA VALOR_DADOS
TRATA BOTÃO 0
1
ATUALIZA LCD
DECREMENTA VALOR_DADOS
TRATA BOTÃO 1
1
ATUALIZA LCD
Sim
Não
Não
EVENTO I2C EM ANDAMENTO?
AGUARDA I2C LIVRE
RETURN
TODOS OS EVENTO I2C
FINALIZADOS?
ACK OUT
CARREGA ACK = 0 (SSPCON2,ACKDT=0)
ENVIA ACK (SSPCON2,ACKEN=1)
RETURN
NACK OUT
CARREGA ACK = 1 (SSPCON2,ACKDT=1)
ENVIA ACK (SSPCON2,ACKEN=1)
RETURN
250
I2C EEPROM READ
ENVIA START BIT (SSPCON2,SEN=1)
ENVIA CONTROLE+END.COMP.+WRITE
(10101110b -> SSPBUF)
RETURN
RECEBE BYTE (SSPCON2,RCEN=1
AGUARDA I2C LIVRE
AGUARDA I2C LIVRE
ENVIA ENDEREÇO HIGH (xxxxxxxxb->SSPBUF)
AGUARDA I2C LIVRE
ENVIA START BIT (SSPCON2,SEN=1)
ENVIA CONTROLE+END.COMP.+READ
(10101111b -> SSPBUF)
AGUARDA I2C LIVRE
AGUARDA I2C LIVRE
AGUARDA I2C LIVRE
NACK OUT
ENVIA STOP BIT (SSPCON2,PEN=1)
AGUARDA I2C LIVRE
ENVIA ENDEREÇO LOW (xxxxxxxxb->SSPBUF)
AGUARDA I2C LIVRE
Curso Módulo 4 –Família 18F e MpLab C18 251
I2C EEPROM WRITE
ENVIA START BIT (SSPCON2,SEN=1)
ENVIA CONTROLE+END.COMP.+WRITE
(10101110b -> SSPBUF)
RETURN
AGUARDA I2C LIVRE
AGUARDA I2C LIVRE
ENVIA ENDEREÇO HIGH (xxxxxxxxb -> SSPBUF)
AGUARDA I2C LIVRE
ENVIA DADO A SER GRAVADO (xxxxxxxxb -> SSPBUF)
AGUARDA I2C LIVRE
ENVIA STOP BIT (SSPCON2,PEN=1)
AGUARDA I2C LIVRE
ENVIA ENDEREÇO LOW (xxxxxxxxb -> SSPBUF)
AGUARDA I2C LIVRE
252
Código /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Programação em C18 - Recursos Básicos de programação * * Exemplo 6 * * * * CENTRO DE TREINAMENTO - MOSAICO ENGENHARIA * * * * TEL: (0XX11) 4992-8775 SITE: www.mosaico-eng.com.br * * E-MAIL: [email protected] * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * VERSÃO : 1.0 * * DATA : 10/08/2006 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Descrição geral * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ /* ESTE EXEMPLO FOI ELABORADO PARA EXPLICAR O FUNCIONAMENTO DA LEITURA/ESCRITA NA MEMÓRIA E2PROM SERIAL EXTERNA, UTILIZANDO O MASTER I2C. */ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * DEFINIÇÃO PIC * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ #include <p18F452.h> // Register definitions /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * INCLUDES DAS FUNÇÕES DE PERIFÉRICOS DO PIC * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ #include <pwm.h> //PWM library functions #include <adc.h> //ADC library functions #include <timers.h> //Timer library functions #include <delays.h> //Delay library functions #include <i2c.h> //I2C library functions #include <stdlib.h> //Library functions #include <usart.h> //USART library functions /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Configurações para gravação * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ #pragma config OSC = XT #pragma config WDT = ON #pragma config WDTPS = 4 #pragma config LVP = OFF /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Definição e inicialização das variáveis Globais * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ //Neste bloco estão definidas as variáveis globais do programa. //Este programa não utiliza nenhuma variável de usuário unsigned char FILTRO11; //FILTRAGEM 1 PARA O BOTÃO 1 unsigned char FILTRO12; //FILTRAGEM 2 PARA O BOTÃO 1 unsigned char FILTRO21; //FILTRAGEM 1 PARA O BOTÃO 2 unsigned char FILTRO22; //FILTRAGEM 2 PARA O BOTÃO 2 unsigned char FILTRO31; //FILTRAGEM 1 PARA O BOTÃO 3 unsigned char FILTRO32; //FILTRAGEM 2 PARA O BOTÃO 3 unsigned char FILTRO41; //FILTRAGEM 1 PARA O BOTÃO 4 unsigned char FILTRO42; //FILTRAGEM 2 PARA O BOTÃO 4 unsigned char valor_dados = 0; /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Constantes internas * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ //A definição de constantes facilita a programação e a manutenção.
Curso Módulo 4 –Família 18F e MpLab C18 253
#define T_FILTRO 25 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Declaração dos flags de software * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ //A definição de flags ajuda na programação e economiza memória RAM. //Este programa não utiliza nenhum flag de usuário struct unsigned BIT0:1; unsigned BIT1:1; unsigned BIT2:1; unsigned BIT3:1; unsigned BIT4:1; unsigned BIT5:1; unsigned BIT6:1; unsigned BIT7:1; FLAGSbits; //ARMAZENA OS FLAGS DE CONTROLE #define ST_BT1 FLAGSbits.BIT0 //STATUS DO BOTÃO 1 #define ST_BT2 FLAGSbits.BIT1 //STATUS DO BOTÃO 2 #define ST_BT3 FLAGSbits.BIT2 //STATUS DO BOTÃO 3 #define ST_BT4 FLAGSbits.BIT3 //STATUS DO BOTÃO 4 #define ST_I2C FLAGSbits.BIT4 //STATUS DO I2C /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Definição e inicialização dos port's * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * PROTOTIPAGEM DE FUNÇÕES * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ void comando_lcd(unsigned char caracter); void escreve_lcd(unsigned char caracter); void limpa_lcd(void); void inicializa_lcd(void); void tela_principal(void); void I2C_EEPROM_WRITE(unsigned int endereco, unsigned char dado); unsigned char I2C_EEPROM_READ(unsigned int endereco); /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * ENTRADAS * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ // As entradas devem ser associadas a nomes para facilitar a programação e //futuras alterações do hardware. #define BT1 PORTBbits.RB0 //PORTA DO BOTÃO //1 -> PRESSIONADO //0 -> LIBERADO #define BT2 PORTBbits.RB1 //PORTA DO BOTÃO //1 -> PRESSIONADO //0 -> LIBERADO #define BT3 PORTBbits.RB2 //PORTA DO BOTÃO //1 -> PRESSIONADO //0 -> LIBERADO #define BT4 PORTBbits.RB3 //PORTA DO BOTÃO //1 -> PRESSIONADO //0 -> LIBERADO /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * SAÍDAS * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ // AS SAÍDAS DEVEM SER ASSOCIADAS A NOMES PARA FACILITAR A PROGRAMAÇÃO E //FUTURAS ALTERAÇÕES DO HARDWARE. #define LINHA_4 PORTBbits.RB7 //PINO PARA ATIVAR LINHA 4 DO TECLADO //MATRICIAL //1 -> LINHA
254
ATIVADA //0 -> LINHA DESATIVADA #define rs PORTEbits.RE0 // via do lcd que sinaliza recepção de dados ou comando #define enable PORTEbits.RE1 // enable do lcd /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Rotina que envia um COMANDO para o LCD * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ void comando_lcd(unsigned char caracter) rs = 0; // seleciona o envio de um comando PORTD = caracter; // carrega o PORTD com o caracter enable = 1 ; // gera pulso no enable Delay10TCYx(1); // espera 3 microsegundos enable = 0; // desce o pino de enable Delay10TCYx(4); // espera mínimo 40 microsegundos /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Rotina que envia um DADO a ser escrito no LCD * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ void escreve_lcd(unsigned char caracter) rs = 1; // seleciona o envio de um comando PORTD = caracter; // carrega o PORTD com o caracter enable = 1; // gera pulso no enable Delay10TCYx(1); // espera 10 microsegundos enable = 0; // desce o pino de enable Delay10TCYx(4); // espera mínimo 40 microsegundos /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Função para limpar o LCD * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ void limpa_lcd(void) comando_lcd(0x01); // limpa lcd Delay1KTCYx(2); /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Inicialização do Display de LCD * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ void inicializa_lcd(void) comando_lcd(0x30); // envia comando para inicializar display Delay1KTCYx(4); // espera 4 milisengundos comando_lcd(0x30); // envia comando para inicializar display Delay10TCYx(10); // espera 100 microsengundos comando_lcd(0x30); // envia comando para inicializar display comando_lcd(0x38); // liga o display, sem cursor e sem blink limpa_lcd(); // limpa lcd comando_lcd(0x0c); // display sem cursor comando_lcd(0x06); // desloca cursor para a direita
Curso Módulo 4 –Família 18F e MpLab C18 255
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Tela Principal * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ void atualiza_linha_1(void) comando_lcd(0x83); // posiciona o cursor na linha 0, coluna 0 escreve_lcd ('M'); // imprime mensagem no lcd escreve_lcd ('A'); escreve_lcd ('S'); escreve_lcd ('T'); escreve_lcd ('E'); escreve_lcd ('R'); escreve_lcd (' '); escreve_lcd ('I'); escreve_lcd ('2'); escreve_lcd ('C'); void atualiza_linha_2(unsigned char aux) unsigned char hexa_high; unsigned char hexa_low; hexa_low = aux & 0b00001111; hexa_high = (aux >> 4) & 0b00001111; comando_lcd(0xC6); // posiciona o cursor na linha 0, coluna 0 if(hexa_high <= 9) escreve_lcd (hexa_high + 0x30); // imprime mensagem no lcd else escreve_lcd(hexa_high + 0x37); if(hexa_low <= 9) escreve_lcd (hexa_low + 0x30); // imprime mensagem no lcd else escreve_lcd(hexa_low + 0x37); escreve_lcd ('h'); // imprime mensagem no lcd /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * ESCRITA DA EEPROM * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Função para escrita na EEPROM externa I2C * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ void I2C_EEPROM_WRITE(unsigned int endereco, unsigned char dado) unsigned char endereco_high; unsigned char endereco_low; endereco_high = endereco >> 8; endereco_low = endereco; StartI2C(); while(SSPCON2bits.SEN); WriteI2C(0b10100110); // controle de escrita IdleI2C(); // espera fim do evento I2C WriteI2C(endereco_high); IdleI2C(); // espera fim do evento I2C WriteI2C(endereco_low); IdleI2C(); // espera fim do evento I2C WriteI2C(dado); // controle de leitura StopI2C(); // fim de comunicação while(SSPCON2bits.PEN); ClrWdt(); Delay100TCYx(100); /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
256
* Função para leitura na EEPROM externa I2C * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ unsigned char I2C_EEPROM_READ(unsigned int endereco) unsigned char endereco_high; unsigned char endereco_low; unsigned char dado; endereco_high = endereco >> 8; endereco_low = endereco; StartI2C(); while(SSPCON2bits.SEN); WriteI2C(0b10100110); // controle de escrita IdleI2C(); // espera fim do evento I2C WriteI2C(endereco_high); IdleI2C(); // espera fim do evento I2C WriteI2C(endereco_low); IdleI2C(); // espera fim do evento I2C RestartI2C(); while(SSPCON2bits.SEN); WriteI2C(0b10100111); // controle de leitura IdleI2C(); // espera fim do evento I2C SSPCON2bits.RCEN = 1; while(SSPCON2bits.RCEN); NotAckI2C(); while(SSPCON2bits.ACKEN); StopI2C(); // fim de comunicação while(SSPCON2bits.PEN); return((unsigned char) SSPBUF); /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Função Principal * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ void main () OpenI2C(MASTER,SLEW_OFF); SSPADD = 0b00001001; PORTA = 0x00; //Clear PORTA PORTB = 0x00; //Clear PORTB PORTC = 0x00; //Clear PORTC PORTD = 0x00; //Clear PORTD PORTE = 0x00; //Clear PORTE LATA = 0x00; //Clear PORTA LATB = 0x00; //Clear PORTB LATC = 0x00; //Clear PORTC LATD = 0x00; //Clear PORTD LATE = 0x00; //Clear PORTE TRISA = 0b00101111; //CONFIG DIREÇÃO DOS PINOS PORTA0 TRISB = 0b00001111; //CONFIG DIREÇÃO DOS PINOS PORTB TRISC = 0b10010001; //CONFIG DIREÇÃO DOS PINOS PORTC TRISD = 0b00000000; //CONFIG DIREÇÃO DOS PINOS PORTD TRISE = 0b00000000; //CONFIG DIREÇÃO DOS PINOS PORTE ADCON1 = 0b00000111; //DESLIGA CONVERSORES A/D while(RCONbits.NOT_TO); //aguarda estouro do WDT /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Inicialização do Sistema * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ RCON = 0X00; LINHA_4 = 1; //ATIVA LINHA 4 DO TECLADO MATRICIAL
Curso Módulo 4 –Família 18F e MpLab C18 257
inicializa_lcd(); // configura o lcd atualiza_linha_1(); valor_dados = I2C_EEPROM_READ(0x0100); atualiza_linha_2(valor_dados); /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Rotina Principal * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ while(1) ClrWdt(); FILTRO11 = 0; //BT1 FILTRO12 = T_FILTRO; //BT1 FILTRO21 = 0; //BT2 FILTRO22 = T_FILTRO; //BT2 FILTRO31 = 0; //BT3 FILTRO32 = T_FILTRO; //BT3 FILTRO41 = 0; //BT4 FILTRO42 = T_FILTRO; //BT4 while(BT1) ClrWdt(); if (--FILTRO11 == 0) FILTRO12--; if (FILTRO12 == 0) if (!ST_BT1) //função valor_dados++; atualiza_linha_2(valor_dados); ST_BT1 = 1; ST_BT1 = 0; // BOTÃO LIBERADO, LIMPA O FLAG while(BT2) ClrWdt(); if (--FILTRO21 == 0) FILTRO22--; if (FILTRO22 == 0) if (!ST_BT2) //função valor_dados--; atualiza_linha_2(valor_dados); ST_BT2 = 1; ST_BT2 = 0; // BOTÃO LIBERADO, LIMPA O FLAG while(BT3) ClrWdt();
258
if (--FILTRO31 == 0) FILTRO32--; if (FILTRO32 == 0) if (!ST_BT3) //função I2C_EEPROM_WRITE(0x0100,valor_dados); atualiza_linha_2(valor_dados); ST_BT3 = 1; ST_BT3 = 0; // BOTÃO LIBERADO, LIMPA O FLAG while(BT4) ClrWdt(); if (--FILTRO41 == 0) FILTRO42--; if (FILTRO42 == 0) if (!ST_BT4) //função valor_dados = I2C_EEPROM_READ(0x0100); atualiza_linha_2(valor_dados); ST_BT4 = 1; ST_BT4 = 0; // BOTÃO LIBERADO, LIMPA O FLAG // FIM LAÇO PRINCIPAL
Curso Módulo 4 –Família 18F e MpLab C18 259
Dicas e Comentários
Este programa não utiliza as interrupções de leitura e escrita relacionadas ao protocolo I2C. Desta forma, ele possui uma rotina (AGUARDA_I2C_LIVRE) para saber se o sistema está liberado para a próxima ação.
Exercícios Propostos
1. Faça três modificações no primeiro exercício proposto da experiência 7. • Utilize a memória externa; • Limite os dados mostrados no display entre 0x41 e 0x5A; • Mostre os dados em ASCII, ou seja, entre A (0x41) e Z (0x5A);
2. Utilizando o exercício anterior grave na memória uma mensagem de até 16 caracteres. Depois, crie um programa que ao ser inicializado leia os 16 caracteres da memória e mostre a mensagem lida no LCD;
260
Experiência 10 – Comunicação serial RS232 via USART
Objetivo
O objetivo desta experiência é o aprendizado do módulo USART do microcontrolador PIC16F877A ou do PIC18F452 utilizado para implementar a comunicação padrão RS232, geralmente utilizada para estabelecer um canal de comunicação entre um microcontrolador e um computador.
Descrição
Para tornar o sistema versátil e simples, criou-se um programa capaz de testar a transmissão e recepção de dados de modo isolado, ou seja, apenas com o MCMASTER sem necessariamente conecta-lo ao computador. Embora nada impeça que a comunicação com o PC seja efetivamente realizada.
Para atender esta necessidade, o software da experiência implementa uma comunicação assíncrona Full duplex, isto é, com a transmissão e a recepção ativadas simultaneamente.
O valor transmitido é obtido a partir da leitura da tensão do potenciômetro através do conversor A/D limitando este valor entre 0 e 255 (8-bits). O valor do A/D é então enviado para a porta serial e para o LCD. Desta forma é possível visualizar o dado transmitido. Para facilitar ainda mais o usuário, mostra-se o valor em decimal (d) e em hexadecimal (h). A comunicação é realizada no padrão 8N1 com uma velocidade de 9.600bps.
Quanto à recepção, o valor obtido pela porta serial é diretamente impresso no display de LCD, através do código ASCII.
Para que o sistema funcione sem o PC, basta interligar os pinos 2 e 3 do conector DB9. Isto fará com que tudo que seja transmitido por TX seja imediatamente recebido em RX. Tanto a transmissão quanto a recepção são contínuas.
Não se deve esquecer de habilitar a porta RS232 para o microcontrolador através do botão de modo de operação após.
Curso Módulo 4 –Família 18F e MpLab C18 261
Esquema Elétrico
262
Fluxograma
INÍCIO
INICIALIZA DISPLAY
Não
Sim
TRAVA O PROGRAMA AGUARDA ESTOURO
DO WDT
VETOR DE RESET PULA P/ INÍCIO DO
PROGRAMA
CONFIGURAÇÕES INICIAIS PORTAS, TIMERS,
INTERRUPÇÕES, OPTION, ADs.
RESET WDT ?
1
PREPARA TELA PRINCIPAL“USART:9600,8,n,1”
“TX: d h RX: ”
CONFIGURA REGISTRADOR TXSTA HABILITA TRANSMISSÃO
MODO ASSÍNCRONO TRANSMISSÃO DE 8 BITS HIGH SPEED BAUD RATE
CONFIGURA BAUD RATE SPBRG = 25d -> 9600 bps
CONFIGURA REGISTRADOR RCSTA HABILITA RECEPÇÃO RECEPÇÃO DE 8 BITS RECEPÇÃO CONTÍNUA
DESABILITA ADDRESS DETECT
Curso Módulo 4 –Família 18F e MpLab C18 263
Testa bit RCIF do registrador
PIR1
Aguarda que o bit GO do registrador
ADCON0 seja 0
Sim
Não
1
LIMPA WDT
INICIA CONVERSÃO (ADCON0,G0=1)
FIM CONVERSÃO?
Sim
Não
ALGUM DADO RECEBIDO?
2
3
4
Testa bit TRMT do registrador
TXSTA
MOSTRA O VALOR DA CONVERSÃO A/D EM DECIMAL E HEXADECIMAL NO
DISPLAY LCD
3
Sim
Não BUFFER DE TX ESTÁ VAZIO?
TRANSMITE VALOR DA CONVERSÃO A/D PELA USART
INICIA UMA NOVA CONVERSÃO(ADCON0,GO=1)
2
MOSTRA O DADO RECEBIDO NO DISPLAY LCD
4
2
264
Código /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Programação em C18 - Recursos Básicos de programação * * Exemplo 10 * * * * CENTRO DE TREINAMENTO - MOSAICO ENGENHARIA * * * * TEL: (0XX11) 4992-8775 SITE: www.mosaico-eng.com.br * * E-MAIL: [email protected] * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * VERSÃO : 1.0 * * DATA : 10/08/2005 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Descrição geral * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ /* ESTE EXEMPLO FOI ELABORADO PARA EXPLICAR O FUNCIONAMENTO DA USART DO PIC. O SOFTWARE CONVERTE O CANAL 1 DO CONVERSOR A/D (POTENCIÔMETRO P2) E MOSTRA NO DISPLAY O VALOR CONVERTIDO EM DECIMAL E HAXADECIMAL. ALÉM DE MOSTRAR O VALOR NO DISPLAY, O SOFTWARE TRANSMITE PELA USART O VALOR DA CONVERSÃO. OS VALORES RECEBIDOS PELA USART TAMBÉM SÃO MOSTRADOS NO LCD COMO CARACTERES ASCII. */ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * DEFINIÇÃO PIC * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ #include <p18F452.h> // Register definitions /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * INCLUDES DAS FUNÇÕES DE PERIFÉRICOS DO PIC * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ #include <pwm.h> //PWM library functions #include <adc.h> //ADC library functions #include <timers.h> //Timer library functions #include <delays.h> //Delay library functions #include <i2c.h> //I2C library functions #include <stdlib.h> //Library functions #include <usart.h> //USART library functions /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Configurações para gravação * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ #pragma config OSC = XT #pragma config WDT = ON #pragma config WDTPS = 4 #pragma config LVP = OFF /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Definição e inicialização das variáveis Globais * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ //Neste bloco estão definidas as variáveis globais do programa. //Este programa não utiliza nenhuma variável de usuário unsigned char unidade; unsigned char dezena; unsigned char centena; unsigned char hexa_low; unsigned char hexa_high; unsigned char converte; char usart_rx; /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Constantes internas * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
Curso Módulo 4 –Família 18F e MpLab C18 265
//A definição de constantes facilita a programação e a manutenção. /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Declaração dos flags de software * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ //A definição de flags ajuda na programação e economiza memória RAM. //Este programa não utiliza nenhum flag de usuário /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Definição e inicialização dos port's * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * PROTOTIPAGEM DE FUNÇÕES * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ void comando_lcd(unsigned char caracter); void escreve_lcd(unsigned char caracter); void limpa_lcd(void); void inicializa_lcd(void); void tela_principal(void); void converte_bcd(unsigned char aux); void converte_hexadecimal(unsigned char aux); /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * ENTRADAS * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ // As entradas devem ser associadas a nomes para facilitar a programação e //futuras alterações do hardware. /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * SAÍDAS * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ // AS SAÍDAS DEVEM SER ASSOCIADAS A NOMES PARA FACILITAR A PROGRAMAÇÃO E //FUTURAS ALTERAÇÕES DO HARDWARE. #define rs PORTEbits.RE0 // via do lcd que sinaliza recepção de dados ou comando #define enable PORTEbits.RE1 // enable do lcd /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Rotina que envia um COMANDO para o LCD * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ void comando_lcd(unsigned char caracter) rs = 0; // seleciona o envio de um comando PORTD = caracter; // carrega o PORTD com o caracter enable = 1 ; // gera pulso no enable Delay10TCYx(1); // espera 3 microsegundos enable = 0; // desce o pino de enable Delay10TCYx(4); // espera mínimo 40 microsegundos /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Rotina que envia um DADO a ser escrito no LCD * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ void escreve_lcd(unsigned char caracter) rs = 1; // seleciona o envio de um comando PORTD = caracter; // carrega o PORTD com o caracter enable = 1; // gera pulso no enable Delay10TCYx(1); // espera 10 microsegundos enable = 0; // desce o pino de enable Delay10TCYx(4); // espera mínimo 40 microsegundos /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Função para limpar o LCD * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
266
void limpa_lcd(void) comando_lcd(0x01); // limpa lcd Delay1KTCYx(2); /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Inicialização do Display de LCD * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ void inicializa_lcd(void) comando_lcd(0x30); // envia comando para inicializar display Delay1KTCYx(4); // espera 4 milisengundos comando_lcd(0x30); // envia comando para inicializar display Delay10TCYx(10); // espera 100 microsengundos comando_lcd(0x30); // envia comando para inicializar display comando_lcd(0x38); // liga o display, sem cursor e sem blink limpa_lcd(); // limpa lcd comando_lcd(0x0c); // display sem cursor comando_lcd(0x06); // desloca cursor para a direita /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Tela Principal * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ void tela_principal(void) comando_lcd(0x80); // posiciona o cursor na linha 0, coluna 0 escreve_lcd ('U'); // imprime mensagem no lcd escreve_lcd ('S'); escreve_lcd ('A'); escreve_lcd ('R'); escreve_lcd ('T'); escreve_lcd (':'); escreve_lcd ('9'); escreve_lcd ('6'); escreve_lcd ('0'); escreve_lcd ('0'); escreve_lcd (','); escreve_lcd ('8'); escreve_lcd (','); escreve_lcd ('n'); escreve_lcd (','); escreve_lcd ('1'); comando_lcd(0xC0); // posiciona o cursor na linha 1, coluna 4 escreve_lcd ('T'); // imprime mensagem no lcd escreve_lcd ('X'); escreve_lcd (':'); escreve_lcd (' '); escreve_lcd (' '); escreve_lcd (' '); escreve_lcd ('d'); escreve_lcd (' '); escreve_lcd (' '); escreve_lcd (' '); escreve_lcd ('h'); escreve_lcd (' '); escreve_lcd ('R'); escreve_lcd ('X'); escreve_lcd (':');
Curso Módulo 4 –Família 18F e MpLab C18 267
escreve_lcd (' '); /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Tela Principal * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ void converte_bcd(unsigned char aux) unidade = 0; dezena = 0; centena = 0; if (aux == 0)return; while(aux--) unidade++; if(unidade != 10)continue; unidade = 0; dezena++; if (dezena != 10)continue; dezena = 0; centena++; if (centena != 10)continue; centena = 0; void converte_hexadecimal(unsigned char aux) hexa_low = aux & 0b00001111; hexa_high = (aux >> 4) & 0b00001111; /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Função Principal * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ void main () OpenADC(ADC_FOSC_32 & ADC_LEFT_JUST & ADC_3ANA_0REF, ADC_CH0 & ADC_INT_OFF ); OpenUSART(USART_TX_INT_OFF & USART_RX_INT_OFF & USART_ASYNCH_MODE & USART_EIGHT_BIT & USART_CONT_RX & USART_BRGH_HIGH,25); PORTA = 0x00; //Clear PORTA PORTB = 0x00; //Clear PORTB PORTC = 0x00; //Clear PORTC PORTD = 0x00; //Clear PORTD PORTE = 0x00; //Clear PORTE LATA = 0x00; //Clear PORTA LATB = 0x00; //Clear PORTB LATC = 0x00; //Clear PORTC LATD = 0x00; //Clear PORTD LATE = 0x00; //Clear PORTE TRISA = 0b00101111; //CONFIG DIREÇÃO DOS PINOS PORTA TRISB = 0b00001111; //CONFIG DIREÇÃO DOS PINOS PORTB TRISC = 0b10011001; //CONFIG DIREÇÃO DOS PINOS PORTC TRISD = 0b00000000; //CONFIG DIREÇÃO DOS PINOS PORTD TRISE = 0b00000000; //CONFIG DIREÇÃO DOS PINOS PORTE while(RCONbits.NOT_TO); //aguarda estouro do WDT /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Inicialização do Sistema * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
268
RCON = 0X00; inicializa_lcd(); // configura o lcd tela_principal(); /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Rotina Principal * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ ConvertADC(); //Inicia conversão AD while(1) ClrWdt(); if(BusyADC()) converte = ADRESH; converte_bcd(converte); comando_lcd(0xC3); // posiciona o cursor na linha 1, coluna 4 escreve_lcd (centena + 0x30); // imprime mensagem no lcd escreve_lcd (dezena + 0x30); escreve_lcd (unidade + 0x30); converte_hexadecimal(ADRESH); comando_lcd(0xC8); // posiciona o cursor na linha 1, coluna 4 if(hexa_high <= 9) escreve_lcd (hexa_high + 0x30); // imprime mensagem no lcd else escreve_lcd(hexa_high + 0x37); if(hexa_low <= 9) escreve_lcd (hexa_low + 0x30); // imprime mensagem no lcd else escreve_lcd(hexa_low + 0x37); while(BusyUSART()) ClrWdt(); WriteUSART(converte); continue; if(DataRdyUSART()) usart_rx = ReadUSART(); comando_lcd(0xCF); // posiciona o cursor na linha 1, coluna 4 escreve_lcd(usart_rx); ConvertADC(); //Inicia conversão AD // FIM LAÇO PRINCIPAL
Curso Módulo 4 –Família 18F e MpLab C18 269
Dicas e Comentários
A rotina de conversão Hex >>> Decimal deste exemplo é mais completa do que nos casos anteriores, pois trabalha com 3 dígitos (CENTENA, DEZENA e UNIDADE). Desta forma, ela pode converter todo o range do argumento de entrada (W) que vai de 0 a 255.
O sistema de conversão A/D é o mesmo apresentado na experiência 11, onde utilizou-se o conversor interno considerando apenas os 8 bits mais significativos. Com isso o valor a ser transmitido já fica limitado a um byte.
Devido a simplicidade do sistema não foi necessário o uso das interrupções, deixando-as desabilitadas. Para o caso da recepção o bit RCIF é testando toda vez dentro do loop principal. Quanto a transmissão, sempre que um novo valor foi convertido, checa-se se o buffer de saída está vazio para poder escrever o novo valor.
Exercícios Propostos
1. Ative o uso da interrupção de recebimento. Quanto à transmissão, em vez de deixá-la contínua, crie uma interrupção de timer como base de tempo. Por exemplo, transmita o valor atual convertido a cada 1 segundo;
2. Crie um programa no PC que receba o valor convertido, efetue alguma operação e devolva outro valor. Por exemplo, divida o valor por 25, pegue a parte inteira e some 30h para imprimir no LCD um valor de 0 a 9;
3. Mude a rotina de recepção e escrita no LCD para poder receber um número de 0 a 50 e mostrá-lo como 0.0 a 5.0. Altere o programa do PC para efetuar a regra de 3 necessária para converter um valor de 0 a 255 para 0 a 50. Com isso você voltou ao multímetro da experiência 7, só que com as contas de multiplicação e divisão não mais sendo feitas no PIC.
270
Experiência 11 – Teclado matricial 4x4
Objetivo
O objetivo desta experiência é mostrar ao aluno um método simples para implementação de um teclado matricial.
Descrição
O teclado matricial do MCMASTER é composto por 4 linhas e 4 colunas formando assim uma matriz de 16 teclas. Desta forma, utilizam-se 8 pinos do microcontrolador para realizar a leitura do teclado.
Os conceitos adotados na experiência de varredura de displays de leds não são muito diferentes dos adotados para varrer os estados de um teclado matricial.
Analisando o esquema elétrico nota-se que todas as teclas de uma mesma coluna estão interligadas. Além disso, nota-se um resistor de pull-down em cada uma das vias. Veja também que todas as teclas de uma mesma linha também encontram-se interligadas. A idéia de varredura aplicada aqui é habilitar uma linha de cada vez e analisar se alguma tecla da linha habilitada está pressionada.
Para isso, deve-se configurar o microcontrolador com os pinos das linhas como saída e os pinos das colunas como entrada. Note que se todas as linhas estiverem em nível lógico 0, ou seja, se nenhuma linha estiver habilitada, ao ler o estado das colunas sempre será lido o valor 0, estando as teclas pressionadas ou não. Na verdade o microcontrolador estará lendo o estado dos resistores que no caso são de pull-down, ou seja, leitura em 0.
Porém, se habilitarmos uma das linhas (e apenas uma) colocando-a em nível lógico 1 e pressionarmos uma tecla dessa linha, ao lermos o estado das colunas encontraremos um bit em 1, sendo que a posição do bit em 1 sinalizará a coluna na qual a tecla foi pressionada. Como foi o próprio microcontrolador que habilitou a linha, o número da linha é conhecido e como a posição do bit em 1 define a coluna da tecla é fácil determinar a linha e coluna da tecla pressionada.
O conceito de varredura continua válido, pois apenas uma linha deve ser habilitada de cada vez e o microcontrolador deve ficar o tempo todo alterando (varrendo) a linha habilitada e lendo o estado das colunas. Enquanto nenhum bit das colunas valer 1, a varredura das linhas continua sendo executada. Ao encontrar uma coluna com tecla pressionada o software deve executar o filtro de debounce mantendo a linha atual habilitada até que o filtro de debounce seja finalizado.
No fluxograma apresentado fica fácil de entender o conceito da varredura do teclado matricial. O exemplo da experiência analisa o teclado e caso alguma tecla seja pressionada, mostra a linha e coluna da
mesma é mostrado no display LCD.
Curso Módulo 4 –Família 18F e MpLab C18 271
Esquema Elétrico
272
Fluxograma
INÍCIO
INICIALIZA DISPLAY
VETOR DE RESET PULA P/ INÍCIO DO
PROGRAMA
CONFIGURAÇÕES INICIAIS PORTAS, TIMERS,
INTERRUPÇÕES, OPTION, ADs.
1
MONTA TELA PRINCIPAL DO LCD “ TECLADO 4x4 ” “LINHA: COLUNA: “
ATIVA UMA DAS LINHAS
Curso Módulo 4 –Família 18F e MpLab C18 273
LINHA 4 ATIVA?
LINHA 3 ATIVA?
LINHA 2 ATIVA?
Não
LIMPA WDT
1
ALGUM BOTÃO
PRESS. ?
Não
Não
Sim
Sim
Sim CARREGA NÚMERO DA LINHA NO WORK
(W=1) LINHA 1 ATIVA?
Sim
Não
ATIVA A PRÓXIMA LINHA
CARREGA NÚMERO DA LINHA NO WORK
(W=2)
CARREGA NÚMERO DA LINHA NO WORK
(W=3)
CARREGA NÚMERO DA LINHA NO WORK
(W=4)
SALVA O NÚEMRO DA LINHA ATIVA
2
274
Sim
Sim
Sim
Sim
Não
Não
Não
Não
ALGUM BOTÃO DA COLUNA 1 PRESS. ?
CARREGA WORK COM 1
ALGUM BOTÃO DA COLUNA 2 PRESS. ?
CARREGA WORK COM 2
ALGUM BOTÃO DA COLUNA 3 PRESS. ?
CARREGA WORK COM 3
ALGUM BOTÃO DA COLUNA 4 PRESS. ?
CARREGA WORK COM 4
RECARREGA FILTRO DE DEBOUNCE
ATUALIZA DISPLAY LCD
1
3
3
3
3
2
Curso Módulo 4 –Família 18F e MpLab C18 275
Não
SALVA O NÚMERO DA COLUNA
3
FIM DEBOUNCE DA TECLA ?
Sim
ATUALIZA O NÚMERO DA LINHA E DA COLUNA
NO DISPLAY LCD
1
276
Código /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Programação em C18 * * Exemplo 11 * * * * CENTRO DE TREINAMENTO - MOSAICO ENGENHARIA * * * * TEL: (0XX11) 4992-8775 SITE: www.mosaico-eng.com.br * * E-MAIL: [email protected] * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * VERSÃO : 1.0 * * DATA : 10/08/2005 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Descrição geral * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ /* Este exemplo foi elaborado para explicar o funcionamento do teclado matricial4x4. O número da linha e coluna da tecla pressionada e mostrado no LCD.*/ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * DEFINIÇÃO DAS VARIÁVEIS INTERNAS DO PIC * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ // O arquivo de definições do pic utilizado deve ser referenciado para que //os nomes definidos pela Microchip possam ser utilizados, sem a necessidade //de redigitação. #include <p18F452.h> // Register definitions /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Configurações para gravação * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ #pragma config OSC = XT #pragma config WDT = ON #pragma config WDTPS = 64 #pragma config LVP = OFF #pragma config PWRT = ON #pragma config BORV = 42 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * INCLUDES DAS FUNÇÕES DE PERIFÉRICOS DO PIC * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ #include <pwm.h> //PWM library functions #include <adc.h> //ADC library functions #include <timers.h> //Timer library functions #include <delays.h> //Delay library functions #include <i2c.h> //I2C library functions #include <stdlib.h> //Library functions #include <usart.h> //USART library functions /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * PROTOTIPAGEM DE FUNÇÕES * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ void comando_lcd(unsigned char caracter); void escreve_lcd(unsigned char caracter); void limpa_lcd(void); void inicializa_lcd(void); void tela_principal(void); void trata_coluna(void); /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Constantes internas * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ //A definição de constantes facilita a programação e a manutenção. /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Definição e inicialização das variáveis *
Curso Módulo 4 –Família 18F e MpLab C18 277
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ //Neste bloco estão definidas as variáveis globais do programa. unsigned char filtro; // Filtro para teclas unsigned char num_linha; // Armazena o número da linha ativada unsigned char num_coluna; // Armazena o número da coluna // unsigned char linha; // Registrador auxiliar para ativar as linhas do teclado 4x4 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Declaração dos flags de software * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ //A definição de flags ajuda na programação e economiza memória RAM. union Linhas_teclado struct unsigned BIT0:1; unsigned BIT1:1; unsigned BIT2:1; unsigned BIT3:1; unsigned BIT4:1; unsigned BIT5:1; unsigned BIT6:1; unsigned BIT7:1; ; //ARMAZENA OS FLAGS DE CONTROLE unsigned char linha_teclado; linha_ativa; #define linha1 linha_ativa.BIT4 /* LINHA_ATIVA,4, BIT 4 DO REGISTRADOR LINHA_ATIVA REPRESENTA A LINHA 1 DO TECLADO 4x4 1 -> LINHA ESTÁ ATIVADA 0 -> LINHA ESTÁ DESATIVADA */ #define linha2 linha_ativa.BIT5 /* LINHA_ATIVA,5, BIT 5 DO REGISTRADOR LINHA_ATIVA REPRESENTA A LINHA 2 DO TECLADO 4x4 1 -> LINHA ESTÁ ATIVADA 0 -> LINHA ESTÁ DESATIVADA */ #define linha3 linha_ativa.BIT6 /* LINHA_ATIVA,6, BIT 6 DO REGISTRADOR LINHA_ATIVA REPRESENTA A LINHA 3 DO TECLADO 4x4 1 -> LINHA ESTÁ ATIVADA 0 -> LINHA ESTÁ DESATIVADA */ #define linha4 linha_ativa.BIT7 /* LINHA_ATIVA,7, BIT 7 DO REGISTRADOR LINHA_ATIVA REPRESENTA A LINHA 4 DO TECLADO 4x4 1 -> LINHA ESTÁ ATIVADA 0 -> LINHA ESTÁ DESATIVADA */ #define fim linha_ativa.BIT3 /* REPRESENTA O FINAL DA VARREDURA 1 -> LINHA ESTÁ ATIVADA -> LINHA ESTÁ DESATIVADA */ #define linha linha_ativa.linha_teclado /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * ENTRADAS * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ // As entradas devem ser associadas a nomes para facilitar a programação e //futuras alterações do hardware. #define coluna1 PORTBbits.0 /* PINO DE ENTRADA DA COLUNA 1 1 -> ALGUMA TECLA DESTA COLUNA ESTÁ PRESSIONADA 0 -> NENHUMA TECLA DESTA COLUNA ESTÁ PRESSIONADA */ #define coluna2 PORTBbits.1 /* PINO DE ENTRADA DA COLUNA 2 1 -> ALGUMA TECLA DESTA COLUNA ESTÁ PRESSIONADA 0 -> NENHUMA TECLA DESTA COLUNA ESTÁ PRESSIONADA */
278
#define coluna3 PORTBbits.2 /* PINO DE ENTRADA DA COLUNA 3 1 -> ALGUMA TECLA DESTA COLUNA ESTÁ PRESSIONADA 0 -> NENHUMA TECLA DESTA COLUNA ESTÁ PRESSIONADA */ #define coluna4 PORTBbits.3 /* PINO DE ENTRADA DA COLUNA 4 1 -> ALGUMA TECLA DESTA COLUNA ESTÁ PRESSIONADA 0 -> NENHUMA TECLA DESTA COLUNA ESTÁ PRESSIONADA */ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * SAÍDAS * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ // As saídas devem ser associadas a nomes para facilitar a programação e //futuras alterações do hardware. #define rs PORTEbits.RE0 // via do lcd que sinaliza recepção de dados ou comando #define enable PORTEbits.RE1 // enable do lcd /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Rotina que envia um COMANDO para o LCD * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ void comando_lcd(unsigned char caracter) rs = 0; // seleciona o envio de um comando PORTD = caracter; // carrega o PORTD com o caracter enable = 1 ; // gera pulso no enable Delay10TCYx(1); // espera 3 microsegundos enable = 0; // desce o pino de enable Delay10TCYx(4); // espera mínimo 40 microsegundos /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Rotina que envia um DADO a ser escrito no LCD * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ void escreve_lcd(unsigned char caracter) rs = 1; // seleciona o envio de um comando PORTD = caracter; // carrega o PORTD com o caracter enable = 1; // gera pulso no enable Delay10TCYx(1); // espera 10 microsegundos enable = 0; // desce o pino de enable Delay10TCYx(4); // espera mínimo 40 microsegundos /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Função para limpar o LCD * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ void limpa_lcd(void) comando_lcd(0x01); // limpa lcd Delay1KTCYx(2); /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Inicialização do Display de LCD * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ void inicializa_lcd(void) comando_lcd(0x30); // envia comando para inicializar display Delay1KTCYx(4); // espera 4 milisengundos comando_lcd(0x30); // envia comando para inicializar display Delay10TCYx(10); // espera 100 microsengundos
Curso Módulo 4 –Família 18F e MpLab C18 279
comando_lcd(0x30); // envia comando para inicializar display comando_lcd(0x38); // liga o display, sem cursor e sem blink limpa_lcd(); // limpa lcd comando_lcd(0x0c); // display sem cursor comando_lcd(0x06); // desloca cursor para a direita /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Tela Principal * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ void tela_principal(void) comando_lcd(0x82); // posiciona o cursor na linha 0, coluna 0 escreve_lcd ('T'); // imprime mensagem no lcd escreve_lcd ('e'); escreve_lcd ('c'); escreve_lcd ('l'); escreve_lcd ('a'); escreve_lcd ('d'); escreve_lcd ('o'); escreve_lcd (' '); escreve_lcd ('4'); escreve_lcd ('X'); escreve_lcd ('4'); comando_lcd(0xC0); // posiciona o cursor na linha 1, coluna 0 escreve_lcd ('L'); // imprime mensagem no lcd escreve_lcd ('i'); escreve_lcd ('n'); escreve_lcd ('h'); escreve_lcd ('a'); escreve_lcd (':'); escreve_lcd (' '); escreve_lcd (' '); escreve_lcd ('C'); escreve_lcd ('o'); escreve_lcd ('l'); escreve_lcd ('u'); escreve_lcd ('n'); escreve_lcd ('a'); escreve_lcd (':'); /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Rotina para tratamento das colunas do Teclado 4X4 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ void trata_coluna(void) if(filtro) filtro--; if (filtro)return; PORTB = 0; comando_lcd(0xC6); // posiciona o cursor na linha 1, coluna 6 escreve_lcd (0x30 + num_linha); // comando_lcd(0xCF); // posiciona o cursor na linha 1, coluna 15 escreve_lcd (0x30 + num_coluna);// PORTD = 0; PORTB = linha;
280
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Configurações do Pic * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ void main() // configura microcontrolador PORTA=0x00; // limpa porta PORTB=0x00; // limpa portb PORTC=0x00; // limpa portc PORTD=0x00; // limpa PORTD PORTE=0x00; // limpa porte LATA=0x00; // limpa porta LATB=0x00; // limpa portb LATC=0x00; // limpa portc LATD=0x00; // limpa PORTD LATE=0x00; // limpa porte TRISA=(0b00101111); // configuração da direção dos pinos de I/O TRISB=(0b00001111); TRISC=(0b10011000); TRISD=(0b00000000); TRISE=(0b00000000); ADCON1=0b00000111; inicializa_lcd(); // configura o lcd tela_principal(); // imprime a tela principal no LCD /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Rotina principal * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ linha = 0b00010000; while(1) // rotina principal ClrWdt(); //Inicia o watch-dog timer ativa_proxima_linha: if (filtro == 100) linha = linha >> 1; if (fim) linha = 0b10000000; PORTB = linha; if (linha4) num_linha = 4; // verifica qual é a linha ativa if (linha3) num_linha = 3; if (linha2) num_linha = 2; if (linha1) num_linha = 1; switch(PORTB & 0b00001111) case 1: num_coluna = 1; // verifica qual é a linha ativa trata_coluna(); break; case 2: num_coluna = 2; // verifica qual é a linha ativa trata_coluna(); break; case 4: num_coluna = 3; // verifica qual é a linha ativa trata_coluna(); break; case 8:
Curso Módulo 4 –Família 18F e MpLab C18 281
num_coluna = 4; // verifica qual é a linha ativa trata_coluna(); break; default: filtro = 100; PORTB = 0; // Desativa todos os displays de 7 segmentos comando_lcd(0xC6); // posiciona o cursor na linha 1, coluna 6 escreve_lcd (' '); // comando_lcd(0xCF); // posiciona o cursor na linha 1, coluna 15 escreve_lcd (' '); // PORTD = 0; PORTB = linha;
282
Dicas e Comentários
Notar que ao habilitar uma linha do teclado matricial também se habilita um dos displays de 7 segmentos, ou seja, foram utilizados os mesmos pinos do microcontrolador para habilitar as linhas e os displays. Esta é uma forma econômica de varrer os displays e ao mesmo tempo varrer o teclado. Veja que com apenas 16 I/Os do microcontrolador foi possível ligar 4 displays de 7 segmentos e 16 teclas. Foram utilizados 8 I/Os para segmentos mais o ponto decimal, 4 I/Os de seleção (linhas e displays) e 4 I/Os de colunas.
Exercícios Propostos
1. Alterar o mapeamento do software invertendo a numeração das linhas e colunas. 2. Mostrar no display LCD o número da tecla pressionada e não o número da linha e da coluna. 3. Sem utilizar o display LCD, fazer um software que utilize a varredura do teclado e dos displays ao mesmo
tempo, mostrando o número da tecla pressionada nos displays.
Curso Módulo 4 –Família 18F e MpLab C18 283
Experiência 12 – Relógio de tempo real (RTC)
Objetivo
O objetivo desta experiência é mostrar como utilizar o relógio de tempo real (RTC).
Descrição
No MCMASTER optou-se pela utilização do relógio de tempo real (RTC) modelo PCF8583P da Philips. O protocolo de comunicação é do tipo I2C e para maiores informações a respeito do componente deve-se consultar o data sheet disponível no CD.
As rotinas de acesso ao relógio seguem o padrão adotado na experiência 16 (Master I2C) com apenas algumas modificações. Na realidade as alterações são que o relógio está mapeado no endereço 0h (000b) para evitar conflitos com a memória e ao invés de serem utilizados dois bytes para compor o endereço foi utilizado apenas um o que já é suficiente.
O software da experiência apenas lê a hora atual do relógio e mostra o resultado no LCD. Vale lembrar que o relógio é completo, ou seja, dispõe de hora, minuto, segundo, dia, mês e ano, inclusive
bissexto.
284
Esquema Elétrico
Curso Módulo 4 –Família 18F e MpLab C18 285
Fluxograma
INÍCIO
INICIALIZA DISPLAY
VETOR DE RESET PULA P/ INÍCIO DO
PROGRAMA
CONFIGURAÇÕES INICIAIS PORTAS, TIMERS,
INTERRUPÇÕES, OPTION, ADs.
1
MONTA TELA PRINCIPAL DO LCD
CONFIGURA FREQUÊNCIA DO CLOCK DO BARRAMENTO I2C
EM 100kHz
HABILITA MODO MASTER I2C
286
LIMPA WDT
1
CARREGA ENDEREÇO DA LEITURA DA HORA
ATUALIZA A HORA NO DISPLAY LCD
SALVA A HORA NA RAM
I2C_READ_RTC
CARREGA ENDEREÇO DA LEITURA DOS MINUTOS
SALVA OS MINUTOS NA RAM
I2C_READ_RTC
CARREGA ENDEREÇO DA LEITURA DOS SEGUNDOS
SALVA OS REGUNDOS NA RAM
I2C_READ_RTC
Curso Módulo 4 –Família 18F e MpLab C18 287
Sim
Não
Não
EVENTO I2C EM ANDAMENTO?
AGUARDA I2C LIVRE
RETURN
TODOS OS EVENTO I2C
FINALIZADOS?
ACK OUT
CARREGA ACK = 0 (SSPCON2,ACKDT=0)
ENVIA ACK (SSPCON2,ACKEN=1)
RETURN
NACK OUT
CARREGA ACK = 1 (SSPCON2,ACKDT=1)
ENVIA ACK (SSPCON2,ACKEN=1)
RETURN
Sim
Não RECEBEU ACK?
TESTA ACK
RETURN
LIMPA FLAG DE ERRO
SETA FLAG DE ERRO
288
I2C_READ_RTC
Não
Sim
ENVIA START BIT (SSPCON2,SEN=1)
ENVIA CONTROLE+END.COMP.+WRITE
(10100000b -> SSPBUF)
OCORREU ERRO?
RETURN
RECEBE BYTE (SSPCON2,RCEN=1
AGUARDA I2C LIVRE
AGUARDA I2C LIVRE
TESTA ACK
Não
Sim
ENVIA ENDEREÇO (xxxxxxxxb->SSPBUF)
OCORREU ERRO?
AGUARDA I2C LIVRE
TESTA ACK
ENVIA START BIT (SSPCON2,SEN=1)
2
2
Não
Sim
ENVIA CONTROLE+END.COMP.+READ
(10100001b -> SSPBUF)
OCORREU ERRO?
AGUARDA I2C LIVRE
TESTA ACK
2
AGUARDA I2C LIVRE
AGUARDA I2C LIVRE
NACK OUT
ENVIA STOP BIT (SSPCON2,PEN=1)
AGUARDA I2C LIVRE
ENVIA STOP BIT (SSPCON2,PEN=1)
RETURN
2
Curso Módulo 4 –Família 18F e MpLab C18 289
Código /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Programação em C18 * * Exemplo 12 * * * * CENTRO DE TREINAMENTO - MOSAICO ENGENHARIA * * * * TEL: (0XX11) 4992-8775 SITE: www.mosaico-eng.com.br * * EMAIL: [email protected] * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * VERSÃO : 1.0 * * DATA : 05/06/2003 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Descrição geral * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * DEFINIÇÃO DAS VARIÁVEIS INTERNAS DO PIC * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ // O arquivo de definições do pic utilizado deve ser referenciado para que //os nomes definidos pela Microchip possam ser utilizados, sem a necessidade //de redigitação. #include <p18F452.h> // Register definitions /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Configurações para gravação * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ #pragma config OSC = XT #pragma config WDT = ON #pragma config WDTPS = 128 #pragma config LVP = OFF #pragma config PWRT = ON #pragma config BORV = 42 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * INCLUDES DAS FUNÇÕES DE PERIFÉRICOS DO PIC * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ #include <pwm.h> //PWM library functions #include <adc.h> //ADC library functions #include <timers.h> //Timer library functions #include <delays.h> //Delay library functions #include <i2c.h> //I2C library functions #include <stdlib.h> //Library functions #include <usart.h> //USART library functions /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * PROTOTIPAGEM DE FUNÇÕES * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ void comando_lcd(unsigned char caracter); void escreve_lcd(unsigned char caracter); void limpa_lcd(void); void inicializa_lcd(void); void tela_principal(void); void LE_RELOGIO(unsigned char endereco); void ESCREVE_RELOGIO(unsigned char endereco); /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Constantes internas * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ //A definição de constantes facilita a programação e a manutenção. #define END_HORA 4 // ENDEREÇO DA HORA NO RELOGIO RTC #define END_MINUTO 3 // ENDEREÇO DO MINUTO NO RELOGIO RTC #define END_SEGUNDO 2 // ENDEREÇO DO SEGUNDO NO RELOGIO RTC /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Definição e inicialização das variáveis *
290
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ //Neste bloco estão definidas as variáveis globais do programa. unsigned char dez_hora; unsigned char uni_hora; unsigned char dez_min; unsigned char uni_min; unsigned char dez_seg; unsigned char uni_seg; unsigned char buffer; /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Declaração dos flags de software * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ //A definição de flags ajuda na programação e economiza memória RAM. /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * ENTRADAS * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ // As entradas devem ser associadas a nomes para facilitar a programação e //futuras alterações do hardware. /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * SAÍDAS * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ // As saídas devem ser associadas a nomes para facilitar a programação e //futuras alterações do hardware. #define rs PORTEbits.RE0 // via do lcd que sinaliza recepção de dados ou comando #define enable PORTEbits.RE1 // enable do lcd /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Rotina que envia um COMANDO para o LCD * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ void comando_lcd(unsigned char caracter) rs = 0; // seleciona o envio de um comando PORTD = caracter; // carrega o PORTD com o caracter enable = 1 ; // gera pulso no enable Delay10TCYx(1); // espera 3 microsegundos enable = 0; // desce o pino de enable Delay10TCYx(4); // espera mínimo 40 microsegundos /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Rotina que envia um DADO a ser escrito no LCD * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ void escreve_lcd(unsigned char caracter) rs = 1; // seleciona o envio de um comando PORTD = caracter; // carrega o PORTD com o caracter enable = 1; // gera pulso no enable Delay10TCYx(1); // espera 10 microsegundos enable = 0; // desce o pino de enable Delay10TCYx(4); // espera mínimo 40 microsegundos /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Função para limpar o LCD * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ void limpa_lcd(void) comando_lcd(0x01); // limpa lcd Delay1KTCYx(2); /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Inicialização do Display de LCD *
Curso Módulo 4 –Família 18F e MpLab C18 291
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ void inicializa_lcd(void) comando_lcd(0x30); // envia comando para inicializar display Delay1KTCYx(4); // espera 4 milisengundos comando_lcd(0x30); // envia comando para inicializar display Delay10TCYx(10); // espera 100 microsengundos comando_lcd(0x30); // envia comando para inicializar display comando_lcd(0x38); // liga o display, sem cursor e sem blink limpa_lcd(); // limpa lcd comando_lcd(0x0c); // display sem cursor comando_lcd(0x06); // desloca cursor para a direita /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * LEITURA DO RELÓGIO * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ void LE_RELOGIO(unsigned char endereco) StartI2C(); IdleI2C(); // espera fim do evento I2C WriteI2C(0b10100000); // controle de escrita IdleI2C(); // espera fim do evento I2C WriteI2C(endereco); // lê o relógio IdleI2C(); // espera fim do evento I2C RestartI2C(); // restart IdleI2C(); // espera fim do evento I2C WriteI2C(0b10100001); // controle de leitura IdleI2C(); // espera fim do evento I2C buffer = ReadI2C(); // recebe dado do relógio IdleI2C(); // espera fim do evento I2C StopI2C(); // fim de comunicação IdleI2C(); // espera fim do evento I2C /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * ESCRITA NO RELÓGIO * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ void ESCREVE_RELOGIO(unsigned char endereco) StartI2C(); IdleI2C(); // espera fim do evento I2C WriteI2C(0b10100000); // controle de escrita IdleI2C(); // espera fim do evento I2C WriteI2C(endereco); // escreve dado no relógio IdleI2C(); // espera fim do evento I2C WriteI2C(buffer); // escreve dado no relógio IdleI2C(); // espera fim do evento I2C StopI2C(); IdleI2C(); // espera fim do evento I2C /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Tela Principal * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ void tela_principal(void) comando_lcd(0x83); // posiciona o cursor na linha 0, coluna 0 escreve_lcd ('R'); escreve_lcd ('e'); escreve_lcd ('l'); escreve_lcd ('o'); escreve_lcd ('g');
292
escreve_lcd ('i'); escreve_lcd ('o'); // imprime mensagem no lcd /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Configurações do Pic * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ void main() // configura microcontrolador PORTA=0x00; // limpa porta PORTB=0x00; // limpa portb PORTC=0x00; // limpa portc PORTD=0x00; // limpa PORTD PORTE=0x00; // limpa porte LATA=0x00; // limpa porta LATB=0x00; // limpa portb LATC=0x00; // limpa portc LATD=0x00; // limpa PORTD LATE=0x00; // limpa porte TRISA=(0b11111111); // configuração da direção dos pinos de I/O TRISB=(0b11111111); TRISC=(0b11110111); TRISD=(0b00000000); TRISE=(0b00000100); ADCON1=0b00000111; OpenI2C(MASTER,SLEW_OFF); SSPADD = 9; buffer = 0x23; // acerta hora ESCREVE_RELOGIO(END_HORA); buffer = 0x40; // acerta minuto ESCREVE_RELOGIO(END_MINUTO); buffer = 0x00; // acerta segundos ESCREVE_RELOGIO(END_SEGUNDO); inicializa_lcd(); // configura o lcd tela_principal(); // imprime a tela principal no LCD /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Rotina principal * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ while(1) // rotina principal ClrWdt(); //Inicia o watch-dog timer LE_RELOGIO(END_HORA); dez_hora = buffer >> 4; dez_hora = (dez_hora & 0b00000011) + 0x30; uni_hora = buffer; uni_hora = (uni_hora & 0b00001111) + 0x30; LE_RELOGIO(END_MINUTO); dez_min = buffer >> 4; dez_min = (dez_min & 0b00001111) + 0x30; uni_min = buffer; uni_min = (uni_min & 0b00001111) + 0x30; LE_RELOGIO(END_SEGUNDO); dez_seg = buffer >> 4; dez_seg = (dez_seg & 0b00001111) + 0x30; uni_seg = buffer; uni_seg = (uni_seg & 0b00001111) + 0x30;
Curso Módulo 4 –Família 18F e MpLab C18 293
comando_lcd(0xC3); // posiciona o cursor na linha 0, coluna 3 escreve_lcd (dez_hora); // DEZ. HORA escreve_lcd (uni_hora); // UNID. HORA escreve_lcd (':'); // escreve_lcd (dez_min); // DEZ. MINUTO escreve_lcd (uni_min); // UNID. MINUTO escreve_lcd (':'); // escreve_lcd (dez_seg); // DEZ. SEGUNDO escreve_lcd (uni_seg); // UNID. SEGUNDO
294
Dicas e Comentários
Além do relógio RTC o componente PCF8583 da Philips apresenta também uma região da memória que pode ser utilizada com uma RAM convencional, porém com acesso via I2C. Como geralmente o relógio sempre é utilizado junto com uma bateria, como no MCMASTER, este recurso da RAM pode ser útil para armazenar informações enquanto o microcontrolador não está energizado.
Exercícios Propostos
1. Altere o software para mostrar a data e a hora no display LCD. 2. Crie um software para ajustar a data e hora do relógio utilizando o teclado matricial.