CONTROLANDO UM LCD 16X2 SEM BIBLIOTECAS “MÁGICAS” COM PIC16F628A
APRENDA A USAR O TIMER0


Nessa experiência será demonstrado o uso de um LCD tipo caracter com 16 colunas e 2 linhas (16x2), com sua configuração e uso.

A placa de estudos PE-PIC16F628A não possui um LCD “nativo”, nem mesmo pontos de conexão prontos para o mesmo. Porém é bem fácil realizar estas conexões e você vai precisar apenas de:

1 – LCD 16x2 sem nenhum tipo de placa adicional (LCD “puro”);
8 – cabinhos tipo jumper fêmea para fêmea com 20 cm para realizar as conexões entre o LCD e a placa;

Conecte os pinos 10 a 14 (D4 a D7) do LCD aos pontos 1 a 4 do conector PORTB_2 da placa, e os pontos 4 e 6 (R/S e EN, respectivamente) do LCD aos pontos 1 e 2 (RA0 e RA1) do conector PORTA da placa. A figura abaixo mostra estas conexões.

Obs. O VCC e GND do LCD devem ser conectados aos pontos +5V e GND da placa. Cuidado para não inverter estas ligações!

Confira todas as conexões antes de alimentar a placa! Não tenha pressa aqui!

O programa faz uso do TIMER0 para realizar a alteração da mensagem no LCD. Note que, o timer0 foi configurado para gerar uma interrupção a cada 8ms aproximadamente. Os registradores necessários para uso do timer0 estão basicamente dentro de INTCON e OPTION (já descrito em "Registradores).

Em OPTION deve-se zerar o bit TOCS para que o clock do timer0 seja retirado do sistema (PIC). Em seguida o bit PSA deve ser também zerado para que o prescaler seja desviado para o timer0. Os bits PS2, PS1 e PS0 devem ser configurados para que se consiga um divisor por 64.

Após isso o valor inicial da contagem é inserido no registrador TMR0. Dessa forma o timer contará de um valor até seu estouro (overflow) em 255H (o timer0 tem apenas 8 bits).

E finalmente em INTCON deve ser setado o bit T0IE que ativará a interrupção do timer0 por overflow (gera a int sempre que o timer0 chegar ao final da contagem).

Sempre que uma interrupção é gerada, a mesma é analizada dentro da função pic_isr() e se for uma interrupção do timer0 a função trata_int_timer0() será chamada. Na mesma a variável count_timer é incrementada até chegar a aproximadamente 2000ms (8ms x 250 = 2s). Sempre que essa variável chegar a 250 a mesma será zerada, e a variável nr_msg sofrerá uma alteração através da lógica XOR, alternando sempre entre 0 e 1 e em seguida a variável change_msg recebe o valor “1”. Dessa forma, dentro da função main, no laço while, é possível observar a alteração das variáveis e realizar o envio da mensagem correta para o LCD.

Note que dentro da função o valor inicial de TMR0 deve ser restabelecido. Sem isso não haverá uma próxima contagem!!!

A programação do LCD segue exatamente conforme descrito nos manuais da maioria dos fabricantes.

Esta experiência, assim como a anterior, tem alguns módulos (arquivos). Crie mesmos  conforme as listagens abaixo e os adicione em seu projeto. Grave o PIC e observe os resultados.


Listagem main.c

//******************************************************************************
// Projeto LCD para placa PE-PIC16F628A - teste de LCD 16x2
// Demonstra como enviar mensagens para um LCD 16 colunas 2 linhas
// Troca mensagens via timer0
//
// Desenvolvido por: Eng. Márcio José Soares
//
// V1.0 - 12/09/2022
//
// Compilador: XC8 v2.45
// IDE : MPLABX 6.0 Linux
// Plataforma: placa PE-PIC16F628A com PIC16F648A
// Gravador : PICKIT3
//
// Pinos utilizados:
// RA0 : EN
// RA1 : RS
// RB4 : D4 - LCD
// RB5 : D5 - LCD
// RB6 : D6 - LCD
// RB7 : D7 - LCD
//
// Obs.: Os jumpers JP2 e JP3 devem ser removidos!
//******************************************************************************

//******************************************************************************
// O Copyright deste programa está reservado para Márcio José Soares e seu
// conteúdo está protegido pela Lei de Direitos Autorais LEI Nº 9.610, de
// 19 de Fevereiro de 1998. É estritamente proibida a reprodução total ou
// parcial dos conteúdos aqui apresentados sem prévia autorização, por escrito,
// do autor.
//******************************************************************************

//******************************************************************************
// Bits de configuração
#pragma config FOSC = HS // Seleção do oscilador (Oscilador externo: cristal)
#pragma config WDTE = OFF // WDT desabilitado
#pragma config PWRTE = ON // Power-up Timer habilitado
#pragma config MCLRE = ON // reset externo via resistor em MCLR
#pragma config BOREN = OFF // Brown-out Detect BOD desabilitado
#pragma config LVP = OFF // Low-Voltage Programming desabilitado - I/O RB4
#pragma config CPD = OFF // EEPROM desabilitada
#pragma config CP = OFF // Flash desabilitada

//******************************************************************************
// Se nenhuma freqüência foi definida, assume 4MHz
// Esta definição é exigida para calibrar as funções __delay_us() e __delay_ms()
#define _XTAL_FREQ 4000000

//******************************************************************************
// Inclui arquivos
#include <xc.h>
#include <string.h>
#include "my_defines.h"
#include "pic_lcd.h"

//******************************************************************************
// Variáveis do módulo
volatile byte count_timer0 = 0;
volatile byte change_msg = 1;
volatile byte nr_msg = 0;

//******************************************************************************
// Funções externas do módulo
extern void clear_LCD(byte modo);
extern void mudalinha(char linha);
extern void config_LCD(void);
extern void stringR_LCD(char *msg);
extern void init_timer0(void);
//extern void I2C_Slave_Init(uint8_t address);

//******************************************************************************
// Funções do módulo

//******************************************************************************
// Função config pic - configura pinos de I/O e outros
//
// Entradas - nenhuma
// Saídas - nenhuma
//******************************************************************************
void config_pic(void){

PORTA = 0; // zera ports
PORTB = 0;

INTCON = 0x00; // Todas as ints desligadas
CMCON = 0x07; // desliga comparadores

TRISA = 0xFC; // Configura PORT A - entrada, exceto RA0 e RA1
TRISB = 0x00; // Configura PORT B - saída

//**************************************************************************
// Prepara LCD
res_lcden;
res_lcdrs;
PLCDDATA = 0x00;

//I2C_Slave_init(I2C_ADD); //inicia I2C no modo slave
config_LCD(); //configura LCD
clear_LCD(0); //apaga display - 2x para estabilizar

}

//******************************************************************************
// Função principal
//
// Entradas - nenhuma
// Saídas - nenhuma
//******************************************************************************
void main(void) {

char msg[NRCOLS+2];

config_pic(); // configura pic
__delay_ms(250); // aguarda estabilizar

clear_LCD(0); // limpa LCD
//**************************************************************************
// Flags de controle das interrupções
INTCONbits.PEIE = 1; // PEIE=1 - habilita int dos periféricos
// timers, comparadores, usart, etc)
INTCONbits.GIE = 1; // GIE=1 - habilita interrupções globais.

while(TRUE){
clear_LCD(0); // limpa LCD
strcpy(msg," PLACA ");
stringR_LCD(&msg[0]); // envia para LCD
strcpy(msg," PE-PIC16F628A");
mudalinha(2); // muda para linha 2
stringR_LCD(&msg[0]); // envia para LCD
__delay_ms(2000);
clear_LCD(0); // limpa LCD
strcpy(msg," by Arne ");
stringR_LCD(&msg[0]); // envia para LCD
strcpy(msg," 2022 ");
mudalinha(2); // muda para linha 2
stringR_LCD(&msg[0]); // envia para LCD
__delay_ms(2000);
}

return;
}




Listagem pic_timer0.c

//*****************************************************************************
// Arquivo pic_timer0.c - módulo de configuração e uso do timer0
// Desenvolvido por Márcio José Soares
//*****************************************************************************

//*****************************************************************************
// O Copyright deste programa está reservado para Márcio José Soares e seu
// conteúdo está protegido pela Lei de Direitos Autorais LEI Nº 9.610, de
// 19 de Fevereiro de 1998. É estritamente proibida a reprodução total ou
// parcial dos conteúdos aqui apresentados sem prévia autorização, por escrito,
// do autor.
//*****************************************************************************

//*****************************************************************************
// Arquivos incluídos no módulo
//*****************************************************************************
#include <xc.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "my_defines.h"

//*****************************************************************************
// Tipos definidos pelo usuário
//*****************************************************************************
#ifndef _MYDEFS_
#define _MYDEFS_
typedef unsigned char byte;
typedef unsigned char Uint8;
typedef unsigned short Uint16;
#endif

//*****************************************************************************
// Se nenhuma freqüência foi definida, assume 4MHz
// Esta definição é exigida para calibrar as funções __delay_us() e __delay_ms()
#ifndef _XTAL_FREQ
#define _XTAL_FREQ 4000000
#endif

//*****************************************************************************
// Variáveis externas do módulo
//*****************************************************************************
extern volatile byte count_timer0;
extern volatile byte change_msg;
extern volatile byte nr_msg;

//******************************************************************************
// Funções externas do módulo
//*****************************************************************************

//*****************************************************************************
// Funções do módulo
//*****************************************************************************
void init_timer0(void);
void trata_int_timer0(void);

//*****************************************************************************
// Função init_timer0 -inicia timer 0
//
// Entradas - nenhuma
// Saidas - nenhuma
//*****************************************************************************
void init_timer0(void){

//**************************************************************************
// Timer 0 habilitado em
// 4MHz/4 = 1MHz => 1MHz/64= 15.625Khz => T=1/15.625 = T=64us
// 64us x 125 = 8 ms - tempo de cada int do timer0
// para obter o valor de inicializaçãoo do timer deve-se fazer (256-125)=131

T0CS = 0; //clock retirado do sistema
PSA = 0; //prescaler desviado para timer0
PS0 = 1; //prescaler setado em 1:64
PS1 = 0;
PS2 = 1;
TMR0 = 0x83; //131 decimal
INTCON |= 0x20; //habilita int do timer 0
}

//*****************************************************************************
// Função trata_int_timer0 -trata chamada da int do timer 0
//
// Conta 8 horas para tempo ligado e 16 horas para tempo desligado
//
// Entradas - nenhuma
// Saidas - nenhuma
//*****************************************************************************
void trata_int_timer0(void){

count_timer0++;
//verifica se já ocorreram 250 interrupções
//8 ms x 250 = 2 segundos
if (count_timer0 >= 250)
{
count_timer0 = 0; // zera contador
nr_msg ^= 0x01; // muda mensagem
change_msg = 1; // autoriza troca
}

TMR0 = 0x83; // reestabelece valor inicial do contador

}



Listagem pic_ints.c

//*****************************************************************************
// Arquivo my_ints.c - tratamento das interrupções no PIC by Arne
// Desenvolvido por Eng. Márcio José Soares
//*****************************************************************************

//******************************************************************************
// O Copyright deste programa está reservado para Márcio José Soares e seu
// conteúdo está protegido pela Lei de Direitos Autorais LEI Nº 9.610, de
// 19 de Fevereiro de 1998. É estritamente proibida a reprodução total ou
// parcial dos conteúdos aqui apresentados sem prévia autorização, por escrito,
// do autor.
//******************************************************************************

//******************************************************************************
// Arquivos incluídos no módulo
//******************************************************************************
#include <xc.h>
//#include <pic16f648a.h>

//******************************************************************************
// Definições importantes no módulo
//******************************************************************************
#ifndef byte
#define byte uint8_t
#endif

//*****************************************************************************
// Funções externas do módulo
//*****************************************************************************
extern void trata_int_comm(void);
extern void putch(unsigned char mbyte);
extern void trata_int_timer0(void);

//*****************************************************************************
// Funções do módulo
//*****************************************************************************

//*****************************************************************************
// Função para tratar o vetor de interrupções no PIC
//
// Este é o tratamento de interrupçõe no PIC. Como este microcontrolador possui
// apenas um único vetor de interrupção (0x0004), toda e qualquer interrupção será
// tratada aqui. Se uma única interrupção for utilizada, o tratamento pode ser direto,
// sem a necessidade de se testar qual int ocorreu. Porém se mais de uma estiver
// em uso, a função deverá testar os flags das ints para saber qual ocorreu, extamente
// como é feito em assembly.
// O uso da diretriz "interrupt 0" é importante, pois é assim que o SDCC reconhece que
// esta função é na verdade um serviço para tratamento das interrupções. Para PICs usar
// apenas nível 0.
//
// Entradas - nenhuma
// Saídas - nenhuma
//*****************************************************************************
void __interrupt() pic_isr(void){

INTCON &= ~0x80; //desabilita ints

//analisa se é int do timer0
if((T0IF == 1) && (T0IE == 1)){ // é int do timer1
trata_int_timer0(); // trata int
T0IF = 0x00; // zera flag
}

//analisa se é int externa
//if((INTF == 1) && (INTE == 1)){ //é int externa
// trata_int_ext(); //trata int
//}

//analisa se é int da usart
//if((RCIF == 1) && (RCIE == 1)){ // é int de recepção da USART
// trata_int_comm(); // trata int
//}

INTCON |= 0x80; // habilita novamente as ints

}



Listagem pic_lcd.h

/* 
* File: pic_lcd.h
* Author: arne
*
* Created on 12 de Setembro de 2022, 14:56
*/

#ifndef PIC_LCD_H
#define PIC_LCD_H

//******************************************************************************
//Definições do módulo
//******************************************************************************

//******************************************************************************
// Se nenhuma freqüência foi definida, assume 4MHz
// Esta definição é exigida para calibrar as funções __delay_us() e __delay_ms()
#ifndef _XTAL_FREQ
#define _XTAL_FREQ 4000000
#endif

//******************************************************************************
//Modo de uso do LCD - selecionar apenas um
//******************************************************************************
//#define __LCD_4__ //modo de operação com 4 bits
//#define __LCD_8__ //modo de operação com 8 bits
#ifndef __LCDDEFS__
#define __LCDDEFS__
#define __LCD_4__

//******************************************************************************
// As definições a seguir são utilizadas para acesso aos pinos do display
// caso o pino RW não seja utilizado, comente a definição lcd_rw
//
// Algumas definições foram modificadas para compatibilizar com a PIC16F877
// definições modificadas (pinos de trabalho para o LCD)
//******************************************************************************

//******************************************************************************
// Endereços para linhas dos display's possíveis de se utilizar com este
// módulo:
//
// Display 1ª linha 2ª linha 3ª linha 4ª linha
// 16 x 2 80H C0H XX XX
// 16 x 4 80H C0H 94H D4H
// 20 x 4 80H C0H 90H D0H
// 40 x 2 80H C0H XX XX
//******************************************************************************

//*****************************************************************************
//Definição das portas e pinos de I/O para LCD
//*****************************************************************************
#define PLCDDATA PORTB
#define PLCDCTRL PORTA
#define pLCD_RS PORTAbits.RA1
#define pLCD_EN PORTAbits.RA0

//*****************************************************************************
// Macros para controle dos pinos de I/O
//*****************************************************************************
#define set_lcdrs pLCD_RS = 1 //liga pino RS
#define res_lcdrs pLCD_RS = 0 //desliga pino RS
#define set_lcden pLCD_EN = 1 //liga pino EN
#define res_lcden pLCD_EN = 0 //desliga pino EN

#define lcd_type 2 // 0=5x7, 1=5x10, 2=2 linhas
#endif

//***********************************************************************
// Funções do módulo
//***********************************************************************
void strobe(void);
void sendC(const char val);
void dataC(char val);
void commandC(char val);
void clear_LCD(byte modo);
void pos_LCD(byte pos);
void mudalinha(char linha);
void config_LCD(void);
void stringR_LCD(char *msg);

#endif /* PIC_LCD_H */



Listagem pic_lcd.c

//******************************************************************************
// Funções para uso de LCDs tipo caracter - modo transferência 4/8 bits
// Desenvolvido por Márcio José Soares
//
// Microcontrolador: AVR
// Compilador: avr-gcc (GCC) 4.1.0 - Linux
//
// Última alteração: 09/08/2007
//
//******************************************************************************

//******************************************************************************
// O Copyright deste programa está reservado para Márcio José Soares e seu
// conteúdo está protegido pela Lei de Direitos Autorais LEI Nº 9.610, de
// 19 de Fevereiro de 1998. É estritamente proibida a reprodução total ou
// parcial dos conteúdos aqui apresentados sem prévia autorização, por escrito,
// do autor.
//******************************************************************************

//******************************************************************************
// Inclui arquivos
#include "my_defines.h"
#include "pic_lcd.h"

//******************************************************************************
// Funções do módulo

//******************************************************************************
//Pulsa pino E (habilitação) no display
//Entradas - nenhuma
//Saidas - nenhuma
//******************************************************************************
void strobe(void){

	set_lcden;				//pulsa pino de controle - habilita
	res_lcden;
}

//******************************************************************************
//Envia um byte para o display - dados ou comando
//Entradas - byte a enviar
//Saidas - nenhuma
//******************************************************************************
void sendC(const char val){

    	__delay_us(250);
	PLCDDATA = val;
	strobe();				// strobe pino E

#if defined(__LCD_4__)				// se modo 4 bits, envia segundo nible
    	PLCDDATA = val<<4;			// envia nibble menos significativo
	strobe();				// mais um strobe
#endif

}

//******************************************************************************
//Envia dados para o display
//Entradas - dado a enviar
//Saidas - nenhuma
//******************************************************************************
void dataC(char val){

	set_lcdrs;                  		//RS em 1
	sendC(val);				//escreve dado

}

//******************************************************************************
//Envia comandos para o display
//Entradas - byte de comando
//Saidas - nenhuma
//******************************************************************************
void commandC(char val){

	res_lcdrs;				//RS em 0
	sendC(val);				//escreve comando
}

//******************************************************************************
//Apaga LCD
//Entradas - byte: 0 - apaga todo LCD; 1 
//                 1 - apaga linha 1; 
//                 2 - apaga linha 2.
//Saidas - nenhuma
//******************************************************************************
void clear_LCD(byte modo){

	byte i;
	switch(modo){
		case 0:
			commandC(0x01);		//apaga todo LCD
			break;
		case 1:
			commandC(0x80);		//muda para linha 1
			for (i=0; i<16; i++)	//envia 15 caracteres 'espaço'
				dataC(' ');
			commandC(0x80);		//muda para linha 1 novamente
			break;
		case 2:
			commandC(0xC0);		//muda para linha 2
			for (i=0; i<16; i++)	//envia 15 caracteres 'espaço'
				dataC(' ');
			commandC(0xC0);		//muda para linha 2 novamente
			break;
  default: //se valor passado não confere commandC(0x01); //apaga todo LCD } for (i=0; i<15; i++) __delay_us(250); // espera 15 ms } //****************************************************************************** //Posiciona cursor no LCD //Entradas - posição do cursor no LCD (1 - 32) //Saidas - nenhuma //****************************************************************************** void pos_LCD(byte pos){
if ((pos > 0) && (pos < 17)) //verifica se linha 1 commandC(0x80 + (pos)); //posiciona na linha 1 else{ pos -= 16; //tira uma linha commandC(0xC0 + (pos)); //posiciona na linha 2 } } //****************************************************************************** //Muda linha no LCD //Entradas - linha (1 ou 2) //Saidas - nenhuma //****************************************************************************** void mudalinha(char linha){ if (linha==1) commandC(0x80); //muda para linha 1 if (linha==2) commandC(0xC0); //muda para linha 2 } //****************************************************************************** //Inicializa LCD // //Entradas - nenhuma //Saidas - nenhuma //****************************************************************************** void config_LCD(void){
byte i; for (i=0; i< 100; i++) __delay_ms(4); // power on em espera de 400ms #if defined(__LCD_4__) commandC(0x28); // modo 4-bits, 2-linha, caracter 5x7 commandC(0x28); // repete comando #endif #if defined(__LCD_8__) commandC(0x38); // modo 8-bits, 2-linha, caracter 5x7 commandC(0x38); // repete comando #endif commandC(0x06); // incrementa caracter a direita, desliga display shift commandC(0x0F); // display ligado, cursor ligado, cursor blink clear_LCD(0); // apague display } //*********************************************************************** //Envia uma string para o LCD // //Entradas - ponteiro da string na memória RAM //Saidas - nenhuma //*********************************************************************** void stringR_LCD(char *msg){ while(*msg != 0x00) //envia enquanto msg for verdadeira dataC(*msg++); //envia caracter por caracter }

Copyright deste conteúdo reservado para Márcio José Soares e protegido pela Lei de Direitos Autorais LEI N° 9.610, de 19 de Fevereiro de 1998. É estritamente proibida a reprodução total ou parcial do conteúdo desta página em outros pontos da internet, livros ou outros tipos de publicações comerciais ou não, sem a prévia autorização por escrito do autor.