Autor Tema: Interrupción ADC  (Leído 1830 veces)

0 Usuarios y 1 Visitante están viendo este tema.

Desconectado JuanjoPic

  • PIC12
  • **
  • Mensajes: 97
Interrupción ADC
« en: 19 de Septiembre de 2017, 16:36:49 »
Hola compañeros.
Estoy haciendo un programa para utilizar el ADC del pic16f716 y la interrupción  de este no se ejecuta.
El código en si es tomar el valor ADC y setear el duty-cycle de una señal PWM.

Código: C
  1. #include "fuses_pic16f716.h"
  2. #define _XTAL_FREQ 1000000
  3. unsigned char volatile RESULTADO;
  4.  
  5. void config_pwm(){
  6.     TRISBbits.TRISB3 =0;
  7.     PR2 = 250 -1 ;           // configuro periodo = 1ms,con clk 1Mh y prescaler 1:1
  8.     CCP1CON = 0b00111100 ;  
  9.     CCPR1L = 0b01111111;
  10.     PIR1bits.TMR2IF = 0;
  11.     T2CON = 0X00;
  12.     T2CONbits.TMR2ON = 1;
  13. }
  14.  
  15. void config_ADC(){
  16.     TRISAbits.TRISA0 = 1;  // setear A0 como entrada
  17.     TRISAbits.TRISA3 = 1;
  18.     ADCON1 = 0b00000101;   // voltaje de referncia Vdd
  19.     ADCON0 = 0x00;
  20.    
  21.     PIE1bits.ADIE = 1;
  22.     PIR1bits.ADIF = 0;    
  23. }
  24.  
  25. void interrupt ADC_vector(){
  26.     RESULTADO = ADRES;
  27.     CCPR1L = RESULTADO;
  28.     ADCON0bits.ADON = 1;
  29.     PIR1bits.ADIF = 0;
  30. }
  31.  
  32. void interrupciones(){
  33.     INTCONbits.PEIE = 1;
  34. }
  35. void main(void) {
  36.     config_pwm();
  37.     config_ADC();
  38.     interrupciones();
  39.     ADCON0bits.ADON = 1;
  40.     INTCONbits.GIE = 1;    
  41.     while(1){    
  42.        
  43.     }      
  44. }

La señal PWM funciona, pero el ADC no se ejecuta.

Cualquier ayuda me sera de utilidad.


Desconectado KILLERJC

  • Colaborador
  • DsPIC33
  • *****
  • Mensajes: 8242
Re:Interrupción ADC
« Respuesta #1 en: 20 de Septiembre de 2017, 15:02:18 »
En el analisis vamos a ir por parte

Citar
#define _XTAL_FREQ 1000000

Tenes puesto un cristal de 1Mhz ? O tenes uno de 4Mhz y pensaste que debes poner alli 1Mhz ?

-------------------------------------------------

ADC:

Código: C
  1. void config_ADC(){
  2.     TRISAbits.TRISA0 = 1;  // setear A0 como entrada
  3.     TRISAbits.TRISA3 = 1;
  4.     ADCON1 = 0b00000101;   // voltaje de referncia Vdd
  5.     ADCON0 = 0x00;
  6.    
  7.     PIE1bits.ADIE = 1;
  8.     PIR1bits.ADIF = 0;    
  9. }

Segun tu codigo estas usando lo siguiente:
- Fosc/2
- RA3 es VREF ( el cual lo mas simple es conectarlo a VDD a ese pin)
- RA0 que es tu entrada y que es la seleccionada.

Para evitar la conexion de RA3 a VCC, total si o si pones 3 pines ( RA0,1 y 3 ) como analogicos, mejor elegir la opcion que pone internamente VREF a VDD.
Tambien para evitar problemas, en ves de hacerlo dependiente del oscilador que pongas, vamos a usar el oscilador RC que posee el ADC

Código: C
  1. void config_ADC(){
  2.     TRISAbits.TRISA0 = 1;  // setear A0 como entrada
  3.     TRISAbits.TRISA3 = 1;
  4.     ADCON1 = 0b00000100;  // RA0, RA1 y RA3 analogicos, VREF = VDD
  5.     ADCON0 = 0xC1;         //FRC y ADC ON
  6.    
  7.     PIR1bits.ADIF = 0;   //Limpias flag
  8.     PIE1bits.ADIE = 1;   // Activas interrupcion
  9. }

Podriamos omitir los TRIS, ya que en el reset todos estan como entradas.

-----------------------------

PWM

Aca voy a suponer que tus valores son correctos. ( Periodo/Duty )
No parece haber nada malo, y corresponden con tus comentarios si es que realmente esta ingresando 1Mhz.
Lo que si tenes que saber que lo maximo a cargar en el duty es (PR2 * 4) es decir 996, usando los 10 bits. O maximo de 249 usando 8 bits (solo el registro CCPR1L )

-----------------------------------

Funcion main

