Autor Tema: DS18B20 evitar espera infinita  (Leído 2111 veces)

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

Desconectado danifiguerola

  • PIC10
  • *
  • Mensajes: 24
DS18B20 evitar espera infinita
« en: 24 de Agosto de 2015, 12:39:34 »
Buenas.

Estoy implementando un proyecto con un pic24f sobre un control de riego. La idea es adquirir, a través de varios sensores, la insolación, la temperatura, la humedad relativa y la velocidad del viento. Con esos datos realizar una serie de cálculos para determinar cuando hay que regar una plantación (según evapotranspiración y balance de agua en el suelo).

El proyecto ya lo tengo casi terminado (lectura de todos los sensores, cálculos...) y ahora estoy repasando todo el código para detectar posibles fallos que pudieran ocurrir y que dejarían el pic "colgado".

Uno de esos fallos es en referencia al termómetro DS18B20. Ese termómetro utiliza para comunicarse un bus one-wire. Se trata de una comunicación por un solo hilo. El tema está en yo, para hacer la lectura de la temperatura, desactivo todas las interrupciones que pudieran distorsionar la lectura. Esto me funciona bien y no hay problema. El problema que veo en la implementación que he hecho es que si hay una desconexión del DS18B20 (falla el cable) o ese se estropea, el pic quedará esperando la respuesta de este infinitamente, quedando colgado ahí.

El código de la rutina que tengo es esa:

Código: [Seleccionar]
float llegir_temperatura (void)
{
    int temp;
    float temperatura;
    unsigned char correcte=1, crc[9], i, error=0;

    _IC1IE = 0;                     //Desactiva interrupció IC1
    _INT1IE = 0;                    //Desactiva interrupció Interrupcio Externa 1
    _INT2IE = 0;                    //Desactiva interrupció Interrupcio Externa 2

    while (correcte != 0)
    {

        while (pols_reset_OW()==1);    //Realitzar reset i esperar pols presència ds18b20

        escriure_byte_OW(Skip_rom);    //Saltar orde ROM

        escriure_byte_OW(Convert_T);    //Indicar a ds18b20 que realitzi conversió temperatura
       
        while (llegir_bit_OW()==0);            //Esperar que ds18b20 acabi conversió temperatura

        while (pols_reset_OW()==1);    //Realitzar reset i esperar pols presència ds18b20

        escriure_byte_OW(Skip_rom);      //Saltar orde ROM

        escriure_byte_OW(Read_scratchpad); //Llegir dades de ds18b20

        for (i=0; i<9; i++)
        {
            crc[i] = llegir_byte_OW();            //Guarda el valor dels 9 bytes del scratchpad
        }

        correcte = comprovar_crc(crc);           //Comprovar si la lectura ha estat correcte
                                               //Al quart intent de lectura amb fallo CRC sortir i avisar
    }

    _IC1IE = 1;                     //Activa interrupció IC1
    _INT1IE = 1;                    //Activa interrupció Interrupcio Externa 1
    _INT2IE = 1;                    //Activa interrupció Interrupcio Externa 2

    temp = crc[1] << 8;
    temp = temp | crc[0];                   //Uneix LSB i MSB de la temperatura en 2 bytes

    if (temp & 0x80)                //Temperatura negativa
    {
        temp = ~temp + 1;
        temperatura = (((float) temp)/16)* (-1);
    }
    else //Temperatura positiva
        temperatura = ((float) temp)/16;

    return (temperatura);
}


Cuando entro en la función, desactivo algunas interrupciones y realizo un "pulso de reset" y espero que el DS18B20 responda. Si por lo que fuera el DS18B20 no responde el programa se queda esperando indefinidamente.

A mi, la única manera que se me ocurre es la siguiente

