Placa entrenadora PIC EBASIC

Introducción

Placa entrenadora PIC EBASIC
Placa entrenadora PIC EBASIC

PIC EBASIC es una placa entrenadora de bajo coste para poder iniciarse en el mundo de los PIC, reúne una serie de cualidades que nos permitirá practicar con ella a distintos niveles de dificultad, desde principiantes hasta niveles avanzados sin perder la cualidad de ser una herramienta de bajo coste.
Esta placa y los ejemplos que acompañan en artículo fueron desarrollados por Carlos "Dogflu66", quien los fue posteando en el foro TODOPIC. Carlos a autorizado la publicación de su trabajo en uControl.

Características

Se ha elegido como base de esta placa al microcontrolador PIC16F88, que mantiene una cierta compatibilidad con el pinour del PIC16F84, pero internamente posee una cantidad y variedad de módulos que lo hace casi único dentro de su categoría.

La placa contiene los siguientes elementos para poder interactuar con ella:
  • Display LCD de 16x2 caracteres con su ajuste de contraste.
  • Control de la retroiluminacion del LCD para hacer practicas con el modulo CCP del PIC.
  • Reloj calendario por I2C, con una batería no recargable de litio y LED indicador.
  • Dos pulsadores en placa.
  • Dos LED que se pueden controlar mediante el PIC.
  • Un resistor ajustable para poder hacer pruebas con el módulo ADC.
  • Conexión exterior para el puerto I2C y el Pin RA4 del ADC u otro uso como controlar servomotores, reles, etc.
  • Puerto serie para ser usado como RS-232.
  • Puerto ICSP que permite programar el PIC directamente en la placa.
  • Protección contra inversión de polaridad de la alimentación y medidas básicas contra interferencias.

El circuito

Este es el esquema electrico de la PIC EBASIC:



Circuito de la PIC EBASIC

Fotografías

Aqui ponemos un par de fotografías de la placa terminada:








Ejercicios de programación

Configuracion del PSI

Los ejemplos estan realizados con el entorno de trabajo del PIC SIMULATOR IDE,y compilados con su [BASIC (PSI)|compilador Basic].
Deberemos instalar el PIC SIMULATOR IDE, y configurar su entorno de trabajo como sigue:
  • En el menú Options seleccionar el PIC16F88 a utilizar mediante Select MIcrocontroller.
  • Elegir la frecuencia del cristal en MHz del microcontrolador, desde Options -> Change Clock Frequency.
  • Seleccionar el modo de trabajo del microcontrolador desde Option -> Configuration Bits.

Todo esto queda ya configurado para otras ocasiones, pero de todas formas cada vez que se entra en el PIC SIMULATOR IDE es conveniente el comprobar que Configuration Bits no ha cambiado, ya que es sensible a algunas modificaciones.
Y ya solo resta indicar en que se va a trabajar en [BASIC (PSI)|Basic] o ASM mediante Tools -> BASIC Compiler.
Estamos listos para comenzar. Si surgen dudas sobre en el entorno de programación, puede consultarse el artículo correspondiente en uControl. Y para comprender el [BASIC (PSI)|Basic], puede consultarse la [BASIC (PSI)|guia] sobre este lenguaje de programación.

Escribir "A" en el LCD

Este es el codigo a compilar y cargar en el microcontrolador:
 
