# ADC con PIC en assembler



## chugus (Sep 4, 2012)

Hola gente, estoy realizando un proyecto de un voltímetro digital con PIC programado en Assembler.

El circuito consta de un PIC 16f818 en el cual el PORTB destino a manejar tres display de 7 segmentos multiplexandolos con tres bits del PORTA. Se usa el conversor en 8 bits.

El dato entra por AN0, pasa por una rutina que lo convierte de binario a decimal separandolo en unidad, decena y centena, luego se llama a la rutina de displayado donde se envía el dato al PORTB multiplexando cada dígito.

Se dispone de un pote de 5K conectado directamente a la entrada analógica. Después de unas cuantas horas funciona a la perfección dando como resultasdo "000" cuando el pote está al minimo y "255" cuando el pote está al máximo.


Ahora bien, mi pregunta es la siguiente, dado que solo se programar en assembler, como hago para hacer el arreglo interno para que en la salida obtenga el valor real en voltios? 

Espero me puedan dar una mano, estuve trabajando mucho y creo que me falta muy poco..

Un saludo!


----------



## Melghost (Sep 4, 2012)

Hola.

     Pues sí, te falta muy poquito. Sólo tienes que hacer una multiplicación: Si alimentas a 5 voltios, tienes que transformar el 255 en 5, o en 500 por ejemplo (5.00 voltios). Como el PIC que utilizas no tiene instrucción de multiplicar, tendrás que programártela. Hay varios algoritmos. Por ejemplo, "AxB" puedes hacerlo con un bucle de "A" ciclos y así sumar B+B+B...

     En tu caso (si alimentas a 5 voltios, que no lo sé) tendrías que multiplicar por 1.96, ya que 255 x 1.96 = 500.

     Como no puedes meter decimales en un byte, puedes multiplicar por 196, y al valor de 2 bytes obtenido (255x196=49980) dividirlo entre 100 (499.80) o directamente pasarlo a decimal y quedarte con los 3 dígitos más significativos (499, que serían 4.99 voltios). Fíjate que es inevitable tener un poquito de inexactitud, salvo que quieras trabajar en coma flotante (y entonces ya tu proyecto se convertiría en un mega-proyecto...) 

Espero haberte ayudado.


----------



## chugus (Sep 4, 2012)

Si, eso es lo que intento hacer, muchas gracias por tus consejos me sirvieron para empezar a armar el código..

Un saludo!!!


----------



## Meta (Sep 5, 2012)

Hola:

Ahora voy a probarlo y te subo un vídeo haber si es lo que buscas. Eso si, este es un jemplo del 16F886.


