Blog de Luiz Oscar Ruschel Pilger destinado ao estudo de Microcontroladores PIC18F4550.
O material deste blog é fruto de um trabalho iniciado em 2013 para as aulas de microcontroladores do curso de Técnico em Eletrônica da Escola Técnica Estadual Parobé de Porto Alegre - RS.
Ele vem a substituir o antigo blog picparobe.
A medição de tempo é feita por contagem de ciclos de instrução (TCI), cujo valor, em segundos depende da frequência do oscilador de clock. O tempo correspondente ao ciclo de instrução (TCI), é dado pela equação:
Para uma frequência de 48MHz temos
xc.h
A bliblioteca xc.h disponibiliza alguma funções que permitem gerar atrasos.
É necessário "chamar" a biblioteca xc.h
#include <xc.h>
Para o correto funcionamento, uma vez que as funções são baseadas em ciclos de instrução do microcontrolador é necessário indicar a frequência em Hz de trabalho do microcontrolador
#define _XTAL_FREQ 48000000
Obs.: Nas funções e macros descritas a seguir o argumento a ser passado ("cycles" ou "x") não pode ser um valor elevado, pois o compilador "entende" que desta forma o programa poderia estar "travado" ou em loop infinito, então para obter tempos maiores, é necessário chamar várias vezes as funções ou realizar inteirações (laços de repetição como "for", "while").
Fonte: Guia do Usuário do compilador XC8 - Microchip
_delay
Gera um atraso de um número de ciclos que é especificado como argumento "cycles"
Sintaxe: void _delay(unsigned long cycles);
Uso: _delay(10); // gera um atraso de 10 ciclos de instrução aproximadamente 833ns (10 x 83,3ns)
_delay3
Gera um atraso de 3 vezes o número de ciclos que é especificado como argumento "cycles"
Sintaxe: void _delay3(unsigned char cycles);
Uso: _delay3(10); // atraso de 30 ciclos de instrução
__delayus
É uma macro baseada na função _delay e gera um atraso em us que é especificado no argumento "x"
Sintase: void __delay_us(x);
Uso: __delay_us(1); // gera uma atraso de 1us
__delayms
É uma macro baseada na função _delay e gera um atraso em ms que é especificado no argumento "x"
Sensores são dispositivos, também chamados de transdutores, são elementos que realizam a interface entre os circuitos e o mundo real convertendo grandezas físicas em elétricas.
– Analógico: o sinal de saída deste tipo de sensor vai variar ao longo do tempo, de forma a assumir valores dentro da sua faixa de operação. Geralmente, os sinais mais utilizados são 4… 20 mA ou 0… 10 V. Mas ele poderá variar de acordo com a sua distância de acionamento ou de acordo com o movimento de um atuador, por exemplo.
– Digital: é um tipo de sensor que pode assumir apenas dois valores em seu sinal de saída, que poderão ser interpretados como 0 (zero) ou então 1.
Existem sensores que podem atuar tanto de forma analógica como digital, e isso vai depender da sua aplicação.
Sensor LDR como entrada digital
A ligação pode ser invertida
Sugere-se a aplicação de um comparador pada adequar o sinal
Atividade 1
Elaborar um programa para acionar uma saída a partir da ação do sensor
Implementar o circuito acima e realizar as medidas de tensão na saída do sensor para as situações abaixo:
Descreva incidência alta de luminosidade:
Descreva incidência baixa de luminosidade:
Avaliação: Mostrar os resultados obtidos.
Atividade 2.2
Inclua junto ao circuito da Atividade 1.1 o circuito abaixo. O Amplificador operacional funciona como comparador, podendo ser utilizado o LM339, LM393, ou qualquer operacional que funcione como comparador.
Calcule os valores de R6 e R7 para construir um divisor de tensão para o comparador de forma que consiga atender todas as situações da tabela da atividade 1.1.
Avaliação: Mostrar os resultados obtidos.
Atividade 2.3
Elabore um programa para contar a passagem de elementos em uma esteira hipotética
Avaliação: Apresentar hardware e software em funcionamento
Baixe o programa “Mem_2_1.c” (listado mais abaixo). Adapte as faixas das teclas para o seu teclado. Lembrar que as teclas estão representadas pelo seu código ASCII
Implemente o hardware mostrado na imagem acima:
Teclado com 16 teclas matricial por conversor A/D e interrupção
Sensor de porta fechada. Utilizar circuito de um botão simples simulando um sensor de contato
Acionamento de tranca: Saída com led indicativo e circuito para acionamento de trava elétrica. Utilizar circuito sugerido na imagem acima com acoplador ótico e acionamento por relé (permite acionamento AC ou DC).
Avaliação: Apresentar hardware e software em funcionamento
EFEITO MAGNÉTICO (TRAVA ELETROMAGNÉTICA)
Atividade 2.2
Modifique o programa da atividade 2.1 para:
Solicitar a senha para operação de trancar a porta (utilizar a mesma senha de abertura)
Verificar se a porta está fechada antes de acionar a tranca
Avaliação: Apresentar hardware e software em funcionamento
Fluxograma de sugestão para implementação da atividade 2.2:
Programa Mem_2_1.c
/* * File: Mem_2_1.c * Author: Pilger * * Created on 4 de Outubro de 2017, 10:17 * * Programa cofre digital * Rotina de leitura de 3 teclas para gerar um senha com valor entre 0 e 255 * Realiza a troca de senha * Solicita 2 vezes a digitação de senha e testa se menor que 255 * compara so 2 valores se forem iguai e menor que 255 grava no endereço 0x00 da EEPROM * Abertura de porta * Solicita senha * Senha == 255 -> troca de senha * Senha <255 testa se senha é a mesma da eeprom * Senha errada, mensagem de erro * Senha correta * destrava porta * espera abrir * espera fechar * tranca a porta * */ #define _XTAL_FREQ 48000000 #include <stdio.h> #include <xc.h> #include "C:\h\Config_PIC18F4550.h" #include "c:\h\biblioteca_lcd_2x162_48M_XC.h" #define tranca LATBbits.LATB7 #define sensor_porta PORTBbits.RB6 //prototipos de funções void inicia_regs(void); void interrupt ISR_alta_prioridade(void); void config_int(void); void config_int0(void); void config_AD(void); int conv_AD(void); void delay_ms(int i); int le_3_tec(unsigned char ns1); unsigned char testa255(unsigned char ns); void troca_senha(void); void Eeprom_write(char addr, char data); unsigned char Eeprom_read(unsigned char addr); unsigned char fl_tec = 0, tec = 0; /* fl_tec: flag que indica que chegou uma tecla * tec: valor asc da tecla recebida */ void main(void) { unsigned char buffer[16]; unsigned char senha_digitada = 0, senha_eeprom = 0; inicia_regs(); config_int(); config_int0(); // configura as interrupções config_AD(); // configura e inicializa o conversor AD lcd_inicia(0x28, 0x0f, 0x06); // incializa o LCD com 4 linhas lcd_LD_cursor(0); // Desliga o cursor tranca = 1; // tranca inicialmente ativada while (1) { lcd_limpa_tela(); lcd_posicao(1, 1); imprime_string_lcd(" Cofre digital "); delay_ms(2000); senha_digitada = testa255(0); lcd_posicao(2, 1); sprintf(buffer, "Senha = %03d ", senha_digitada); imprime_buffer_lcd(buffer, 15); if (senha_digitada == 255) { troca_senha(); } else { senha_eeprom = Eeprom_read(0); if (senha_digitada == senha_eeprom) { tranca = 0; // abre tranca lcd_limpa_tela(); lcd_posicao(1, 1); imprime_string_lcd(" Destravado "); lcd_posicao(2, 1); imprime_string_lcd(" Porta Fechada "); while (!sensor_porta); //espera a porta abrir lcd_posicao(1, 1); imprime_string_lcd(" Porta Aberta "); lcd_posicao(2, 1); imprime_string_lcd("Feche p/travar "); while (sensor_porta); // aguarda porta fechar tranca = 1; // fecha tranca lcd_posicao(1, 1); imprime_string_lcd(" Cofre digital "); lcd_posicao(2, 1); imprime_string_lcd(" Porta Fechada "); delay_ms(2000); } else { lcd_limpa_tela(); lcd_posicao(1, 1); imprime_string_lcd("ERRO - Senha "); lcd_posicao(2, 1); imprime_string_lcd(" nao confere"); delay_ms(2000); } } } } // função de configuração do conversor A/D void config_AD(void) { ADCON0 = 0b00000001; /* canal AN0 selecionado <5:2> 0000 Flag GO - /DONE desligado <1> 0 Módulo conversor ligado <0> 1 */ ADCON1 = 0b00111110; /* Vref- = GND <5> 0 RA2 <5> 1 Vref+ = VDD <4> 0 pino RA0/AN0 analógico e demais digitais <3:0> 1110 */ ADCON2 = 0b10110110; /* Resultado justificado a direita <7> 1 * Tempo de Aquisição de 16 TDA <5:3> 110 * TDA = 1,33us <2:0> 110 * 48MHz/64 = 750kHz = 1,33us */ } // função que efeuta uma conversão A/D e retorna um inteiro com o valor convertido int conv_AD(void) { int result_AD; // Variável local para armazenar o resultado da conv ADCON0bits.GO = 1; // inicia a conversão while (ADCON0bits.GO); // Aguarda a o fim da conversão result_AD = (((int) ADRESH) << 8) | (ADRESL); return result_AD; } // função que inicia os registradores I/O void inicia_regs(void) { // Configura todas as portas multiplexadas com o módulo conversor A/D, como I/O digital. (cap13) ADCON1 = 0x0F; TRISA = 0b00001101; TRISB = 0b01000001; // define a B0 como entrada (INT0) e B6 sensor da porta TRISD = 0x00; // define a porta D como saída - onde está o LCD TRISE = 0x00; // define a porta E como saída - onde está o Led que indica que passou pela interrupção PORTA = 0; PORTB = 0; PORTC = 0; PORTD = 0; PORTE = 0; // zeroa todos os pinos } // função de interrupção de alta prioridade void interrupt ISR_alta_prioridade(void) { delay_ms(100); // atraso de 0,1s // este atraso é importante para evitar pulos do teclado, pois o up roda muito rápido 48MHz int vc; // unsigned char buffer[16]; fl_tec = 1; tec = 0; vc = conv_AD(); lcd_posicao(2, 1); if (vc > 965 && vc < 1003) tec = 47; else if (vc > 929 && vc < 965) tec = 57; else if (vc > 899 && vc < 929) tec = 56; else if (vc > 865 && vc < 899) tec = 55; else if (vc > 821 && vc < 865) tec = 120; else if (vc > 755 && vc < 821) tec = 54; else if (vc > 737 && vc < 775) tec = 53; else if (vc > 665 && vc < 737) tec = 52; else if (vc > 575 && vc < 665) tec = 45; else if (vc > 509 && vc < 575) tec = 51; else if (vc > 453 && vc < 509) tec = 50; else if (vc > 369 && vc < 453) tec = 49; else if (vc > 260 && vc < 369) tec = 43; else if (vc > 163 && vc < 260) tec = 35; else if (vc > 70 && vc < 163) tec = 48; else if (vc > 20 && vc < 70) tec = 42; // sprintf(buffer, " Conv= %04d", vc); // imprime_buffer_lcd(buffer, 15); INTCONbits.INT0IF = 0; // Limpa o flag de ativação da interrupção } // função de configuração geral de interrupções void config_int(void) { // configurações gerais a todas as interrupçãoes RCONbits.IPEN = 1; // Habilita interrupção com nível de prioridade. End 0x08 - alta e 0x18 - baixo INTCONbits.GIEH = 1; // Habilita todas as interrupções de alta prioridade INTCONbits.GIEL = 0; // Desabilita todas as interrupções de baixa prioridade } // função de configuração da Interrupção externa 0 void config_int0(void) { // configurações específicas a INT0 INTCONbits.INT0IE = 1; // Ativa a inerrupção externa INT0 (RB0) INTCON2bits.INTEDG0 = 0; // Interrupção externa INT0 na borda de descida INTCONbits.INT0IF = 0; // Limpa o flag bit da interrupção externa INT0 } // função que gera atraso em mlisegundos void delay_ms(int i){ for ( int x = 0; x < i; x++ ) { __delay_ms(1); } } unsigned char testa255(unsigned char ns) { // ns = 0 escreve "senha" // ns = 1 escreve "senha 1" ou 2 escreve "senha 2" int pass = 999; unsigned char fl_testa = 1; while (fl_testa) { pass = le_3_tec(ns); if (pass > 255) { lcd_limpa_tela(); lcd_posicao(1, 1); imprime_string_lcd("ERRO"); lcd_posicao(2, 1); imprime_string_lcd("Senha > 255"); delay_ms(2000); } else fl_testa = 0; } return (char) pass; } /* * Função que lê 3 num do teclado de 0 a 9 * Backspace tecla * * OK tecla # Saída da função e retorna um num de 0 a 999 */ int le_3_tec(unsigned char ns1) { // ns1 = 0 escreve "senha" // ns1 = 1 escreve "senha 1" ou 2 escreve "senha 2" unsigned char i = 0, fl_3 = 1, num_asc[4] = {48, 48, 48, 48}; int num = 0; unsigned char buffer[16]; lcd_posicao(1, 1); if (ns1 != 0) sprintf(buffer, "Digite senha %d ", ns1); else sprintf(buffer, "Digite senha "); imprime_buffer_lcd(buffer, 15); // imprime_string_lcd("Digite senha "); lcd_posicao(2, 1); imprime_string_lcd(" * <- # OK "); imprime_buffer_lcd(buffer, 3); while (fl_3) { if (fl_tec) { fl_tec = 0; // desliga flag que avisa que tecla chegou // lcd_posicao(1, 14); // sprintf(buffer, "%d", i+1); // imprime_buffer_lcd(buffer, 1); if (tec == 35) { // tec # OK if (i == 1) { num_asc[0] = num_asc[2]; num_asc[2] = 48; //num_asc[1]=48; } else if (i == 2) { num_asc[0] = num_asc[1]; num_asc[1] = num_asc[2]; num_asc[2] = 48; } if (i < 4) i = 4; } else if (tec == 42) { // tec * Backspace if (i == 1) num_asc[2] = 48; if (i == 2) num_asc[1] = 48; if (i == 3) num_asc[0] = 48; if (i > 0) i--; else i = 0; } else if (i < 3) { if (tec < 58 && tec > 47) { // garante que só vale teclas de 0 a 9 para registrar num_asc[2 - i] = tec; i++; } } if (i == 4) { //se # - OK foi pressionado fl_3 = 0; num = (num_asc[2] - 48)*100 + (num_asc[1] - 48)*10 + (num_asc[0] - 48); // converte asc para num } lcd_posicao(2, 1); if (i == 0) sprintf(buffer, " "); if (i == 1) sprintf(buffer, "%d ", num_asc[2] - 48); if (i == 2) sprintf(buffer, "%d%d ", num_asc[2] - 48, num_asc[1] - 48); if (i == 3) sprintf(buffer, "%d%d%d", num_asc[2] - 48, num_asc[1] - 48, num_asc[0] - 48); imprime_buffer_lcd(buffer, 3); } } return num; } void troca_senha(void) { unsigned char fl_4 = 1; unsigned char buffer[16]; int senha1 = 0, senha2 = 0; while (fl_4) { lcd_limpa_tela(); lcd_posicao(1, 1); imprime_string_lcd("Troca de senha "); delay_ms(2000); lcd_posicao(1, 1); imprime_string_lcd("Digite "); lcd_posicao(2, 1); imprime_string_lcd("Senha 1 "); delay_ms(1000); senha1 = testa255(1); lcd_posicao(2, 1); sprintf(buffer, "Senha 1 = %03d ", senha1); imprime_buffer_lcd(buffer, 16); delay_ms(2000); lcd_posicao(1, 1); imprime_string_lcd("Digite "); lcd_posicao(2, 1); imprime_string_lcd("Senha 2 "); delay_ms(1000); senha2 = testa255(2); lcd_posicao(2, 1); sprintf(buffer, "Senha 2 = %03d ", senha2); imprime_buffer_lcd(buffer, 16); delay_ms(2000); if (senha1 == senha2) { if (senha1 == 255) { lcd_limpa_tela(); lcd_posicao(1, 1); imprime_string_lcd("ERRO senha "); lcd_posicao(2, 1); imprime_string_lcd(" Nao pode 255 "); delay_ms(2000); } else { lcd_limpa_tela(); lcd_posicao(1, 1); imprime_string_lcd("Senha 1=Senha 2"); lcd_posicao(2, 1); imprime_string_lcd("Gravando senha "); Eeprom_write(0, senha1); delay_ms(2000); fl_4 = 0; } } else { lcd_limpa_tela(); lcd_posicao(1, 1); imprime_string_lcd("ERRO de senhas "); lcd_posicao(2, 1); sprintf(buffer, " %03d <> %3d ", senha1, senha2); imprime_buffer_lcd(buffer, 16); delay_ms(2000); } } } void Eeprom_write(char addr, char data) { char intcon_temp; while (EECON1bits.WR); // se uma escrita estiver em andamento, aguarda EEADR = addr; // configura o endereço inicial da escrita EECON1bits.EEPGD = 0; // Seleciona área de mem = eeprom EECON1bits.CFGS = 0; // Seleciona área de mem = eeprom EECON1bits.WREN = 1; // escrita permitida (1)) EEDATA = data; // escreve no registrador EEDATA o dado a ser gravado intcon_temp = INTCON; // Guarda o valor do registrador INTCON INTCON = 0; // Erase INTCON e disable interrups EECON2 = 0x55; // senha de escrita em memória EECON2 = 0xAA; EECON1bits.WR = 1; // habilita a escrita , volta a zero no fim da op while (EECON1bits.WR); // aguarda o fim da escrita, pois leva 4ms INTCON = intcon_temp; // restaura o valor do registrador INTCON } unsigned char Eeprom_read(unsigned char addr) { EEADR = addr; // EEADR recebe o endereço passado para a função EECON1bits.EEPGD = 0; // Seleciona área de mem = eeprom EECON1bits.CFGS = 0; // Seleciona área de mem = eeprom EECON1bits.FREE = 0; // modo de apagamento de flash não selecionada (0) EECON1bits.WREN = 0; // escrita não permitida (0)) EECON1bits.RD = 1; // habilita opção de leitura, volta a zero no fim da op return (EEDATA); // retorna o conteúdo lido (de EEDATA) }