Problemas con ADC en PIC16F887 y CCS

Que tal brothers!!! Tengo un ligero problema, estoy realizando un ADC con el PIC16f887, para ello utilizo CCS y Proteus. En la simulacion corre perfectamente, el problema es cuando programo el PIC, es decir en el circuito real no jala jaja.
 

Adjuntos

  • Fuente.jpg
    Fuente.jpg
    160.4 KB · Visitas: 38
  • Fuente.txt
    1.4 KB · Visitas: 25
Última edición:
Algún detalle mas estaría bien.
¿No va nada de nada?
¿Va el display pero mide mal?
...
 
En el código que pones está definido el canal de esta forma: setup_adc_ports(PIN_A0);
Esa selección corresponde al pin RA0, no al canal AN0
La definición del pin RA0 equivale a 40 y no a 1 que es el valor para sAN0

#define PIN_A0 40
#define sAN0 1

Cambia setup_adc_ports(PIN_A0); por setup_adc_ports(sAN0); y prueba otra vez.
 
Lo que pasa es que en la simulación funciona bien. La idea es que conforme vaya moviendo el potenciometro me vaya mostrando distintos valores el LCD (3v, 5v, 12v, 15v, 18v), a su vez que cada rele se energize para cada caso. Respecto al circuito fisico, no me funciona nada, no hace nada solamente ponerme en "alto" todas las salidas que utilice.
 

Adjuntos

  • Fuente.rar
    57.4 KB · Visitas: 52
Última edición:
Veo que no me hiciste caso sobre lo que te comenté.
El código sigue estando igual.

Mira esta advertencia sobre lo que está ocurriendo:
ADC Warning.jpg
 
Disculpa compa, me equivoque de archivo... pero sí cambie lo que me dijiste y sigue pasando lo mismo, me funciona la simulacion pero en el cto, fisico no
 

Adjuntos

  • Fuente 1.rar
    57.6 KB · Visitas: 10
Disculpa compa, me equivoque de archivo, pero sí cambié lo que me dijiste y sigue pasando lo mismo, me funciona la simulación pero en el circuito físico, no.
OK. Tal vez tengas algo mal conectado. Cristal y/o capacitores, potenciómetro, falsos contactos, etc.
Para salir de dudas, prueba ahora con los cambios que le hice al programa. (Corrección, fuses y simplificación de código.)
Ahora se mostrará el voltaje de entrada en la pantalla, pero al cambiar la fórmula, también tendrás que corregir el valor de los voltajes de comparación, que ahora irán de 0 a 18.
 

Adjuntos

  • Fuente II.rar
    78.8 KB · Visitas: 18
Última edición:
Hola a todos. Tengo un problema, lo que quiero hacer es por uno de los puertos ADC por ejemplo AN0 del PIC, conectar 8 Pulsadores y cada uno tiene un preset calibrado a un valor, para que esto según el valor en otro puerto del PIC me enciende un LED.
Pulsador 1 led 1, pulsador 2 led 2 y así. El problema que a veces me enciende y a veces no, es inestable.
¿Por qué es eso? Hice una rutina que se llama Rangos que según el rango me retorna un valor que va de 0 a 7, eso después lo utilizo en una rutina Switch para que seleccione el LED a encender, pero repito, a veces enciende y a veces no.

Mas abajo en el programa van a ver que están las subrutinas de GrabaEEPROM, LEEEEPROM, i2c_envia, porque utilizo el PCF8574.
Osea Lee el conversor ADC, busco el rango en rangos_ADC() y luego lo guardo en la EEPROM, para más tarde leerlo y luego enviarlo por I2C al PCF8574, que éste luego me enciende el LED.
Se puede omitir todo esto para no hacerse un enredo en la cabeza del que me va a ayudar.

Lo que necesito, es que cuando lea el pulsador y me retorne por medio de Rangos_ADC un valor entre 0 y 7 y me encienda un LED, cosa que a veces lo enciende y otras no, como que es inestable.
Lo mas triste que en el Proteus anda pero en la vida real no.