```
;El módulo convertidor ADC. Interrupción trás la conversión. Voltímetro digital
;
;Este ejemplo visualiza sobre la pantalla LCD el valor obtenido por el convertidor a partir
;de una tensión analógica de entrada de entre 0 y 5Vcc que se aplica por la entrada RA0/AN0.
;A lo que indique el LCD se le debe multiplicar 0.004887 (resolución/bit) para obtener la
;tensión de entrada en RA0/AN0.

        List    p=16F886        ;Tipo de procesador
        include    "P16F886.INC"    ;Definiciones de registros internos

;Ajusta los valores de las palabras de configuración durante el ensamblado.Los bits no empleados
;adquieren el valor por defecto.Estos y otros valores se pueden modificar según las necesidades

        __config    _CONFIG1, _LVP_OFF&_PWRTE_ON&_WDT_OFF&_EC_OSC&_FCMEN_OFF&_BOR_OFF    ;Palabra 1 de configuración
        __config    _CONFIG2, _WRT_OFF&_BOR40V                                    ;Palabra 2 de configuración

            cblock    0x20            ;Inicio de variables de la aplicación
                Byte_L                ;Parte baja del byte a convertir
                Byte_H                ;Parte alta del byte a convertir
                BCD_2                ;Byte 2 de conversión a BCD
                BCD_1                ;Byte 1 de conversión a BCD
                BCD_0                ;Byte 0 de conversión a BCD
                Contador            ;Variable de contaje
                Temporal            
                Temporal_1
                Temporal_2            ;Variables temporales
            endc        

Lcd_var            equ    0x70            ;Variables de las rutinas LCD
    
                org    0x00
                goto    Inicio        ;Vector de reset
                org    0x04
                goto    Inter        ;Vector de interrupción
                org    0x05

;******************************************************************************************
;Según el valor contenido en el registro W, se devuelve el carácter a visualizar

Tabla_Mensajes    movwf    PCL        ;Calcula el desplazamiento sobre la tabla

;***********************************************************************************
;La directiva DT genera tantas intsrucciones RETLW como bytes o caracteres contenga

Mens_0            equ    $        ;Mens_0 apunta al primer carácter del mensaje 0
                dt    "AN0=    (*4.8mV)",0x00

        include    "LCD4bitsPIC16.inc"        ;Incluye rutinas de manejo del LCD

;*************************************************************************************
;Mensaje: Esta rutina envía a la pantalla LCD el mensaje cuyo inicio está  indicado en
;el acumulador. El fin de un mensaje se determina mediante el código 0x00

Mensaje            movwf      Temporal_1         ;Salva posición de la tabla
Mensaje_1          movf       Temporal_1,W       ;Recupera posición de la tabla
                   call       Tabla_Mensajes     ;Busca caracter de salida
                   movwf      Temporal_2         ;Guarda el caracter
                movf       Temporal_2,F
                btfss      STATUS,Z           ;Mira si es el último
                goto       Mensaje_2
                return
Mensaje_2       call    LCD_DATO           ;Visualiza en el LCD
                incf    Temporal_1,F       ;Siguiente caracter
                goto    Mensaje_1

;****************************************************************************************
;Inter: Programa de tratamiento de interrupción cuando finaliza una conversión. Lee el 
;resultado, lo convierte a BCD y lo visualiza sobre la pantalla LCD
Inter            movf    ADRESH,W
                movwf    Byte_H
                bsf        STATUS,RP0        ;Banco 1
                movf    ADRESL,W
                bcf        STATUS,RP0        ;Banco 0
                movwf    Byte_L            ;Lee y salva el resultado de la conversión
                call    Bits16_BCD        ;Convierte a BCD
                movlw    0x84
                call    LCD_REG            ;Coloca el cursor
                call    Visualizar        ;Visualiza sobre el LCD
                bsf        ADCON0,GO_DONE    ;Inicia una nueva conversión
                retfie
    
;Visualizar: Visualiza sobre la pantalla LCD, en la posición actual del cursor, los cuatro 
;dígitos situados en las variables BC_1 y BCD_2
Visualizar        movlw    2
                movwf    Contador        ;Inicia contador de bytes a convertir
                movlw    BCD_1
                movwf    FSR                ;Inicia puntero índice
Visual_loop        swapf    INDF,W
                andlw    0x0f
                iorlw    0x30            ;Convierte a ASCII el nible de más peso
                call    LCD_DATO        ;Lo visualiza
                movf    INDF,W
                andlw    0x0f
                iorlw    0x30            ;Convierte a ASCII el nible de menos peso
                call    LCD_DATO        ;Lo visualiza
                decf    FSR,F            ;Siguiente byte
                decfsz    Contador,F
                goto    Visual_loop
                return

;16Bits_BCD: Esta rutina convierte un número binario de 16 bits situado en Cont_H y
;Cont_L y, lo convierte en 5 dígitos BCD que se depositan en las variables BCD_0, BCD_1
;y BCD_2, siendo esta última la de menos peso.
;Está presentada en la nota de aplicación AN544 de MICROCHIP y adaptada por MSE
Bits16_BCD        bcf        STATUS,C
                clrf    Contador    
                bsf        Contador,4        ;Carga el contador con 16        
                clrf    BCD_0
                clrf    BCD_1
                clrf    BCD_2            ;Puesta a 0 inicial

Loop_16            rlf        Byte_L,F
                rlf        Byte_H,F
                rlf        BCD_2,F
                rlf        BCD_1,F
                rlf        BCD_0,F            ;Desplaza a izda. (multiplica por 2)
                decfsz    Contador,F
                goto    Ajuste
                return

Ajuste            movlw    BCD_2
                movwf    FSR                ;Inicia el índice
                call    Ajuste_BCD        ;Ajusta el primer byte
                incf    FSR,F
                call    Ajuste_BCD        ;Ajusta el segundo byte
                incf    FSR,F
                call    Ajuste_BCD
                goto    Loop_16

Ajuste_BCD        movf    INDF,W        
                addlw    0x03
                movwf    Temporal    
                btfsc    Temporal,3        ;Mayor de 7 el nibble de menos peso ??
                movwf    INDF            ;Si, lo acumula
                movf    INDF,W        
                addlw    0x30
                movwf    Temporal
                btfsc    Temporal,7        ;Mayor de 7 el nibble de menos peso ??
                movwf    INDF            ;Si, lo acumula
                return

;Programa principal
Inicio               clrf    PORTA
                clrf    PORTB            ;Borra salidas
                bsf        STATUS,RP0
                bsf        STATUS,RP1        ;Banco 3
                movlw    b'00000001'
                movwf    ANSEL            ;RA0/AN0/C12IN0- entrada analógica, resto digitales
                clrf    ANSELH            ;Puerta B digital
                bcf        STATUS,RP1        ;Banco 1
                clrf    TRISB            ;Puerta B se configura como salida
                movlw    b'11110001'
                movwf    TRISA            ;RA3:RA1 salidas
                bcf        STATUS,RP0        ;Selecciona banco 0

;Inicio de la pantalla LCD y visualiza mensaje inicial
                call    UP_LCD            ;Configura E/S para el LCD
                call    LCD_INI            ;Secuencia de inicio del LCD
                movlw    b'00001100'
                call    LCD_REG            ;LCD On, cursor y blink Off
                movlw    Mens_0
                call    Mensaje            ;Visualiza "AN0=    (*4.8mV)"

;Se activa el ADC y se selecciona el canal RA0/AN0.    Frec. de conversión = Fosc/32        
                bsf        STATUS,RP0        ;Selecciona página 1
                movlw    b'10000000'
                movwf    ADCON1            ;Alineación dcha. Vref= VDD
                bcf        STATUS,RP0        ;Selecciona página 0
                movlw    b'10000001'
                movwf    ADCON0            ;ADC en On, seleciona canal RA0/AN0 y Fosc/32

;Habilita interrupción provocada al finalizar la conversión
                bcf        PIR1,ADIF        ;Restaura el flag del conversor AD
                bsf        STATUS,RP0        ;Banco 1
                bsf        PIE1,ADIE        ;Activa interrupción del ADC
                bcf        STATUS,RP0        ;Banco 0
                bsf        INTCON,PEIE        ;Habilita interrupciones de los periféricos
                bsf        INTCON,GIE        ;Habilita interrupciones
                bsf        ADCON0,GO_DONE    ;Inicia la conversión

;Bucle principal
            
Loop            nop
                goto    Loop            ;Repetir la lectura

                end                        ;Fin del programa fuente
```