Hicimos un par de cambios aca.
El INTCON.GIE lo pasamos a la funcion "interrupciones"
El ADCON0.ADON lo pasamos al "config_ADC"

Y finalmente necesitamos saber cuando es que le vamos a dar marcha al ADC

Código: C
  1. void main(void) {
  2.     config_pwm();
  3.     config_ADC();
  4.     interrupciones();
  5.  
  6.     ADCON0bits.GO = 1;
  7.  
  8.     while(1);
  9.  
  10. }

No recuerdo bien el nombre del bit, porque tiene varios (es el bit GO_DONE ), vos pones a 1 ese bit y solo resta esperar que termine la conversion, a la cual va a salir la interrupcion.

---------------------------------

Interrupcion

Un pequeño cambio, si observas no posee sentido alguno que tengas una variable ahi.
Asi que la vamos a eliminar, y el error es que para que inicie de nuevo la conversion es que debemos activar el bit GO_DONE de nuevo
quedando:

Código: C
  1. void interrupt ADC_vector(){
  2.     PIR1bits.ADIF = 0;     // Limpio flag
  3.     CCPR1L = ADRES;      // Actualizo PWM
  4.     ADCON0bits.GO = 1;  // Inicio de nuevo la conversion
  5. }
« Última modificación: 20 de Septiembre de 2017, 15:04:35 por KILLERJC »

Desconectado JuanjoPic

  • PIC12
  • **
  • Mensajes: 97
Re:Interrupción ADC
« Respuesta #2 en: 20 de Septiembre de 2017, 16:13:57 »
Gracias por tu ayuda  KILLERJC.

Ahora me funciono solo tuve que agregar en la función de ADC_configuración() habilitar la interrupción Global.

acerca de:
Citar
Tenes puesto un cristal de 1Mhz ? O tenes uno de 4Mhz y pensaste que debes poner allí 1Mhz ?

En la simulación  tengo puesto 1Mhz, ya que había calculado un periodo para la PWM y necesitaba que fuera de ese valor el cristal.

Tengo entendido que
Citar
#define _XTAL_FREQ 1000000
solo afecta para la función intrínseca delay del compilador(o no?), pero en mi programa no se utiliza (la saque del programa y me funciona igual) y estaba ahí por que en el mismo programa esta haciendo una prueba con un delay.

Ahora acerca de la variable que había usado, lo hice por que "creí" que no me funcionaba el programa por que los registros no sea actualizaban y pensé utilizar una variable intermedia que los actualizara jajaja (como en asm que utiliza el registro W para setear registros xD) . Incluso definí a esa variable como Volatil para que el compilador no la optimizara y permita que esta se actualice siempre. 

Otra consulta acerca de esto:
Citar
Lo que si tenes que saber que lo máximo a cargar en el duty es (PR2 * 4) es decir 996, usando los 10 bits. O máximo de 249 usando 8 bits (solo el registro CCPR1L )
¿Por que no puedo utilizar todo el registro?¿Esto es valido para cualquier ADC de un micro o de este en particular?

Gracias por la ayuda.
Saludos!!

Desconectado KILLERJC

  • Colaborador
  • DsPIC33
  • *****
  • Mensajes: 8242
Re:Interrupción ADC
« Respuesta #3 en: 20 de Septiembre de 2017, 20:47:11 »
Podes usar todo el registro, pero el duty va a variar solo hasta esos valores.

El funcionamiento del PWM es simple, es por comparacion sobre un timer. Y tenes 2 registros que se comparan.

PR2 que te da la frecuencia/periodo la cual es comparada con el registro del TMR2, cuando son activa la salida.
Observaras que el PR2 y el registro del TMR2 son de 8 bits, pero tu duty es de 10bits. En realidad lo que hacen internamente es agregarlo 2 bits (los menos significantes ) mas a cada uno.
Que es lo mismo que multiplicar el valor de PR2 y TMR2 por 4.

El duty son 10 bits, suponiendo unicamente los 8bits de mayor peso ( CCPR1L ) . Se terminan comparando con los bits del TMR2. Al momento que coinciden pone la salida en 0.
Asi que el paso a paso seria:

- Timer2 corriendo continuamente, Salida en 1
- Cuando coincide con el duty, pone salida en 0
- Cuando coincide con el PR2, pone salida en 1 y pone el TMR2 en 0 para comenzar de vuelta.

Entonces si tu PR2 es de 200, tu 0% de duty es 0. Y tu 100% es 200. Si usas 210, 230, 255 sigue siendo 100%.

A lo que voy es que si usas el ADC para que te entregue de 0 a 255, vas a notar que es lineal hasta una parte y luego deja de ser lineal. Ya que se mantiene en el 100% si o si, ya que nunca igualaria el TMR2 con el duty.

Si uno quisiera tener algo lineal, deberia convertir esa variacion de  0-255 en 0-200 con alguna formula.
Como vos tenes un PR2 de 249 (proximo a 255) tal ves no sea ningun problema.

Espero haberme explicado bien.