¿Para qué es todo esto? Es para poner pulsadores en la casa e ir encendiendo las luces.
Lo bueno es que por cada ADC puedo conectar ocho pulsadores y según el rango de tensión que entrega cada pulsador me enciende la luz del lugar.
Por eso son 8 los valores que me entrega Rangos ADC. ¿Me expliqué bien en todo?
Código:
int8 ADDRE[6] = {0x40,0x42,0x44,0x46,0x48,0x4A};   //Array no la uso ocupa mas memoria

void TodasLasSalidas (char dato)
{ int i;

   for(i=AD0;i<=AD5;++i)
      i2c_envia(ADDRE[i],dato);                 //Ponea cero el Integrado 1
}//FIN TodasLasSalidas

int BCD(int y)
{
   unsigned int r=1,i;
   
   for(i=1;i<y;i++)
         r*=2;
   return r;
}


void i2c_envia(char addr,char data)
{ 
   i2c_start(); 
   i2c_write(addr); 
   i2c_write(data); 
   i2c_stop(); 
} 

int rangos_adc(int16 dato)
{
      if((dato>204)&&(dato<307))             //1V-1.5V
         return 0;
      if((dato>307)&&(dato<409))             //1.5V-2V
         return 1;
      if((dato>409)&&(dato<511))             //2V-2.5V
         return 2;
      if((dato>511)&&(dato<614))             //2.5V-3V
         return 3;
      if((dato>614)&&(dato<716))             //3V-3.5V
         return 4;
      if((dato>716)&&(dato<818))             //3.5V-4V
         return 5;   
      if((dato>818)&&(dato<920))             //4V-4.5V
         return 6;
      if((dato>920)&&(dato<1023))             //4.5V-5V
         return 7;
     
     return -1;
     
     
}//FIN RANGO ADC

void grabaEEPROM(char addr, char dato)
{
   int val;
   val = read_eeprom(addr);
   delay_us(10);
   if(val!=0)
      dato = 0;

   write_eeprom(addr,dato);
   delay_us(10);   
      
}

int leeEPROM(char dato)
{
   int i,val;
   val=0;
   for(i=dato;i<=(dato+7);++i)
      val += read_eeprom(i);
   
   return val;
   // write_eeprom(0x80,dato);
  // i2c_envia(addr,val);
}

void main()
{
   int i;
   int v;
   int16 m=0;
   
   setup_adc_ports(sAN0|sAN1|sAN2|sAN3|sAN4);
   setup_adc(ADC_CLOCK_INTERNAL);

   eeprom_ini();
   TodasLasSalidas(0);
   
   while(TRUE)
   {
      //TODO: User Code
   set_adc_channel(AD0);
   delay_us(20);
   for(i=1;i<=10;++i){
      m=read_adc();
      delay_us(20);
   }


   v=rangos_adc(m);
   
    while(read_adc()) delay_us(20);  //Espera que se suelte la tecla


   switch(v){
      case 0: grabaEEPROM(0,BCD(1));  //i2c_envia(ADDRE[0],1);
              break;
      case 7: grabaEEPROM(7,BCD(8));// i2c_envia(ADDRE[0],2);
               break;
      default: i=leeEPROM(0); i2c_envia(ADDRE[0],i);
               break;
     }//FIN SWITCH*/
     
 

   }//FIN WHILE

}
 
Hola a todos. Tengo un problema, lo que quiero hacer es por uno de los puertos ADC por ejemplo AN0 del PIC, conectar 8 Pulsadores y cada uno tiene un preset calibrado a un valor, para que esto según el valor en otro puerto del PIC me enciende un LED.
Pulsador 1 led 1, pulsador 2 led 2 y así. El problema que a veces me enciende y a veces no, es inestable.
El código contiene errores y está incompleto cómo menciona Lord Chango.
Omites partes importantes en donde puede estar el problema.

Viendo el código que colocas, existe esto que resulta confuso.
for(i=AD0;i<=AD5;++i)

Tanto AD0 y AD5, no están declaradas, o no se ven en el código.
Luego usas AD0 para asignar el canal. ¿Pero que valor tiene AD0?
set_adc_channel(AD0);

La subrutina i2c_envia se encuentra en el código, pero no está declarada y eeprom_ini() no existe.

Por eso es recomendable que adjunten el proyecto completo dentro de un archivo comprimido.
Se puede omitir todo esto para no hacerse un enredo en la cabeza del que me va a ayudar.
Omitiendo las rutinas que no están, quedaría poco para encontrar la causa, pero por lo que comenté anteriormente, resulta confuso por los valores no asignados.