También está en C de CCS.

```
/*
         

El módulo convertidor ADC. Interrupción trás la conversión. Voltímetro digital

Este ejemplo visualiza sobre la pantalla LCD el valor obtenido por el convertidor a partir
de una tensión analógica de entrada de entre 0 y 5Vcc que se aplica por la entrada RA0/AN0. */

#include <16f886.h>

/* Ajusta los valores de las palabras de configuración durante el ensamblado.Los bits no empleados
adquieren el valor por defecto.Estos y otros valores se pueden modificar según las necesidades */

#fuses     NOLVP,PUT,NOWDT,EC_IO,NOFCMEN,NOBROWNOUT    //Palabra 1 de configuración
#fuses    NOWRT,BORV40                                //Palabra 2 de configuración

/* Con estas directivas las funciones "input" y "output_bit" no reprograman
el pin de la puerta cada vez que son utilizadas. Si no se indica el
modo fast_io se asume por defecto standard_io el cual reprograma el pin
siempre antes de ser utilizadas estas funciones. */

#device ADC=10                                //Conversor ADC/ de 10 bits de resolución
#use fast_io (A)
#use fast_io (B)
#use delay(clock=4000000)                    //Frecuencia de trabajo

#include <lcd4bitsPIC16.h>                    //Incluye funciones del manejo del LCD

#int_ad                                        //Vector de interrupción al finalizar la conversión A/D
tratamiento()
{    
    float Vin;
    lcd_gotoxy(1,1);                        //Coloca el cursor        
    Vin=read_adc(adc_read_only);            //Lee el resultado de la conversión
    printf(lcd_putc,"AN0= %1.2g Volt.",Vin*0.004887585);    //Visualiza el resultado de la conversión, convertido a voltios
    delay_us(10);                            //Temporiza 10uS
    read_adc(adc_start_only);                //Inicia una nueva conversión
}    
                    
main()
{  
    delay_ms(50);    
    lcd_init();                                //Inicia la pantalla LCD

//Se activa el ADC y se selecciona el canal RA0/AN0. Frecuencia de trabajo Fosc/32
    setup_adc(adc_clock_div_32);            //Ajusta frecuencia de muestreo del ADC
    setup_adc_ports(sAN0);                    //RA0 entrada analógica

    set_adc_channel(0);                        //Selección del canal RA0/AN0

    enable_interrupts(INT_AD);                //Activa interrupción al finalizar la conversión
    enable_interrupts(global);                //Habilita interrupciones    
    read_adc(adc_start_only);                //Inicia la conversión

    while(1)
    {
    }
}
```