'NOMBRE:16F88_PicEBasic
'MICRO: PIC16F88/8Mhz reloj interno
'Uso del LCD con bus de datos a 4Bit y sin pin de RW
'**********************************************************************
'----------Puertos del LCD---------------------------------------------
Define LCD_BITS = 4  'indicamos que el bus de datos del lcd sera de 4bit
Define LCD_DREG = PORTA  'el bus de datos sera el puerto A
Define LCD_DBIT = 0  'el bus de datos seran los 4 bit menos significativos del puerto A
Define LCD_RSREG = PORTB  'el bit de control RS sera del puerto A
Define LCD_RSBIT = 7  'se usara el RA7 como RS
Define LCD_EREG = PORTB  'el bit de control E sera del puerto A
Define LCD_EBIT = 6  'se usara el RA6 como E
'Define LCD_RWREG = PORTA 'el bit de control RW sera del puerto A (si se suprime el compilador no  protesta
'Define LCD_RWBIT = 5 'se usara el RA5 como RW (si se suprime el compilador no protesta)
Define LCD_COMMANDUS = 2000  'Tiempo de espera despues de ejecutar un comand del lcd en uSeg.
Define LCD_DATAUS = 50  'Tiempo de espera despues de enviar un dato al LCD en uSeg.
Define LCD_INITMS = 5  '(50) 'Tiempo de espera despues de inicializar el Display, solo se ejecuta  una vez en Seg.
'---------------------------------------------------------------
'Define SIMULATION_WAITMS_VALUE = 1  'suprime los tiempos de espera Waitms, solo activar esta linea en simulacion
'---Definicion de puertos y registros----------------------------
ANSEL = 0x00  'los pin I/O digitales (no analogicos)
OSCCON = 0x7e  'set intrc To 8mhz, se usara reloj interno a 8Mhz
CMCON = 0x07  'comparador a off
TRISA = 0x00  'Puerto A como salidas
TRISB = 0x00  'puerto B como salidas
'---------------------Inicio-------------------------------------
Lcdinit  'inicializa el lcd sin cursor
WaitMs 500  'pausa para estabilizar
main:  'comienza el programa principal
    Lcdcmdout LcdClear  'borra el display
    WaitMs 1000  'hace una pausa de 1Seg.
    Lcdout "A"  'escribe el caracter "A" en el display
    WaitMs 1000  'hace una pausa de 1Seg.
Goto main  'impide que termine el programa principal
End  'es conveniente ponerlo siempre segun el manual

El resultado es una letra "A" que parpadea en la primer posición de la primer fila del display LCD. Permanece durante un segundo, desaparece durante otro segundo, y vuelve a comenzar.


Teclas y LEDs

Este ejemplo simplemente lee el estado de las teclas, mostrando en el display LCD el estado de las mismas, a la vez que enciende el LED correspondiente:


'************ PIC Entrenadora BASIC (PicEBasic)******************************
'NOMBRE: 16F88_LCD_I/O
'VERSION: 1.0
'MICRO: PIC16f88A
'FECHA/AUTOR: 12/06 - By COS
'DESCRIPCION: MUESTRA EN EL DISPLAY EL ESTADO DE LA TECLA ACTIVANDO EL LED ASOCIADO
'*****************************************************************************
'-------------------tos del LCD-----------------------------------------------
Define LCD_BITS = 4  'indicamos que el bus de datos del lcd sera de 4bit
Define LCD_DREG = PORTA  'el bus de datos sera el puerto A
Define LCD_DBIT = 0  'el bus de datos seran los 4 bit menos significativos del puerto A
Define LCD_RSREG = PORTB  'el bit de control RS sera del puerto B
Define LCD_RSBIT = 7  'se usara el RB7 como RS
Define LCD_EREG = PORTB  'el bit de control E sera del puerto B
Define LCD_EBIT = 6  'se usara el RB6 como E
'Define LCD_RWREG = PORTx  'el bit de control RW sera del puerto x(no se usa)
'Define LCD_RWBIT = n  'se usara el Rxn como RW (no se usa)
'
Define LCD_COMMANDUS = 2000	'Tiempo de espera despues de ejecutar un comand del lcd en uSeg.
Define LCD_DATAUS = 50	'Tiempo de espera despues de enviar un dato al LCD en uSeg.
Define LCD_INITMS = 50  'Tiempo de espera despues de inicializar el Display, solo se ejecuta una vez en mSeg.
'
'*****************DEFINICION DE VARIABLES**************************************
'--------------------Variables Programa----------------------------------------
Dim tecla_s1 As Byte  'guarda el estado de la tecla hasta terminar la maniobra
Dim tecla_s2 As Byte  'guarda el estado de la tecla hasta terminar la maniobra
'
'------------------------ASIGNACIONES------------------------------------------
tecla_s1 = 0
tecla_s2 = 0
'
'********************DEFINICION DE PUERTOS*************************************
'-----ASIGNACION DE I/O y valores de inicio de las salidas---------------------
ANSEL = %00000000  'los pin I/O digitales
CMCON = 0x07 'comparador a off
OSCCON = 0x7e 'set intrc To 8mhz, se usara reloj interno a 8Mhz
TRISA = 0x00 'Puerto A como salidas
TRISB = 0x00 'puerto B como salidas
TRISA.4 = 1 'como entrada (RA4, adc)
TRISA.6 = 1 'como entrada (RA6, tecla S1)
TRISA.5 = 1 'como entrada (RA5, tecla S2)
PORTB.3 = 1 'luz lcd a on (RB3)
PORTA.7 = 1 'led amarillo a off
PORTB.0 = 1 'led verde a off
'
'******************Inicio Programa*********************************************
Lcdinit  'inicializa el lcd sin cursor
WaitMs 1000  'espera 1Seg
Lcdout "16f88_LCD_I/O"  'escribe en el lcd
Lcdcmdout LcdLine2Home  'seleciona la linea dos como proxima para escritura
Lcdout "Pruebas con I/O"  'escribe en el lcd
WaitMs 3000  'espera 3Seg
'------------------Main Programa-----------------------------------------------
main:
  If RA6 = 0 Then tecla_s1 = 1  'lee el estado de la tecla S1
  If RA5 = 0 Then tecla_s2 = 1  'lee el estado de la tecla S2
  Gosub print_lcd  'salto con retorno, rutina que imprime los valores en el lcd
  WaitMs 100
  If tecla_s1 = 1 Then  'si la tecla S1 fue pulsada
    Toggle RA7  'invierte el estado de RA7, led amarillo
    tecla_s1 = 0  'tecla queda en reposo
  Endif
  If tecla_s2 = 1 Then  'si la tecla S2 fue pulsada
    Toggle RB0  'Invierte el estado de RB0, led verde
    tecla_s2 = 0  'tecla queda en reposo
  Endif
  WaitMs 100  'hace una pausa de 100mSeg
Goto main
End                                               
'---------------------FIN DEL MAIN---------------------------------------------
'
'*********************Subrrutinas**********************************************
print_lcd:  '____________________muestra los datos por el display
  Lcdcmdout LcdLine1Clear  'borra la primera linea y cursor al principio
  Lcdout "  S1 ", #tecla_s1, " -- S2 ", #tecla_s2  'imprime en el lcd
  Lcdcmdout LcdLine2Clear  'borra la segunda linea y cursor al principio
  Lcdout "----------------"  'muestra los datos en el lcd
REturn                                            
End

DS1307: Un reloj en tu LCD

El siguiente ejemplo muestra como implementar un reloj utilizando el DS1307. Recordemos que los registros de dicho circuito integrado se leen y escriben en formato BCD.


'************ PIC Entrenadora BASIC (PicEBasic)******************************
'NOMBRE: 16F88_LCD_RTC
'VERSION: 1.0
'MICRO: PIC16f88A
'FECHA/AUTOR: 12/06 - By COS
'DESCRIPCION: MUESTRA EN EL DISPLAY LA FECHA Y LA HORA
'para transferir la hora al reloj dejar pulsada la tecla S1
'****************************************************************************
'--------------------Puertos del LCD-----------------------------------------
Define LCD_BITS = 4  'indicamos que el bus de datos del lcd sera de 4bit
Define LCD_DREG = PORTA  'el bus de datos sera el puerto A
Define LCD_DBIT = 0  'el bus de datos seran los 4 bit menos significativos del puerto A
Define LCD_RSREG = PORTB  'el bit de nrol RS sera del puerto B
Define LCD_RSBIT = 7  'se usara el RB7 como RS
Define LCD_EREG = PORTB  'el bit de nrol E sera del puerto B
Define LCD_EBIT = 6  'se usara el RB6 como E
'Define LCD_RWREG = PORTx  'el bit de nrol RW sera del puerto x(no se usa)
'Define LCD_RWBIT = n  'se usara el Rxn como RW (no se usa)
Define LCD_COMMANDUS = 2000	'Tiempo de espera despues de ejecutar un comand del lcd en uSeg.
Define LCD_DATAUS = 50	'Tiempo de espera despues de enviar un dato al LCD en uSeg.
Define LCD_INITMS = 50  'Tiempo de espera despues de inicializar el Display, solo se ejecuta una vez en mSeg.
'
'---------------------Puerto I2C---------------------------------------------
Symbol sda = PORTB.1  'asignamos nombre al pin que hara de SDA del puerto I2C
Symbol scl = PORTB.4  'asignamos nombre al pin que hara de SCL del puerto I2C
'
'***************DEFINICION DE VARIABLES**************************************
'
'------------------Variables Programa----------------------------------------
Dim aux_4h As Byte  'contendra el nibble alto del dato leido por el I2C
Dim aux_4l As Byte  'contendra el nibble bajo del dato leido por el I2C
Dim i As Byte  'variable indice
Dim reg(7) As Byte  'contendra los valores finales de fecha y hora en el sistema decimal
'
'----------------------ASIGNACIONES------------------------------------------
'******************DEFINICION DE PUERTOS*************************************
'-------------ASIGNACION DE I/O y valores de inicio de las salidas-----------
ANSEL = %00000000 'los pin I/O digitales
CMCON = 0x07 'comparador a off
OSCCON = 0x7e 'set intrc To 8mhz, se usara reloj interno a 8Mhz
TRISA = 0x00 'Puerto A como salidas
TRISB = 0x00 'puerto B como salidas
TRISA.4 = 1 'como entrada (RA4, adc)
TRISA.6 = 1 'como entrada (RA6, tecla S1)
TRISA.5 = 1 'como entrada (RA5, tecla S2)
PORTB.3 = 1 'luz lcd a on (RB3)
PORTA.7 = 1 'led amarillo a off
PORTB.0 = 1 'led verde a off
'
'***************Inicio Programa**********************************************
Lcdinit  'inicializa el lcd sin cursor
WaitMs 1000  'espera 1Seg
Lcdout "16f88LCD_RTC/I2C"  'escribe en el lcd
Lcdcmdout LcdLine2Home  'seleciona la linea dos como proxima para escritura
Lcdout "Pruebas con I2C"  'escribe en el lcd
WaitMs 3000  'espera 3Seg
Lcdcmdout LcdClear
'--------------------inicializacion del reloj---------------------------------
'
'------------Aqui escribimos los valores para cargar al RTC-------------------
If RA6 = 0 Then  'si la tecla S1 esta pulsada pasaremos los datos al reloj
 I2CWrite sda, scl, 0xd0, 0, 0x00  'segundos, escrito en hex, porque coincide en este caso con BCD del RTC
 I2CWrite sda, scl, 0xd0, 1, 0x59  'minutos
  I2CWrite sda, scl, 0xd0, 2, 0x23  'horas
  I2CWrite sda, scl, 0xd0, 3, 0x01  'dia de la semana
  I2CWrite sda, scl, 0xd0, 4, 0x31  'dia del mes
  I2CWrite sda, scl, 0xd0, 5, 0x12  'mes
  I2CWrite sda, scl, 0xd0, 6, 0x06  'año
  I2CWrite sda, scl, 0xd0, 7, 0x10  'salida a 1hz
Endif
'
'-----------------------Main Programa-----------------------------------------
main:
  Gosub print_lcd  'actualiza la hora
  WaitMs 100  'pausa de 100mSeg.
Goto main
End
'
'--------------------------FIN DEL MAIN----------------------------------------
'
'**********************Subrrutinas*********************************************
'
'lee la hora del RTC, la pasa a decimal y la envia al lcd
print_lcd:  
  i = 0
  While i <= 6  'se repite el codigo comprendido entre while y wend, mientras i sea menor o igual a 6
    I2CRead sda, scl, 0xd0, i, reg(i)  'lectura de la direccion de memoria indicada del RTC, formato BCD
    aux_4l = reg(i) And %00001111  'eliminamos los bit de configuracion del RTC y dejamos solo el nibble bajo
    If i = 0 Then aux_4h = reg(i) And %01110000  'segundos, borrado de bit configuracion y almacena nibble alto
    If i = 1 Then aux_4h = reg(i) And %11110000  'minutos, almacena nibble alto
    If i = 2 Then aux_4h = reg(i) And %10110000  'horas, borrado de bit configuracion y almacena nibble alto
    If i = 3 Then aux_4h = reg(i) And %11110000  'dia_semana, almacena nibble alto
    If i = 4 Then aux_4h = reg(i) And %11110000  'dia_mes, almacena nibble alto
    If i = 5 Then aux_4h = reg(i) And %11110000  'mes, almacena el nibble alto
    If i = 6 Then aux_4h = reg(i) And %11110000  'año, almacena el nibble alto
    aux_4h = ShiftRight(aux_4h, 4)  'convierte el nibble alto en bajo
    reg(i) = (aux_4h * 10) + aux_4l  'fusiona los nibbles alto y bajo en una sola variable en formato decimal
    i = i + 1  'incrementa la variable indice con cada repeticion del bucle While/Wend
  Wend  'fin del bucle y salta a While
  Lcdcmdout LcdLine1Home  'cursor del lcd a la izquierda en la linea 1
  If reg(2) < 10 Then Lcdout "0"  'si horas es inferior a 10 escribe el cero delante
  Lcdout #reg(2), ":"  'escribe la hora y el separador
  If reg(1) < 10 Then Lcdout "0"  'si minutos es inferior a 10 escribe el cero delante
  Lcdout #reg(1), ":"  'escribe los minutos y el separador
  If reg(0) < 10 Then Lcdout "0"  'si segundos es inferior a 10 escribe el cerodelante
  Lcdout #reg(0)  'escribe los segundos
  Lcdcmdout LcdLine2Home  'cursor del lcd a la izquierda en la linea 2
  If reg(3) = 1 Then Lcdout "Dom"  'asigna el literal al dia de la semana
  If reg(3) = 2 Then Lcdout "Lun"
  If reg(3) = 3 Then Lcdout "Mar"
  If reg(3) = 4 Then Lcdout "Mie"
  If reg(3) = 5 Then Lcdout "Jue"
  If reg(3) = 6 Then Lcdout "Vie"
  If reg(3) = 7 Then Lcdout "Sab"
  Lcdout "/"  'escribe separador
  If reg(4) < 10 Then Lcdout "0"  'si dia del mes es inferior a 10 escribe cero
  Lcdout #reg(4), "/"  'escribe el dia del mes y el separador
  If reg(5) < 10 Then Lcdout "0"  'si mes es inferior a 10 escribe cero
  Lcdout #reg(5), "/20"  'escribe el mes, separador y los dos primeros digitos del año de 4 digitos
  If reg(6) < 10 Then Lcdout "0"  'si año es inferior a 10 escribe primero el cero
  Lcdout #reg(6)  'escribe el año
Return
End

LCD y ADC

Sin usar funciones

El siguiente ejemplo muestra como utilizar el conversor Analógico/Digital del PIC16F88 y como mostrar los resultados por el display LCD. Como siempre, el código esta perfectamente documentado:


'************* PIC Entrenadora BASIC (PicEBasic)******************************
'NOMBRE: 16F88_LCD_ADC
'VERSION: 1.0
'MICRO: PIC16f88A
'FECHA/AUTOR: 12/06 - By COS
'DESCRIPCION: MUESTRA EN EL DISPLAY EL VALOR DE LA ENTRADA 
'ANALOGICA CON DOS DECIMALES Y EN VOLTIOS
'*****************************************************************************
'---------------Puertos del LCD-----------------------------------------------
Define LCD_BITS = 4  'indicamos que el bus de datos del lcd sera de 4bit
Define LCD_DREG = PORTA  'el bus de datos sera el puerto A
Define LCD_DBIT = 0  'el bus de datos seran los 4 bit menos significativos del puerto A
Define LCD_RSREG = PORTB  'el bit de control RS sera del puerto B
Define LCD_RSBIT = 7  'se usara el RB7 como RS
Define LCD_EREG = PORTB  'el bit de control E sera del puerto B
Define LCD_EBIT = 6  'se usara el RB6 como E
'Define LCD_RWREG = PORTx  'el bit de control RW sera del puerto x(no se usa)
'Define LCD_RWBIT = n  'se usara el Rxn como RW (no se usa)
Define LCD_COMMANDUS = 2000  'Tiempo de espera despues de ejecutar un comand del lcd en uSeg.
Define LCD_DATAUS = 50  'Tiempo de espera despues de enviar un dato al LCD en uSeg.
Define LCD_INITMS = 50  'Tiempo de espera despues de inicializar el Display, solo se ejecuta una vez en mSeg.
'********************DEFINICION DE VARIABLES**************************************
'----------------Variables del ADC y Rutina division------------------------------
Dim num_1 As Word  'valor del adc y retorna la parte entera
Dim num_2 As Word  'factor de la escala y retorna el primer decimal
Dim num_3 As Word  'segundo decimal
Dim dvdo As Word  'dividendo operacion
Dim dvsor As Word  'divisor operacion
'------------------------Variables Programa----------------------------------------
'
'----------------------------ASIGNACIONES------------------------------------------
' 
'************************DEFINICION DE PUERTOS*************************************
'--------ASIGNACION DE I/O y valores de inicion de las salidas---------------------
ANSEL = %00000000  'los pin I/O digitales
Define ADC_SAMPLEUS = 0  'el minimo, configuracion del ADC
Define ADC_CLOCK = 5  '16Tad a 8Mhz = 2uSeg, minimo permitido por el micro 1.5uSeg, configuracion ADC
' 
'_______________registros afectados por Define ADC_CLOCK y ADCIN________________
'ANSEL = %00010000  'los pin I/O digitales y RA4 analogico
'ADCON0 = %01100000  'Fosc/16 a 8Mhz (ADCON1.ADCS2=1) =2uSeg (minimo 1.uSeg), channel RA4
'ADCON1 = %01000000  'voltage reference (AVdd/AVss) y seleccion de escala division clock/2 ADCON1.ADCS2=1
'
'_______________________________________________________________________________
CMCON = 0x07  'comparador a off
OSCCON = 0x7e  'set intrc To 8mhz, se usara reloj interno a 8Mhz
TRISA = 0x00  'Puerto A como salidas
TRISB = 0x00  'puerto B como salidas
TRISA.4 = 1  'como entrada (RA4, adc)
TRISA.6 = 1  'como entrada (RA6, tecla S1)
TRISA.5 = 1  'como entrada (RA5, tecla S2)
PORTB.3 = 1  'luz lcd a on (RB3)
PORTA.7 = 1  'led amarillo a off
PORTB.0 = 1  'led verde a off
' 
'*******************Inicio Programa*********************************************
Lcdinit  'inicializa el lcd sin cursor
WaitMs 1000  'espera 1Seg
Lcdout "16f88_LCD_ADC"  'escribe en el lcd
Lcdcmdout LcdLine2Home  'seleciona la linea dos como proxima para escritura
Lcdout "Pruebas con ADC"  'escribe en el lcd
WaitMs 3000  'espera 3Seg
'
'-------------------Main Programa-----------------------------------------------
main:
  Gosub lee_adc  'salto con retorno, rutina de lectura de la entrada ADC
  Gosub division_dos_decimales  'salto con retorno, rutina de conversion de valor adc a voltios
  Gosub print_lcd  'salto con retorno, rutina que imprime los valores en el lcd
  WaitMs 100  'hace una pausa de 100mSeg
Goto main
End                                               
'----------------------FIN DEL MAIN---------------------------------------------
' 
'**********************Subrrutinas**********************************************
'--------------------------------------Subrrutina lectura ADC, division y LCD
lee_adc:  '_______________________lee la entrada analogica
  Adcin 4, num_1  'lee el valor de la entrada analogica y lo carga en num_1
  num_2 = 205  'asigna factor de correccion, para 5V
Return                                            
'
'________Rutina division con dos decimales y conversion a voltios
division_dos_decimales:  
  dvdo = num_1  'asigna dividendo
  dvsor = num_2  'asigna divisor
  If dvdo > dvsor Then  'calcula la parte entera
    num_1 = dvdo / dvsor
    dvdo = dvdo Mod dvsor
  Else
    num_1 = 0
  Endif
  dvdo = dvdo * 10
  If dvdo > dvsor Then  'extrae el primer decimal
    num_2 = dvdo / dvsor
    dvdo = dvdo Mod dvsor
  Else
    num_2 = 0
  Endif
  dvdo = dvdo * 10
  If dvdo > dvsor Then  'extrae el segundo decimal
    num_3 = dvdo / dvsor
  Else
    num_3 = 0
  Endif	
Return  
'                                          
'____________________muestra los datos por el display
print_lcd:  
  Lcdcmdout LcdLine1Clear  'borra la primera linea y cursor al principio
  Lcdout "ENTR. ADC ", #num_1, ".", #num_2, #num_3, "V"  'muestra los datos en el lcd
  Lcdcmdout LcdLine2Clear  'borra la segunda linea y cursor al principio
  Lcdout "Lectura Pin RA4"  'muestra los datos en el lcd
Return

Usando funciones

Este es el programa modificado. Se han utilizado funciones, que estan disponibles en una de las licencias del PIC SIMULATOR IDE.

'********************************** PIC Entrenadora BASIC (PicEBasic)******************************
'NOMBRE: 16F88_LCD_ADC_Funcion_12
'VERSION: 1.2
'MICRO: PIC16f88A
'FECHA/AUTOR: 12/06, 13/07, 24/2/7 - By COS
'Version: 1.0
'DESCRIPCION: MUESTRA EN EL DISPLAY EL VALOR DE LA ENTRADA ANALOGICA CON DOS DECIMALES Y EN VOLTIOS
'Version: 1.1
'se acelera la rutina del display
'se implementa una rutina tipo funcion para la division
'version: 1.2
'simplificado de la rutina de division
'modificacion de la rutina print_lcd, para hacerla compatible con el nuevo formato salida de datos  de la division
'********************************************************************************************
Define SIMULATION_WAITMS_VALUE = 1
'-----------------------------Puertos del LCD-----------------------------------------------
Define LCD_BITS = 4  'indicamos que el bus de datos del lcd sera de 4bit
Define LCD_DREG = PORTA  'el bus de datos sera el puerto A
Define LCD_DBIT = 0  'el bus de datos seran los 4 bit menos significativos del puerto A
Define LCD_RSREG = PORTB  'el bit de control RS sera del puerto B
Define LCD_RSBIT = 7  'se usara el RB7 como RS
Define LCD_EREG = PORTB  'el bit de control E sera del puerto B
Define LCD_EBIT = 6  'se usara el RB6 como E
'Define LCD_RWREG = PORTx  'el bit de control RW sera del puerto x(no se usa)
'Define LCD_RWBIT = n  'se usara el Rxn como RW (no se usa)
Define LCD_COMMANDUS = 2000	'Tiempo de espera despues de ejecutar un comand del lcd en uSeg.
Define LCD_DATAUS = 50	'Tiempo de espera despues de enviar un dato al LCD en uSeg.
Define LCD_INITMS = 1  '50 'Tiempo de espera despues de inicializar el Display, solo se ejecuta una  vez en mSeg.
'***********************************************************************************************
Define SIMULATION_WAITMS_VALUE = 1
'**********************************DEFINICION DE VARIABLES**************************************
'------------------------------Variables del ADC y Rutina division------------------------------
Dim entero As Word	'parte entera
Dim decimal As Word	'los dos decimales
Dim division As Long  'almacenara el numero con los dos decimales
Dim adc_4 As Word  'contendra el valor de la entrada del ADC
Dim factor_escala As Word  'contiene el factor de correcion para pasar el valor del ADC a voltios,  max. 5V
'--------------------------------------Variables Programa----------------------------------------
'------------------------------------------ASIGNACIONES------------------------------------------
entero = 0
decimal = 0
division = 0
adc_4 = 0
factor_escala = 205  'factor de escala maximo 5V,en este caso
'**************************************DEFINICION DE PUERTOS*************************************
'----------------------ASIGNACION DE I/O y valores de inicio de las salidas---------------------
ANSEL = %00000000  'los pin I/O digitales
Define ADC_SAMPLEUS = 0  'el minimo, configuracion del ADC
Define ADC_CLOCK = 5  '16Tad a 8Mhz = 2uSeg, minimo permitido por el micro 1.5uSeg, configuracion  ADC
'_______________registros afectados por Define ADC_CLOCK y ADCIN________________
AN SEL = %00010000  'los pin I/O digitales y RA4 analogico
ADCON0 = %01100000  'Fosc/16 a 8Mhz (ADCON1.ADCS2=1) =2uSeg (minimo 1.uSeg), channel RA4
ADCON1 = %01000000  'voltage reference (AVdd/AVss) y seleccion de escala division clock/2 ADCON1.ADCS2=1
'_______________________________________________________________________________
CMCON = 0x07	'comparador a off
OSCCON = 0x7e 	'set intrc To 8mhz, se usara reloj interno a 8Mhz
TRISA = 0x00	'Puerto A como salidas
TRISB = 0x00	'puerto B como salidas
TRISA.4 = 1		'como entrada (RA4, adc)
TRISA.6 = 1		'como entrada (RA6, tecla S1)
TRISA.5 = 1		'como entrada (RA5, tecla S2)
PORTB.3 = 1		'luz lcd a on (RB3)
PORTA.7 = 1		'led amarillo a off
PORTB.0 = 1		'led verde a off
'**************************************Inicio Programa*********************************************
Lcdinit  'inicializa el lcd sin cursor
WaitMs 1000  'espera 1Seg
Lcdout "16f88_LCD_ADC"  'escribe en el lcd
Lcdcmdout LcdLine2Home  'seleciona la linea dos como proxima para escritura
Lcdout "Pruebas con ADC"  'escribe en el lcd
WaitMs 3000  'espera 3Seg
Lcdcmdout LcdClear  'borra el display, y cursor al principio de la primera linea
Lcdout "Lectura Pin RA4"  'muestra los datos en el lcd
'--------------------------------------Main Programa-----------------------------------------------
main:
 Gosub lee_adc  'salto con retorno, rutina de lectura de la entrada ADC
 division = dvsion_2dcmles(adc_4, factor_escala)  'llama a la funcion dvsion_2dcmles, que  retorna un long
 Gosub print_lcd  'salto con retorno, rutina muestra los datos por el LCD
 WaitMs 100  'hace una pausa de 100mSeg
Goto main
End
'--------------------------------------FIN DEL MAIN---------------------------------------------
'**************************************Subrrutinas**********************************************
'--------------------------------------Subrrutina lectura ADC, division y LCD
'_______________________lee la entrada analogica
lee_adc:  
  Adcin 4, adc_4  'lee el valor de la entrada analogica y lo guarda en adc_4
Return
'
'____________________muestra los datos por el display 
print_lcd:  
  entero = division.HW  'sede el word alto a entero, parte entera
  decimal = division.LW  'sede el word bajo a decimal
  Lcdcmdout LcdLine2Home  'selecciona la primera linea y cursor al principio
  Lcdout "ENTR. ADC ", #entero, "."  'muestra los datos en el lcd
  If decimal < 10 Then Lcdout "0"  'asegura que decimal siempre se escribira con dos digitos
  Lcdout #decimal, "V  "  'muestra los datos en el lcd
Return
' 
'____Rutina funcion division con  dos decimales
'____Argumentos de entrada: 2 word, dividendo y divisor, salida: variable tipo long
Function dvsion_2dcmles(dvdo As Word, dvsor As Word) As Long  
 Dim f_entera As Word  'variable local, contendra la parte entera
 Dim f_resto As Word  'varable local, contendra el resto
 Dim f_decimal As Word  'contendra la parte decimal
 f_entera = dvdo / dvsor  'obtengo la division
 f_resto = dvdo Mod dvsor  'obtengo el resto
 f_decimal = (f_resto * 100) / dvsor  'calculo los 2 decimales, para obtener 3 decimales multiplicar por 1000
 dvsion_2dcmles.HW = f_entera  'almacena la parte entera en el word alto
 dvsion_2dcmles.LW = f_decimal  'almacena la parte decimal word bajo
 'Recuerda que la parte decimal tiene que ser mostrada en el display con plantilla de dos digitos siempre
End Function
End

Video demostrativo

El siguiente video muestra el programa anterior funcionando:

TIMER1 y las Interrupciones del PIC

'***************** Placa Entrenadora **********************************
'NOMBRE:16F88_PicEBasic_LCD_4Bit_No_RW_Timer1
'MICRO: PIC16F88/8Mhz reloj interno
'Uso del LCD con bus de datos a 4Bit y sin pin de RW
'Activacion del Timer1 por rebose cada 1mSeg.
'implementacion de timer (bases de tiempos)por soft
'refresco del display segun la base de tiempos asignada
'cambio de estado del RA7 de forma ciclica (led intermitente)
'**********************************************************************
'-----------------Puertos del LCD--------------------------------------
Define LCD_BITS = 4  'indicamos que el bus de datos del lcd sera de 4bit
Define LCD_DREG = PORTA  'el bus de datos sera el puerto A
Define LCD_DBIT = 0  'el bus de datos seran los 4 bit menos significativos del puerto A
Define LCD_RSREG = PORTB  'el bit de control RS sera del puerto B
Define LCD_RSBIT = 7  'se usara el RB7 como RS
Define LCD_EREG = PORTB  'el bit de control E sera del puerto B
Define LCD_EBIT = 6  'se usara el RB6 como E
'Define LCD_RWREG = PORTA 'el bit de control RW sera del puerto A (si se suprime el compilador no protesta)
'Define LCD_RWBIT = 5 'se usara el RA5 como RW (si se suprime el compilador no protesta)
Define LCD_COMMANDUS = 2000  'Tiempo de espera despues de ejecutar un comand del lcd en uSeg.
Define LCD_DATAUS = 50  'Tiempo de espera despues de enviar un dato al LCD en uSeg.
Define LCD_INITMS = 5  '(50) 'Tiempo de espera despues de inicializar el Display, solo se ejecuta una vez en mSeg.
'------------------------------------------------------------------------
Define SIMULATION_WAITMS_VALUE = 1  'suprime los tiempos de espera Waitms, solo activar esta linea en simulacion
'---------------declaracion de variables---------------------------------
'******************variables de los timer********************************
Dim timer_base As Byte  'tiempo referencia para los timer por soft del programa
Dim timer_base_tiempo As Byte  'contiene el valor del tiempo del timer_base
Dim timer1_ms As Word  'base de tiempos en ms
Dim timer2_ms As Word  'base de tiempos en ms
Dim timer3_ms As Word  'base de tiempos en ms
Dim timer1_100ms As Byte  'base de tiempos msx100
Dim timer2_100ms As Byte  'base de tiempos msx100
Dim timer3_100ms As Byte  'base de tiempos msx100
Dim timer1_ms_tiempo As Word  'contiene el valor del tiempo del timer1_ms
Dim timer2_ms_tiempo As Word  'contiene el valor del tiempo del timer2_ms
Dim timer3_ms_tiempo As Word  'contiene el valor del tiempo del timer3_ms
Dim timer1_100ms_tiempo As Byte  'contiene el valor del tiempo del timer1_100ms
Dim timer2_100ms_tiempo As Byte  'contiene el valor del tiempo del timer2_100ms
Dim timer3_100ms_tiempo As Byte  'contiene el valor del tiempo del timer3_100ms
'-----------------------Variable General---------------------------------
Dim flag As Byte  'se utiliza para controlar el parpadeo de "A"
'---------asignacion de valores a las variables-------------------------- 
timer_base = 0
timer1_ms = 0
timer2_ms = 0
timer3_ms = 0
timer1_100ms = 0
timer2_100ms = 0
timer3_100ms = 0
timer_base_tiempo = 100  'cuenta 100mSeg
timer1_ms_tiempo = 5  'cuenta 5 mSeg.
timer2_ms_tiempo = 5
timer3_ms_tiempo = 5
timer1_100ms_tiempo = 1  'cuenta 0.1 Seg.
timer2_100ms_tiempo = 1
timer3_100ms_tiempo = 1
flag = 1  'flag=0 permite que se imprima "A", flag=1 permite que se borre el display
'-------------Definicion de puertos--------------------------------------
ANSEL = 0x00  'los pin I/O digitales (no analogicos)
OSCCON = 0x7e  'set intrc To 8mhz, se usara reloj interno a 8Mhz
CMCON = 0x07  'comparador a off
TRISA = 0x00  'Puerto A como salidas
TRISA.5 = 1  'como entrda, no permite ser usada como salida digital
TRISB = 0x00  'puerto B como salidas
'
'-----------Inicializacion de Interrupciones-----------------------------
T1CON.TMR1CS = 0  'asigna el reloj interno al timer1, se incrementa cada ciclo de instruccion
T1CON.T1CKPS0 = 0  'factor del preescales del timer1, 0
T1CON.T1CKPS1 = 0  'factor del preescales del timer1, 0
TMR1H = 0xf8  'carga el registro del contador para que desborde cada 1mSeg, byte alto
TMR1L = 0x30  'carga el registro del contador para que desborde cada 1mSeg, byte bajo
T1CON.TMR1ON = 1  'habilitacion del TMR1, comienza a incrementarce
'PIE1.TMR1IE = 1  'activa las interrupciones del timer1
INTCON.PEIE = 1  'bit de habilitacion de interrupciones de perifericos
Enable  'INTCON.GIE habilita todas las interrupciones globales
'-----------------Inicio------------------------------------------------
Lcdinit  'inicializa el lcd sin cursor
WaitMs 500  'pausa para estabilizar
Lcdout "Listo"  'muestra en el display
WaitMs 500  'pausa de 0,5Seg
timer1_ms_tiempo = 2
timer2_ms_tiempo = 3
PIE1.TMR1IE = 1  'activa las interrupciones del timer1
'------------------------------------rutinas del Programa----------------------
main:  'comienza el programa principal
  If timer1_ms >= timer1_ms_tiempo Then  'refresco el display cada timer1_ms_tiempo
    If flag = 1 Then Lcdcmdout LcdClear  'borra el display
    If flag = 0 Then Lcdout "A"  'escribe el caracter "A" en el display
    flag = flag + 1  'incrementa flag, controla el parpadeo de "A"
    If flag > 1 Then flag = 0  'hace reset al flag
      timer1_ms = 0  'reinicio el timer1_ms
    Endif
    If timer2_ms >= timer2_ms_tiempo Then  'cambio de estado el pin RA7 cada timer2_ms_tiempo
    Toggle RA7
    timer2_ms = 0  'reinicio el timer2_ms
  Endif
  Goto main  'impide que termine el programa principal
End  'es conveniente ponerlo siempre segun el manual
'-----------------------Interrupciones------------------------------------------
On Interrupt  'Comienzan las rutinas de las interrupciones, desactiva las interrupciones
  Save System  'Guarda los valores del sistema para poder reemprender el curso normal del programa
  If PIR1.TMR1IF = 1 Then  'comprueba que la interrupcion del timer1 es activa
  '********************base de tiempos********************************************  
    timer_base = timer_base + 1
    If timer1_ms < timer1_ms_tiempo Then timer1_ms = timer1_ms + 1
    If timer2_ms < timer2_ms_tiempo Then timer2_ms = timer2_ms + 1
    If timer3_ms < timer3_ms_tiempo Then timer3_ms = timer3_ms + 1
    If timer_base >= timer_base_tiempo Then
      If timer1_100ms < timer1_100ms_tiempo Then timer1_100ms = timer1_100ms + 1
      If timer2_100ms < timer2_100ms_tiempo Then timer2_100ms = timer2_100ms + 1
      If timer3_100ms < timer3_100ms_tiempo Then timer3_100ms = timer3_100ms + 1
      timer_base = 0
    Endif
  '*************************fin base de tiempos**********************************
  Endif
  TMR1H = 0xf8  'recarga el contador del timer1 para que desborde pasado 1mSeg, byte alto
  TMR1L = 0x30  'recarga el contador del timer1 para que desborde pasado 1mSeg, byte bajo
  PIR1.TMR1IF = 0  'borra el flag de salto del tmr1
Resume  'activa las interrupciones y retorna al curso normal del programa antes del salto
'
End