Mejor adjunta el proyecto completo incluyendo esquema y/o simulación.
 
AD0 y AD5 son constantes que van de 0 a 5 que son las entradas a los AN del Pic osea AD0 que tiene valos 0 para la entrada a AN0 AD1 para AN1 y asi y lo que es EPROM INI lo que hace es borrar las FF de la EEPROM, pero repito el problema aca copie algunas partes porque tengo problemas con el conversor, No hay problemas en la eeprom ni nada aca mi problema es que si preciono un pulsador quiero que encienda un led, bueno el problema es ese a veces prende y otras no.
Porque sucede eso? hagan de cuenta un Programa Basico donde Selecciono el Canal 0 (AN0), leo la entrada analogica lo guiardo en una variable que despues se busca en una tabla retorna un valor entre 0 y 7 y con un comando switch selecciono que led encender lo demas hagan de cuenta que no existe. bueno eso que escribi no lo hace, o a veces si y otras no, porque si yo leo 1 volt para el led 1 a veces enciende y otras no. Quiero hacer eso para no hacerlo con entradas digitales son 40 pulsadores mas o menos. si conecto 8 por cada entrada digital esta barbaro un pulsador seria 1 volt por ejemplo el 2 pulsador 1.5 volt, el 3 pulsador 2 volt y asi y segun la tension que lea enciende el led. Alguien hizo algun programa o realizo varios pulsadores en una entrada Analoga y que funcione y sea estable? que cuando precione el pulsador encienda un led. son 8 pulsadores por entrada. Si logro que ande perfecto me salvan



Ahi subi el Zip con el programa estoy utilizando solo el main y algunas subrutinas porque solo me estoy abocando a leer el ADC que seleccione un numero entre 0 y 7 y me encienda uno de los 8 leds nada mas
 

Adjuntos

  • Prueba.zip
    164.9 KB · Visitas: 12
Última edición:
Acá mi problema es que si presiono un pulsador quiero que encienda un led, bueno el problema es ese, a veces prende y otras no.
¿Por qué sucede eso?
El problema puede estar en la forma de comprobación sobre la tecla soltada al hacerlo con un bucle while esperado que la lectura sea 0.
¿Alguien hizo algún programa o realizo varios pulsadores en una entrada Análoga, que funcione y sea estable?
Que cuando presione el pulsador encienda un led. Son 8 pulsadores por entrada.
Si logro que ande perfecto, me salvan.
En el ejemplo que adjunto, se lee un teclado matricial análogo 4x4 y conforme el valor entregado por división de tensión, se compara, se da cierto margen y se enciende o apaga un LED por cada valor devuelto.
Mira la forma de la comprobación de tecla para determinar cuando se ha soltado.

Espero te sirva para darte una idea de cómo puedes hacer lo que requieres.
 

Adjuntos

  • 16F887 Teclado Analógico.rar
    59.3 KB · Visitas: 14
Una pregunta D@rkbytes, lo llegaste hacer el circuito osea lo utilizaste fisicamente o solo por simulador? porque en el simular cuando lo habia hecho funcionaba peroen lo real ahi me di cuenta que no fue asi. Lastima por proteus que la verdad me defraudo en eso porque pense que funcionaria lomejor en realidad es hacerlo aunque sea en un protoboard y ver que funcione. Si te funciono en lo real voy a probar, lo que pasa que no utilizo un teclado matricial, si utilizo un divisor de tension hay una resistencia y luego cada tecla tiene su resistencia asi se cual se preciono segun el valor de tension encenderia. y otra cosa yo utilice 8 pulsadores asi el rango para seleccionar es mas amplio en tension.
 