Puedes adaptarlo al PIC16F818.


----------



## chugus (Sep 5, 2012)

Buenisimo, gracias META ya estoy encaminado en el código, aunque yo descartaría toda la parte de rutinas para el LCD porque no lo utilizo.

Un saludo!


----------



## Meta (Sep 5, 2012)

Si no usas el LCD normal que lo descartes. Estoy haciendo pruebas con el 16F886, luego te subo un vídeo para que lo veas.

Espero que te vaya bien y nos enseña tus proyectos.


----------



## Meta (Sep 5, 2012)

Acabade el vídeo.






El otro con dos potenciómetros al mismo tiempo.






Saludo.


----------



## chugus (Sep 6, 2012)

Bueno, voy dejando videos y fotos de mi proyecto. Gracias a todos los que me dieron una mano. Estoy en diseño del PCB si alguien lo necesita que me chifle por acá.

Un saludo!

Video.






Imágenes del PCB.


----------



## Meta (Sep 6, 2012)

Buenísimo. 

No has tardado en hacerlo. Parece que el código que te mostré arriba te ha servido un poco.
¿Al final lohicieste en asm?

Buen trabajo.


----------



## chugus (Sep 6, 2012)

Hola META, en realidad me he replanteado el problema que tenía y llegue a la conclusión que para el uso que yo le voy a dar no necesito una medición que supere los 25.5V por lo que he optado con poner en la entrada analógica un divisor resistivo formado por una R de 10K y un preset de 5K ajustado en la mitad (2K5 ohms) con lo que logro una escala lineal de 00.0 a 25.5 Voltios. Y todo eso sin tocar ninguna linea mas de código lo que me alivia mucho el trabajo.

PD: el programa esta en su totalidad en ASM.

Un saludo!


----------



## Melghost (Sep 7, 2012)

Solución salomónica. ¡Bravo!


----------



## chugus (Sep 7, 2012)

> Solución salomónica. ¡Bravo!


Jaja. Si de por cierto a veces se me prende la lamparita !!!

Un saludo!


----------



## Holas (Dic 13, 2012)

Chugus , podrías poner el esquemático de tu diseño?.
Y el programa que has utilizado en assembler?


----------

