Comecei estudar C para pic com o CCS, desde Maio/2020, consegui fazer muita coisa com sensores, LCD funcionando sozinho, funções, endereço de memória, enfim sem dificuldades...
Estou programando um pic em uma placa que já existe, sem dificuldades, para aprendizado e já aproveitar e re-programar ela oficialmente corrigindo alguns erros do senhor que fez uns 15 anos atrás, porém enrrosquei em fazer o LCD e o Teclado funcionarem no mesmo port, e foi isso que ficou por último.
Até chegou a funcionar mas com falhas, números que não respondem, então fui tentando, tentando, tentando até que consegui fazer funcionar, colocando aquele delay_ms(1) que está no drive do teclado, mas quando coloquei no programa principal que já está 52% utilizado do PIC, não conseguia chamar o teclado só quando precisa, ele faz o programa principal travar pois deve ser aquela função tec=proctec(), ela fica o tempo todo verificando o teclado, e não só apenas quando alguem pressiona uma tecla, chegava travar o while. Voltei então pro programa pequeno que fiz, que estou utilizando de um livro, e agora nem ele funciona mais, só alguns números, e já tentei de diversas formas, ainda acho que o problema está com a velocidade que está sendo feita a leitura das colunas, por isso coloquei aquele atraso de 1ms e deu certo na primeira vez.
Alguem pode me ajudar por favor, o que estou fazendo errado?
Estou utilizando o CCS, o PIC é o 16F876, são 12 teclas e o LCD está em 8 bits.
As 4 linhas estão com um resistor de pull-up aos 5V, valor 100K, as 3 colunas vão direto ao PIC, sem resistor de pull-up.
Segue o drive do teclado "12teclas16F876.c"
cpp code
// informa ao compilador que linha1 é o mesmo que PIN_B1
#define linha2 PIN_B2 // informa ao compilador que linha2 é o mesmo que PIN_B2
#define linha3 PIN_B3 // informa ao compilador que linha3 é o mesmo que PIN_B3
#define linha4 PIN_B4 // informa ao compilador que linha4 é o mesmo que PIN_B4
#define coluna1 PIN_B5 // informa ao compilador que coluna3 é o mesmo que PIN_B5
#define coluna2 PIN_B6 // informa ao compilador que coluna2 é o mesmo que PIN_B6
#define coluna3 PIN_B7 // informa ao compilador que coluna1 é o mesmo que PIN_B7
char tec; // declara variável tec, tipo char
int1 prestec; // declara a variável prestec como booleana
/********************************* Key e LCD *********************************/
char scantec(void) // função scantec que determina qual a tecla foi pressionada // Varredura das teclas
{
char tec = ' '; // declara a variável local tec, e a inicializa com o valor ' ' (em branco)
///////////////////////////////////////////////////////////////////////////
output_high(coluna1); // ativa a coluna1. Houve necessidade de ser nessa ordem para funcionar corretamente as teclas
output_low(coluna2); // desativa a coluna2. Houve necessidade de ser nessa ordem para funcionar corretamente as teclas
output_low(coluna3); // desativa a coluna3. Houve necessidade de ser nessa ordem para funcionar corretamente as teclas
delay_ms(1); // a primeira vez que deu certo, só com esse delay, então fui pro código grande e travava, então voltei
// para o código pequeno de teste, e agora os números ficaram malucos..
if(!input(linha1)) tec='2'; // se o botão da linha 1 na coluna 1 for pressionado, faz tec = '1'
if(!input(linha2)) tec='5'; // se o botão da linha 2 na coluna 1 for pressionado, faz tec = '4'
if(!input(linha3)) tec='8'; // se o botão da linha 3 na coluna 1 for pressionado, faz tec = '7'
if(!input(linha4)) tec='0'; // se o botão da linha 4 na coluna 1 for pressionado, faz tec = '*'
///////////////////////////////////////////////////////////////////////////
output_high(coluna2); // ativa a coluna2. Houve necessidade de ser nessa ordem para funcionar corretamente as teclas
output_high(coluna1); // ativa a coluna1. Houve necessidade de ser nessa ordem para funcionar corretamente as teclas
output_low(coluna3); // desativa a coluna3. Houve necessidade de ser nessa ordem para funcionar corretamente as teclas
delay_ms(1); // a primeira vez que deu certo, só com esse delay, então fui pro código grande e travava, então voltei
// para o código pequeno de teste, e agora os números ficaram malucos..
if(!input(linha1)) tec='1'; // se o botão da linha 1 na coluna 2 for pressionado, faz tec = '2'
if(!input(linha2)) tec='4'; // se o botão da linha 2 na coluna 2 for pressionado, faz tec = '5'
if(!input(linha3)) tec='7'; // se o botão da linha 3 na coluna 2 for pressionado, faz tec = '8'
if(!input(linha4)) tec='*'; // se o botão da linha 4 na coluna 2 for pressionado, faz tec = '0'
///////////////////////////////////////////////////////////////////////////
output_high(coluna3); // ativa a coluna3. Houve necessidade de ser nessa ordem para funcionar corretamente as teclas
output_high(coluna2); // ativa a coluna2. Houve necessidade de ser nessa ordem para funcionar corretamente as teclas
output_high(coluna1); // ativa a coluna1. Houve necessidade de ser nessa ordem para funcionar corretamente as teclas
delay_ms(1); // a primeira vez que deu certo, só com esse delay, então fui pro código grande e travava, então voltei
// para o código pequeno de teste, e agora os números ficaram malucos..
if(!input(linha1)) tec='3'; // se o botão da linha 1 na coluna 3 for pressionado, faz tec = '3'
if(!input(linha2)) tec='6'; // se o botão da linha 2 na coluna 3 for pressionado, faz tec = '6'
if(!input(linha3)) tec='9'; // se o botão da linha 3 na coluna 3 for pressionado, faz tec = '9'
if(!input(linha4)) tec='#'; // se o botão da linha 4 na coluna 3 for pressionado, faz tec = '#'
output_low(coluna1); // desativa a coluna3. Houve necessidade de ser nessa ordem para funcionar corretamente as teclas
output_low(coluna2); // desativa a coluna2. Houve necessidade de ser nessa ordem para funcionar corretamente as teclas
output_low(coluna3); // desativa a coluna1. Houve necessidade de ser nessa ordem para funcionar corretamente as teclas
delay_ms(1); // a primeira vez que deu certo, só com esse delay, então fui pro código grande e travava, então voltei
// para o código pequeno de teste, e agora os números ficaram malucos..
return(tec); // retorna o valor de 'tec'
}
char proctec() // função proctec para o processamento dos botões pressionados
{
int t; // declara a variável 't' do tipo int 8 bits
t=scantec(); /* chama a função scantec();
* verifica se há uma tecla pressionada e guarda na variável 't' o que retornar */
if((t!=' ')&&(!prestec)) // se houver tecla pressionada (t!) e 'prestec' estiver em 0 (nivel baixo), faz o comando abaixo
/* este comando if((t!='')&&(!prestec)) tem função anti-bouncing, que aqui é feito por
* software. Seu objetivo é evitar os efeitos do rebatimento dos contatos das teclas */
{
prestec=TRUE; // inicializa a variável prestec com o valor 1, ou seja, informa que há tecla pressionada
if(t!=tec) // se a tecla atual for diferente da anterior
{
delay_ms(10); // atraso de 10ms
if(scantec()==t) // lê novamente as teclas e verifica se a mesma tecla ainda está pressionada
return (t); // se a mesma tecla ainda estiver pressionada, retorna o caractere da tecla
}
}
prestec=FALSE; // zera a variável 'prestec'
}
Segue o drive do display em 8 bits "display_8bits16f876.c"
cpp code
#define rs PIN_C4 // define que o bit 4 do port C se chamará RS: 1(dado) 0(instrução)
#define en PIN_C5 // define que o bit 5 do port C se chamará EN: Descida de 5V para 0V, habilita escrita
#define data output_b // define o portB inteiro como sendo onde serão enviados os dados
void display_cmd(int cmd) // função para envio de comandos para o display
{
data(cmd); // coloca o conteúdo da variável cmd no portD
output_high(en);// leva o pino en (pino 1 do port e) para nivel alto
output_low(en); // retorna o pino en para nível baixo
}
void display_send_byte(short int level, int data) // função para envio de dados ou escrita para o display
{
output_bit(rs,level); // coloca o pino rs no nivel da variavel level
delay_us(100); // atraso de 100microssegundos
display_cmd(data); // chama a função display_cmd que já tem os dados a serem enviados para o port D
}
void display_pos_xy(int x, int y)// função para o posicionamento do cursor
{
int address; // variavel que informa o endereço(posicionamento) do cursor
if (y!=1) // se o valor de y for 2 (se não for 1) faz o comando abaixo
address=0xc0;// atribui 0xc0 à address, "Primeira posição da Segunda linha"
else // se não ou caso contrário
address=0x80;// atribui 0x80 à address, "Primeira posição da Primeira linha"
address+= x-1; // decrementa o valor da variavel x e o restuldado é somado ao conteudo da variavel
// address. Seria o mesmo que address = address + (x-1)
display_send_byte(0,address); /* Chama a função display_send_byte com o valor 0, informando para
* o display que será enviado um comando, que é a variável address.
* Por exemplo: se x=0x10 (o decimal 16) e y=1, o caractere deverá
* ser escrito na coluna 15(ultima coluna) da linha 1 do display,
* ou seja: 16-1=15 ou 0x8f. Por outro lado, se x=0x03 e y=2, então
* o caractere deverá ser escrito na coluna 2 (terceira coluna) da
* linha 2 do display, ou seja: 3-1=2 ou 0xc2. Esta função só é usada
* quando se deseja escrever a partir de uma coluna que não seja a
* primeira, da 1ª ou da 2ª linha, do display. */
}
void write_display(char c) // função para envio dos comandos e/ou dados para o display
{
switch(c) //declaração de controle switch com a variável C
{
case '\f' : display_send_byte(0,1); // Caso 'c' seja '\f', o dado 1 será enviado ao display
// para apagar todo o seu conteúdo
delay_us(1600); // atraso de 1600 microssegundos
break; // sai do switch e não testa mais nenhum outro caso
case '\n' : // caso 'c' seja '\n'
case '\r' : display_pos_xy(1,2); // caso 'c' seja '\r' muda o cursor para a 2ª linha do display
break; // sai do switch e não testa mais nenhum outro caso
case '\b' : display_send_byte(0,0x10);// caso 'c' seja '\b' desloca o cursor para a esquerda
break; // sai do switch e não testa mais nenhum outro caso
default : display_send_byte(1,c); /* Caso 'c' seja um caractere qualquer, então esse caractere será
* escrito no display pela função "display_send_byte". note-se que o
* bit rs, desta vez, está em nível 1, pois se trata de um caractere a
* ser escrito no display e não de um comando*/
}
}
void display_ini() // função de inicialização do display
{
output_low(rs); // coloca o pino rs em nivel logico baixo
delay_ms(16); // aguarda 16ms para garantir que a instrução anterior foi concluida
display_cmd(0x30); // envia o comando 0x30 para o display
delay_us(4500); // aguarda 4500us para garantir que a instrução anterior foi concluida
display_cmd(0x30); // envia o comando 0x30 para o display
delay_us(110); // aguarda 110us para garantir que a instrução anterior foi concluida
display_cmd(0x30); // envia o comando 0x30 para o display
delay_us(45); // aguarda 45micrrosegundos para garantir que a instrução anterior foi concluida
display_cmd(0x38); // envia o comando para a comunicação em 8 bits, display com 2 linhas e matriz 7x5
display_cmd(0x01); // envia comando para limpar o display e enviar o cursor para 1ª coluna da 1ª linha
delay_us(1600); // aguarda 1,6milissegundos para garantir que a instrução anterior foi concluida
display_cmd(0x0c); // envia comando para ligar o display sem cursor
display_cmd(0x06); // envia comando para escrever com deslocamento para a direita
// aqui deveria haver um tempo de 45us, e não foi incluida, pois foi constatado na prática, que ela
// introduzia falhas no funcionamento do driver
}
Este é o código principal de testes, o leve, não consigo mais fazer o teclado funcionar corretamente, e já funcionou a uns dias atrás.
Se gravo uma firmware que tenho aqui nesta placa, o teclado funciona normal, isso já descarta a placa.
cpp code
#include <16F876a.h> // diretiva que define o pic utilizado, arquivo de cabeçalho, arquivo header
//#case // diretiva que define a padronização ANSI
#use delay(clock=10000000) // frequencia para calculos e atrasos
#fuses HS // utiliza cristal >4mhz
#fuses NOWDT // desativa reset por watch dog
#fuses PUT // aguarda 72ms para estabilizar o circuito elétrico e então liga o PIC
#fuses BROWNOUT // reset por tensão <4.5v
#fuses NOLVP // desativa gravação por baixa tensão
//#fuses PROTECT // protege a firmware contra cópia/leitura
//#fuses CPD // progete a eeprom contra cópia
#use rs232(baud=9600, xmit=PIN_C6, rcv=PIN_C7, parity=N, bits=8, stop=1, ERRORS)
#bit tmr0if = 0x0b.2 /* define que o bit2 do registrador INTCON/Timer0 se chamará tmr0if
* 1 - o timer0 transbordou/estourou
* 0 - o timer0 não transbordou/estourou */
#bit RBPU = 0x81.7 // Bit de habilitação do pull up no port B do PIC16F876 ( não serviu pra nada)
#bit RBPB = 0x181.7
#use fast_io (a)
//#use standard_io (b)
#use fast_io (c)
#include "display_8bits16f876.c" // biblioteca do display
#include "12teclas16F876.c" // biblioteca do teclado
/******************************************************************************/
#define LED_RED PIN_C0
#define LED_GREEN PIN_C1
#define RELE1 PIN_C2
#define RELE2 PIN_C3
#define BEEP PIN_C4
/******************************************************************************/
////////////////////////////////////////////////////////////////////////////////
// protótipos de função ////////////////////////////////////////////////////////
void boot(); // função de exibição, beep e testes, que ocorre só ao ligar
////////////////////////////////////////////////////////////////////////////////
// variable global /////////////////////////////////////////////////////////////
int conta=0; // variável global para posicionamento do cursor no LCD
////////////////////////////////////////////////////////////////////////////////
// Função principal ////////////////////////////////////////////////////////////
void main()
{
setup_timer_0(RTCC_INTERNAL|RTCC_8_BIT|RTCC_DIV_256);
set_timer0(11); // timer 0 iniciando em 11, prescaler dividindo por 256, para se ter 25.0880000 mili segundos
enable_interrupts(GLOBAL);
enable_interrupts(INT_RDA); // inicia o leitor
set_tris_a(0b10110000);
set_tris_c(0b00000000);
output_c(0b00000000);
//port_b_pullups(TRUE); // essencial habilitar o registrador RBPU referente ao pullups no portB, se não, o teclado não funciona
RBPU=0; /* setar o bit RBPU em 0 as teclas funcionam perfeitamente e é o mesmo que port_b_pullups(TRUE);
// * setar o bit RBPU em 1, os pullups são desabilitados e as teclas ficam todas desconfiguradas*/
RBPB=0;
display_ini(); // inicializa o display
boot(); // função de boot, versão, testes iniciais
prestec=0; // esta variável está na biblioteca do teclado, como do tipo booelana
while(TRUE)
{
//display_pos_xy(1,1);
printf(write_display,"\fTeste LCD ");
//display_pos_xy(1,2);
printf(write_display,"\fDigite: Senha ");
delay_ms(50); // usar aqui para não ficar piscando
tec=proctec(); // chama a função proctec que está na biblioteca do teclado e guarda o resultado na variável tec
if(prestec) // se presetec for verdadeiro, alguma tecla foi pressionada, faz o comando abaixo
{
if(conta>=8) // se a variável conta for maior ou igual a 8
{
conta=0; // zera a variável conta, ou seja, se der mais de 16 caracteres, limpa o display
display_pos_xy(9,2); // posiciona o cursor na coluna 1, na linha 2 do lcd
printf(write_display," "); // limpa onde estaria o numero digitado
display_pos_xy(9,2); // posiciona o cursor na coluna 1, na linha 2 do LCD
}
printf(write_display,"%c",tec); // escreve no display o caracter correspondente a tecla pressionada
printf("%c",tec); // escreve na saida serial o caractere correspondente a tecla pressionada
conta++; // incrementa a variável conta
}
prestec=0;
}
}
////////////////////////////////////////////////////////////////////////////////
// Função para boot, testes, exibição de versão ////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
void boot()
{
printf(write_display,"\f"); // começa limpando a linha 1
printf(write_display,"\n"); // começa limpando a linha 2
delay_ms(40);
printf(write_display,"\f Testes ");
printf(write_display,"\nModulo de Testes");
output_high(BEEP);
delay_ms(100);
output_low(BEEP);
delay_ms(2000);
printf(write_display,"\f Teclado e LCD ");
printf(write_display,"\nKey and LCD Port");
int beep_boot;
for(beep_boot=1; beep_boot<=3; beep_boot++)
{
output_high(BEEP);
delay_ms(80);
output_low(BEEP);
delay_ms(80);
}
delay_ms(1500);
printf(write_display,"\fPega Tecla Envia");
printf(write_display,"\nv_21.0305-2222TD");
output_high(LED_GREEN);
delay_ms(50);
output_low(LED_GREEN);
delay_ms(50);
output_high(RELE1);
delay_ms(250);
output_low(RELE1);
delay_ms(100);
output_high(LED_RED);
delay_ms(50);
output_low(LED_RED);
delay_ms(50);
int rele2_boot;
for(rele2_boot=1; rele2_boot<=2; rele2_boot++)
{
output_high(RELE2);
delay_ms(120);
output_low(RELE2);
delay_ms(80);
}
delay_ms(2000);
printf(write_display,"\f"); // começa limpando a linha 1
printf(write_display,"\n"); // começa limpando a linha 2
}
Comecei a gostar de programar(estudar sempre), mas agora estou frustrado com o que imaginava ser fácil!!!
Fico no aguardo de uma ajuda, obrigado!!!