Una pregunta D@rkbytes. ¿Lo llegaste hacer el circuito osea lo utilizaste físicamente o solo por simulador? porque en el simular cuando lo había hecho funcionaba pero en lo real ahí me di cuenta que no fue así.
Lastima por proteus que la verdad me defraudó en eso porque pensé que funcionaría.
Lo mejor en realidad es hacerlo aunque sea en un protoboard y ver que funcione.
Si te funcionó en lo real voy a probar.
Sip, sí he probado ese sistema físicamente pero no con ese PIC, aunque debe funcionar igual, pero es mejor que te cerciores realizando una prueba física.
Así es como funcionan varios equipos electrónicos que usan teclado resistivo.
Lo que pasa es que no utilizo un teclado matricial, si utilizo un divisor de tensión, hay una resistencia y luego cada tecla tiene su resistencia, así sé cual se presionó, según el valor de tensión encendería.
Y otra cosa, yo utilicé 8 pulsadores, así el rango para seleccionar es más amplio en tensión.
Bien, pues es similar, sólo que en este sistema se usa algo así: Teclado por divisor resistivo.jpg
Usando el ADC a 10 bits y si todo fuera precisión, podrían ser 1023 -1 teclas,
y se tendría un rango de 4.88mV. por tecla para sumar 5V, pero esto es prácticamente imposible.
Así que, a menor cantidad de teclas existe mayor rango separación y por ende menor error.

Prueba este método y nos comentas qué tal te va.
 
La Verdad sos un maestro porque ahora si me reconoce, pero me paso otra cosa y seguro sabes, en el archivo adjunto anterior lo que hice fue poner en el MAIN
Código:
v=valoradc(AD0);           
         v=AD0*8;                   //Mul. por 8 asi despues busca en la EEPROM
         ve=leeEPROM(v);            //Guarda en iel valor delaeeprom
         i2c_envia(ADDRE[AD0],ve);  //Utiliza i para enviar el dato por I2C

con lo anterior Funciona

Código:
v=valoradc(AD1);           
         v=AD1*8;                   //Mul. por 8 asi despues busca en la EEPROM
         ve=leeEPROM(v);            //Guarda en iel valor delaeeprom
         i2c_envia(ADDRE[AD1],ve);  //Utiliza i para enviar el dato por I2C
Cuando Coloco para leer el Segundo canal de ADC no funciona lo del canal anterior, es raro. ¿Por qué será? Deja de funcionar siendo que puse el mismo código para leer el canal 1 y el cero no anda.


Código:
void main()
{
   char ve;
   int i;
   int v;
   int16 m=0;
   
   setup_adc_ports(sAN0|sAN1|sAN2|sAN3|sAN4);   //CONFIGURA LAS ENTRADAS ADC
   setup_adc(ADC_CLOCK_INTERNAL);               //SETUP RELOJ INTERNO

   eeprom_ini();                 //INICIALIZA LA EEPROM BORRANDO TODO
   TodasLasSalidas(0);           //ENVIA AL I2C PARA PONER TODO A 0
 //  lcd_init();
   
   while(TRUE)
   {
      
      //TODO: User Code


         v=valoradc(AD0);           
         v=AD0*8;                   //Mul. por 8 asi despues busca en la EEPROM
         ve=leeEPROM(v);            //Guarda en iel valor delaeeprom
         i2c_envia(ADDRE[AD0],ve);  //Utiliza i para enviar el dato por I2C
         
         v=valoradc(AD1);           
         v=AD1*8;                   //Mul. por 8 asi despues busca en la EEPROM
         ve=leeEPROM(v);            //Guarda en iel valor delaeeprom
         i2c_envia(ADDRE[AD1],ve);  //Utiliza i para enviar el dato por I2C
                  

   }//FIN WHILE

}



Por las dudas adjunté de nuevo el archivo del programa.



Osea no anda más ni uno ni otro.



SI utilizo uno u otro, osea anulo uno de los dos con // funciona pero ambos no y lo mas triste que necesito hacer 8 entradas análogas y ya así se me complicó.
 

Adjuntos

  • Prueba.zip
    183 KB · Visitas: 17
Última edición por un moderador:
Hay algo que no le veo sentido.
Al hacer v=AD0*8; No estás multiplicando nada, AD0 es una constante que vale 0 (0 * 8 = 0)
Y si lo haces por 1 el resultado será 8: v=AD1*8; (1 * 8 = 8)

Si quieres que v se multiplique por 8, tendría que ser así: v *= 8;
 