Código: [Seleccionar]
  T4CONbits.TON = 1;              //Activa el Timer 4 en mode 16 bits
    _T4IE = 1;
    while (correcte != 0)
    {
        TMR4 = 0;
        PR4 = 0xF424;
        _LATA0 = 1;
        while (_T4IF != 1);
        _LATA0 = 0;
        _T4IF = 0;

        while (_T4IF != 1)
        {
            if (pols_reset_OW()==0)
            {
                error = 0;
                //break;
            }
            else
            {
                error = 1;
                correcte = 0;
            }
        }
        _T4IF = 0;
       

Falta parte del código pero solo muestro lo modificado. Mediante el Timer4 (Timer 1 lo uso con el módulo IC1 y el timer 2 y 3 los tengo como contador de 32bytes para otra cosa). Las primeras líneas modificadas era para ver si entraba bien (parpadeo de un led de 4s). Luego, entro en un bucle while que durará como mucho 4s (oscilador de 8MHz, timer4 con preescaler 1:256), si el pulso de respuesta del DS18B20 se recibe marca error 0 y sinó error 1.

No me gusta demasiado esta manera (la encuentro poco clara, y poco "elegante") pero me viene otra manera de hacerlo. Lo quería hacer por rutina de interrupción pero no se como ya que al salir de ella vuelve al bucle.

El problema que tengo es que me parpadea el led y luego el pic se resetea, no lo entiendo. En RCON me marca reset de mclr, por SWR, por POR y Bor.

No se que pasa.

Si alguien tiene alguna idea del poruqe me será de gran ayuda.

Gracias

Desconectado KILLERJC

  • Colaborador
  • DsPIC33
  • *****
  • Mensajes: 8242
Re: DS18B20 evitar espera infinita
« Respuesta #1 en: 24 de Agosto de 2015, 18:06:52 »
Lamentablemente si no queres esperar vas a tener que usar interrupciones.

Mi pregunta es...

¿Tan rapida es la comunicacion con el DS18B20 que no podes tener habilitada las interrupciones?

Digo por que ejecutar un poco de codigo (que es lo que deberia ser una interrupcion), en un PIC24 que imagino que ira a cerca de 80Mhz  te llevaria nanosegundos y tenes una GRAN amplitud de tiempo como para mandar/recibir el dato.
Ejemplo mandar un 0 al DS18B20 (me fije en el datasheet) implica tenerlo en 0 al menos 15us como maximo (75 instrucciones a 20Mhz,150 a 40Mhz y es sufficiente como para una interrupcion) y luego en 1 hasta completar los 60 minimo a 120 us maximos ( La cantidad de instrucciones que pueden ir aqui son demasiadas).

En fin, por eso preguntaba si era que necesitabas desactivar las interrupciones. Para que sea mas rapido podes usar una interrupcion por el puerto (si es que posee) y si tenes un timer corriendo usar ese valor para saber exactamente el tiempo entre un dato y otro, tratando de hacer una especie de maquina de estado en la interrupcion.

Con respecto a que falle el cable, no te queda otra que implementar una especie de "timeout", o tomas un timer que esta corriendo, cuando mandas el pulso tomas el tiempo y luego vas preguntando, si supero cierta cantidad de ciclos del timer significaria que hubo algo malo y procedes a salir del while + activar una bandera que indique un error.
Si tenes un timer de mas directamente usas ese timer como timeout y se vuelve mas simple ya que solo te fijas en el flag.

Ademas tenes que considerar que tenes parado el micro esperando los datos cuando la conversion puede durar hasta 750ms maximo en 12-bit, es MUCHO.

Desconectado danifiguerola

  • PIC10
  • *
  • Mensajes: 24
Re: DS18B20 evitar espera infinita
« Respuesta #2 en: 24 de Agosto de 2015, 20:45:19 »
Hola Killerjc

Lamentablemente si no queres esperar vas a tener que usar interrupciones.

Mi pregunta es...

¿Tan rapida es la comunicacion con el DS18B20 que no podes tener habilitada las interrupciones?

Digo por que ejecutar un poco de codigo (que es lo que deberia ser una interrupcion), en un PIC24 que imagino que ira a cerca de 80Mhz  te llevaria nanosegundos y tenes una GRAN amplitud de tiempo como para mandar/recibir el dato.
Ejemplo mandar un 0 al DS18B20 (me fije en el datasheet) implica tenerlo en 0 al menos 15us como maximo (75 instrucciones a 20Mhz,150 a 40Mhz y es sufficiente como para una interrupcion) y luego en 1 hasta completar los 60 minimo a 120 us maximos ( La cantidad de instrucciones que pueden ir aqui son demasiadas).

Con lo que comentas tienes toda la razón. La rutina de interrupción más larga que tengo es de 5 o 6 instrucciones. El pic lo tengo a 8MHz (no necesito velocidad en mi caso, es el pic24fj64gb002).
Las desconecto para asegurarme que no me van a "interferir" en la comunicación, aunque realmente seria muy mala suerte, pero por si acaso... Sobretodo la del input capture que lo tengo conectado a el TSL235R, que es un sensor que entrega frecuencia proporcional a la luz. En esa interrupción entra mucho el programa.
Las deshabilitaba porque, aunque hablando de micros sea muuuucho tiempo, para las medidas que hago 1 segundo no afecta en cantidad considerable. Pero en lo que dices tienes mucha razón igualmente

Citar
En fin, por eso preguntaba si era que necesitabas desactivar las interrupciones. Para que sea mas rapido podes usar una interrupcion por el puerto (si es que posee) y si tenes un timer corriendo usar ese valor para saber exactamente el tiempo entre un dato y otro, tratando de hacer una especie de maquina de estado en la interrupcion.
No acabo de entender exactamente lo que  quieres decir con eso. Comunicar el pic con el DS18B20 a través de interrupciones y calcular los tiempos a través de un timer?

Citar
Con respecto a que falle el cable, no te queda otra que implementar una especie de "timeout", o tomas un timer que esta corriendo, cuando mandas el pulso tomas el tiempo y luego vas preguntando, si supero cierta cantidad de ciclos del timer significaria que hubo algo malo y procedes a salir del while + activar una bandera que indique un error.
Si tenes un timer de mas directamente usas ese timer como timeout y se vuelve mas simple ya que solo te fijas en el flag.

Mi primea idea era lo que comentas último. Hacer correr el timer4 para que generara interrupción al cabo de x tiempo. Lo que no acabo de ver es como hacer salir del bucle. Había pensado en cambiar de estado una variable y que fuera condición del bucle, no se. Lo que pasa es que tengo las funciones hechas en una libreria a parte del programa principal y no se como pasar la variable para que la reconozca (hacia mucho tiempo que no programaba en C y me estoy volviendo loco con la visibilidad de las variables intentando que no sean globales)

Citar
Ademas tenes que considerar que tenes parado el micro esperando los datos cuando la conversion puede durar hasta 750ms maximo en 12-bit, es MUCHO.
Si. Pero para la aplicación, aunque a priori es desaprovechar el pic, no es un problema.


Gracias por la respuesta

Desconectado KILLERJC

  • Colaborador
  • DsPIC33
  • *****
  • Mensajes: 8242
Re: DS18B20 evitar espera infinita
« Respuesta #3 en: 24 de Agosto de 2015, 22:22:33 »
Citar
Mi primea idea era lo que comentas último. Hacer correr el timer4 para que generara interrupción al cabo de x tiempo. Lo que no acabo de ver es como hacer salir del bucle. Había pensado en cambiar de estado una variable y que fuera condición del bucle, no se. Lo que pasa es que tengo las funciones hechas en una libreria a parte del programa principal y no se como pasar la variable para que la reconozca (hacia mucho tiempo que no programaba en C y me estoy volviendo loco con la visibilidad de las variables intentando que no sean globales)

Para salir de un while() utiliza un break;

Código: C
  1. while(1)
  2. {
  3.         if(Flag_timer_4) { timeout=1; break; }
  4. }

El break te sirve para salirte del os for(), do..while y while

Desconectado danifiguerola

  • PIC10
  • *
  • Mensajes: 24
Re: DS18B20 evitar espera infinita
« Respuesta #4 en: 25 de Agosto de 2015, 04:14:38 »
Citar
Mi primea idea era lo que comentas último. Hacer correr el timer4 para que generara interrupción al cabo de x tiempo. Lo que no acabo de ver es como hacer salir del bucle. Había pensado en cambiar de estado una variable y que fuera condición del bucle, no se. Lo que pasa es que tengo las funciones hechas en una libreria a parte del programa principal y no se como pasar la variable para que la reconozca (hacia mucho tiempo que no programaba en C y me estoy volviendo loco con la visibilidad de las variables intentando que no sean globales)

Para salir de un while() utiliza un break;

Código: C
  1. while(1)
  2. {
  3.         if(Flag_timer_4) { timeout=1; break; }
  4. }

El break te sirve para salirte del os for(), do..while y while


Ok. Ni me acordaba del break.

Muchas gracias!!!