Porque yo primero lo que leo lo grabo en la EPROM, luego lo leo y lo envió por i2c a los pcf8574. Porque está así v=ad0*8 es por lo siguiente:
De la dirección 0 a 7 graba las 8 salidas del PCF8574 con dirección 0x40, de la 8 a la 15 la del otro integrado del 16 al 23 otro y así.
Entonces si AD0 es cero, listo, sabe que tiene que ser para el primero, cuando es AD1 viene para el segundo integrado y así, lo que pasa que eso después tiene que ir en un for que aún no hice.
Por eso está el archivo Zip fíjate lo que esté anulado y te vas a dar cuenta.
Pero igual a ver el problema no viene por ahí, yo quiero leer la entrada del AN0 y guardar el valor, leerlo y sacarlo por I2C, luego hacer lo mismo con AN1 y así con AN2...
Pero no funciona, sólo lo hace con uno, si pongo ambos no funciona.



En Proteus anda, en la vida real no. ¿Por qué? Si leo AN0 funciona, si fuese solo AN1 funciona, osea cuando hay uno solo, pongo AN0 para que lea y lo envíe luego escribo lo mismo para AN1 y no funciona ninguno de los dos, sólo funciona si es uno solo, cosa que no se entiende.



Ejemplo que hice en una imagen de ejemplo de la memoria eeprom 00 a 07 corresponde a un integrado PCF8574 con dirección 0x40, de la direccióne eprom 08 a 1F a otro PCF8574 con dirección 0x42 , de la dirección eeprom 20 a 27 otro PCF8574, y así, entonces grabo los valores.

Dirección eeprom Valor a Grabar
00 ------------------- 01
01 ------------------- 02
02 ------------------- 04
03 ------------------- 10
04 ------------------- 20

Y así, con eso lo que hago es salir por la patita 1 del PCF o la 2 o la 3 y así.
Entonces graba en la eeprom eso y luego lo leo para enviarlo.
Todo eso lo hace, pero el problema es que cuando coloco para leer la entrada análoga 1 para que haga eso y luego la entrada dos análoga para hacer eso y asi no funiona.
Pero si ahora hago sólo una entrada análoga no importa si la 1 o 2 o 3 funciona. ¿Me explico?

---------- Actualizado después de 3 horas ----------

Aclaro qué lo que está en el gráfico es un ejemplo.
 

Adjuntos

  • eprom.jpg
    eprom.jpg
    72.7 KB · Visitas: 11
Última edición por un moderador:
Porque yo primero lo que leo lo grabo en la EPROM, luego lo leo y lo envió por i2c a los pcf8574. Porque está así v=ad0*8 es por lo siguiente:
De la dirección 0 a 7 graba las 8 salidas del PCF8574 con dirección 0x40, de la 8 a la 15 la del otro integrado del 16 al 23 otro y así.
Entonces si AD0 es cero, listo, sabe que tiene que ser para el primero, cuando es AD1 viene para el segundo integrado y así, lo que pasa que eso después tiene que ir en un for que aún no hice.
Es que mira, pasa lo siguiente con el código actual que estás poniendo...
Código:
         v=valoradc(AD0);           // Guardas en v el resultado de valoradc
         v=AD0*8;                   // 0 * 8 = 0
         ve=leeEPROM(v);            // ve = valor en la EEPROM dirección 0
         i2c_envia(ADDRE[AD0],ve);  // envías Dir = 0x40, Dato = Lo que tenga la dirección 0 de la EEPROM
Y para leer la EEPROM tienes esto...
Código:
char leeEPROM(char dato)
{
   int i,val;
   val=0;
   for(i=dato;i<=(dato+7);++i)
      val += read_eeprom(i);
   
   return val;
}// FIN leeEEPROM
Pides un dato que usas para un bucle For, pero si envías 0 entonces dato siempre es 7
Cosa que yo no encuentro lógica y así en varios aspectos más de tu programa.
Supongamos que dato sea 8... 8 + 7 = 15
Entonces esas veces serán las que leas la EEPROM desde la dirección 0 a la 15 y lo que contenga cada dirección se irá sumando en la variable "val" (INT8) :confused:
¿Qué pasa si la EEPROM no está escrita y entonces cada dirección contiene 0xFF?

En fin, sinceramente yo no entiendo tu programación.
Ya adjunté un ejemplo que podrías adaptar para lo que requieres y aparte funciona.
 
Última edición:
Atrás
Arriba