El circuito integrado MAX6675 de Maxim/Dallas Semiconductor es un convertidor analógico a digital para termopares tipo K. Bueno, la verdad es que este circuito tras su apariencia inocente con un encapsulado SOIC de 8 pines, esconde mucho más que un ADC y nos ahorrará bastante espacio al momento de diseñar un circuito impreso.
Dentro de este pequeño circuito se encuentra la electrónica necesaria para amplificar, compensar y convertir a digital el voltaje generado por el termopar, lo que hace muy sencilla la tarea de conectar un termopar a un microcontrolador. El único “pero” es que este circuito solo se consigue en encapsulado SOIC, por lo que no es tan fácil usarlo en el protoboard y hará falta adquirir o fabricar un adaptador para poder experimentar.
Existen muchos sensores de temperatura en el mercado, sin embargo las soluciones basadas en silicio, como el LM35 por citar un ejemplo que sea familiar, están normalmente limitados a un rango de temperatura por debajo de los 150 grados centígrados. Lo que los deja fuera de consideración cuando debemos monitorear algún proceso con temperaturas superiores, afortunadamente los termopares vienen a salvar el día, permitiéndonos mediciones en un rango más amplio, usualmente de varias centenas de grados centígrados y sin un costo excesivo.
Características del MAX6675
Hay algunas características importantes que se deben tomar en cuenta antes de usar este circuito, a continuación las más importantes:
- Interfaz compatible con SPI solo de lectura.
- Resolución de 12 bits, 0.25 grados centígrados.
- Medición hasta 1024 grados centígrados.
- Alimentación de 3.3 a 5 volts.
- Frecuencia de reloj SPI máxima Fscl 4.3 Mhz.
- Tiempo de conversión 0.17 s máximo 0.22 segundos.
- Consumo máximo de 1.5 mA.
Existen más, pero creo que aquí esta lo más importante, para más detalles habrá que consultar la hoja de datos que proporciona el fabricante.
Formato de salida de datos del MAX6675
El MAX6675 se conecta con un microcontrolador mediante una interfaz de 3 lineas compatible con el estándar SPI. El formato en el que el MAX6675 envía datos al microcontrolador es el siguiente.
Como se puede observar, además de la palabra digital correspondiente a la temperatura tenemos un bit que nos indica si el termopar esta abierto (desconectado o roto, por ejemplo) que podemos usar para tomar acciones correctivas o informativas en el software, como disparar una alarma o mostrar un aviso. También hay una buena cantidad de bits que no tienen significado.
Software de ejemplo para PIC24.
Para este ejemplo estaré usando un microcontrolador PIC24, ya que he venido trabajando los últimos meses con estos microcontroladores. Usaremos el módulo SPI con interrupciones del microcontrolador para comunicarnos con el MAX6675. El software que escribí, usa colas (queues) de FreeRTOS para gestionar la entrada de datos por el módulo SPI al que esta conectado el MAX6675 (I/O de datos por interrupcion), sin embargo las funciones de colas e incluso las funciones asociadas a la interrupción pueden descartarse y simplemente usar la técnica de bit polling para esperar hasta que el módulo SPI termine la transferencia, por lo tanto puede usarse este código como base para usar el MAX6675 con cualquier otro microcontrolador sin importar nuestras preferencias o requisitos de programación.
Inicializar módulo SPI: Afortunadamente el MAX6675 no requiere de ninguna inicialización, por lo tanto lo único que hace esta función es cargar los valores iniciales en los registros del módulo SPI del PIC y también se lleva a cabo la inicialización de la cola de FreeRTOS con la que se comunicará el MAX con la aplicación. Esta cola actúa mas bien como un semáforo, por que solo tiene capacidad para almacenar un elemento. Cuando la ISR del módulo SPI recoge el valor leído y lo coloca en la cola, la tarea (task) que estaba esperando el dato puede continuar con su ejecución.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | int iThermocoupleInitialize() // Setup SPI module for MAX6675 operation { // Prepare RTOS resources xThrmcplQueue = xQueueCreate( 1, sizeof( unsigned int ) ); if( xThrmcplQueue == NULL ) return E_THERMOCOUPLE_FAIL; // Initialize MAX6675 chip select PORT and TRIS registers CONFIG_THERMOCOUPLE_CS1_PORT = 1; CONFIG_THERMOCOUPLE_CS2_PORT = 1; CONFIG_THERMOCOUPLE_CS1_TRIS = 0; CONFIG_THERMOCOUPLE_CS2_TRIS = 0; // SPI1 module configuration SPI1CON1 = 0x043A; // Primary prescaler 2:1, secondary 4:1. SPI operating at 2 Mhz @ 16 MIPS SPI1CON2 = 0x0000; // No framed SPI support SPI1STAT = 0X0000; // Clear SPI status flags // Interrupt configuration IFS0bits.SPI1IF = 0; // Clear interrupt flag IEC0bits.SPI1IE = 1; // Enable SPI interrupt SPI1STATbits.SPIEN = 1; // Enable SPI module return E_THERMOCOUPLE_OK; } |
Leyendo el resultado: Si observamos el formato de salida del MAX6675 veremos que los datos transmitidos tienen una buena cantidad de bits de relleno o sin significado, por lo que hay que eliminarlos antes de que el resultado pueda usarse para convertirse a grados centígrados. La función en C que escribí para leer el termopar retorna un unsigned int que corresponde al valor leído desde el MAX6675, NO a la temperatura en grados centígrados. Existe otra función para leer el termopar que además hacer lo mismo que la siguiente función también puede reportarnos si el termopar esta abierto (ver los archivos de codigo fuente).
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | unsigned int iThermocoupleRead( enum eThermocoupleInputs eThermocouple ) { unsigned int iTempBuffer = 0; // ADD HERE CHIP SELECT PINS TO DRIVE MORE MAX6675 CHIPS switch( eThermocouple ) { case E_TC1: CONFIG_THERMOCOUPLE_CS1_PORT = 0; break; case E_TC2: CONFIG_THERMOCOUPLE_CS2_PORT = 0; break; default: return 0; } asm("nop"); asm("nop"); // Wait for CS to stabilize, 100 nS min( view timing diagrams ) SPI1BUF = 0xAAAA; // Transmit dummy byte to start reception xQueueReceive( xThrmcplQueue, &iTempBuffer, portMAX_DELAY ); // The calling task is blocked until the thermocouple reading is received iTempBuffer &= 0x7FF8; // apply mask to leave valid data only iTempBuffer = iTempBuffer >> 3; // align data return iTempBuffer; } |
Rutina de interrupción: En la rutina de interrupción del modulo SPI se realiza la lectura del registro correspondiente al buffer de recepción y se envía la palabra leída a través de la cola para que sea procesada por el código de la aplicación.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | static void prvThermocoupleISR( portBASE_TYPE * pxTaskWoken ) { unsigned int iTempBuffer = 0; iTempBuffer = SPI1BUF; xQueueSendToBackFromISR( xThrmcplQueue, &iTempBuffer, pxTaskWoken ); // Send received word to the queue // ADD HERE CHIP SELECT PINS TO DRIVE MORE MAX6675 CHIPS CONFIG_THERMOCOUPLE_CS1_PORT = 1; // Release chip select lines CONFIG_THERMOCOUPLE_CS2_PORT = 1; IFS0bits.SPI1IF = 0; // Clear module interrupt flag } void __attribute__((interrupt, auto_psv)) _SPI1Interrupt (void) { portBASE_TYPE xTaskWoken = pdFALSE; prvThermocoupleISR( & xTaskWoken ); if( xTaskWoken != pdFALSE ) // Check if interrupt caused a task to wake taskYIELD (); // Force Context Switch } |
Como se puede ver realizar la medición de temperatura es bastante fácil, ni siquiera hay que preocuparse demasiado de los detalles de la conversión A/D. Lo único que se debe tomar en cuenta es que al MAX6675 le toma alrededor de 220 milisegundos como máximo completar una conversión, por lo que como máximo debemos leer el valor de la temperatura unas 4 o 5 veces por segundo. Esto será más que suficiente para la mayoría de las aplicaciones. Hay que recordar que al llamar a la función iThermocoupleIsOpen() equivale a leer la temperatura y por lo tanto además del bit de termopar abierto, vamos a leer la ultima conversión de temperatura que realizo el integrado con su reloj interno. Voy a citar la hoja de datos del integrado:
Force CS low and apply a clock signal at SCK to read the results at SO. Forcing CS low immediately stops any conversion process. Initiate a new conversion process by forcing CS high.
Por lo tanto, si llamamos a la función iThermocoupleIsOpen() e inmediatamente después llamamos a iThermocoupleRead() cualquier proceso de conversión que el MAX pudiera estar realizando entre las llamadas a dichas funciones se detendrá y las dos funciones leerán el mismo valor de temperatura, que es el correspondiente a la última conversión que se pudo completar (aunque la función iThermocoupleIsOpen no toma en cuenta el valor de temperatura, aún así tiene que leerlo). En resumen… hay que respetar el ciclo de CS para el MAX6675 y permitirle 220 milisegundos entre lecturas, de lo contrario hay que estar consientes de lo que esperamos leer:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | int iDiagnosticThermocouple( xHardwareResources xHardware, void * pvActionParams ) { char cBuffer[17];// Holds the string to be printed to the LCD vLcdDriverCommand( lcdCLEAR, xHardware->xLcdDisplay ); // Clear LCD display do { vLcdDriverCommand( lcdHOME, xHardware->xLcdDisplay ); // move pointer/cursor to address 0 if( iThermocoupleIsOpen( E_TC1 ) != E_THERMOCOUPLE_OK ) // check if thermocouple is open vHelpersAltSprintf( cBuffer, "Probe 1: %03u%cC", iThermocoupleRead( E_TC1 )/4, 0xDF ); // If not, read and display the current temperature else vHelpersAltSprintf( cBuffer, "Probe 1: Open" ); // Display thermocouple open vLcdDriverPutStr( cBuffer, xHardware->xLcdDisplay ); // Print line to LCD vTaskDelay(500); } while( xKeypadGetEvent( xHardware->xKeypad, NULL, 0) == pdFALSE ); return 0; } |
Como se puede ver en mi función de diagnostico, se llama a las dos funciones iThermocoupleIsOpen() y iThermocoupleRead() sin ningún retardo entre ellas, por lo tanto, el valor del campo temperature reading será el mismo en ambas lecturas, sin embargo, verán que respeto el tiempo mínimo de conversión cuando espero valores válidos, manteniendo la linea CS en alto por mas de 220 mS (de hecho tiene un retardo de 500 mS). Podría hacerse en definitiva de muchas otras formas, pero esta es la que implemente yo.
Otras consideraciones.
Hay que recordar que el termopar opera con voltajes muy bajos que son amplificados. He notado que las mediciones son bastante sensibles a la interferencia de otros aparatos, por ejemplo, sucede que al acercar el cable de una fuente de poder conmutada de una laptop (que es de dudosa calidad y al parecer mal filtrada) la medición que realiza el MAX6675 muestra valores irreales y me causo un poco de problemas al principio, antes de darme cuenta del problema. En la hoja de datos menciona algunos consejos para mejorar la estabilidad de las mediciones entre las que se encuentran colocar capacitores bypass cerca de los pines de alimentación del MAX6675 y proveer un plano de masa adecuado en el circuito impreso.
Por otra parte quedan pendientes las consideraciones sobre la linealidad del termopar, que requiere más procesamiento de la lectura como la implementación de tablas de búsqueda o solucionar ecuaciones polinomiales de grado superior, consumiendo más recursos del microcontrolador. Afortunadamente el termopar tipo K tiene una respuesta cercana a la lineal y permite una buena aproximación con el programa tal como esta, sin embargo para aplicaciones que requieren mayor precisión o un mayor rango, no podemos saltarnos este paso. Para más información se pueden revisar algunas notas de aplicación de Microchip Technology.
Finalmente agradecer a la gente de Maxim/Dallas Semiconductor por las muestras del MAX6675.
Descarga de los archivos para el termopar.
Aquí tienes los archivos de código fuente en C que contienen todas las funciones necesarias para leer el termopar. Hay que recordar que el ejemplo NO compila directamente ya que depende de FreeRTOS y otros modulos que yo hice. Por otra parte, si ya tienes un proyecto con FreeRTOS deberá ser fácil agregar estos módulos para permitir mediciones de temperatura usando un termopar + PIC24F o dsPIC.
- LINK NO DISPONIBLE. ESTAMOS TRABAJANDO PARA HACERLO FUNCIONAL NUEVAMENTE.
1 | E_TC1 |
hola!! estoy haciendo un proyecto donde quiero usar dos termopares de este tipo pero no se como conectarlos si no cuentan con pin SS para seleccionar la comunicacion
Si tenia tres errores: el primero era la frecuencia #use delay(clock=40000000) la tenia en 4MHz y la cambie a 40Mhz, el segundo fue que el pin del CS no lo ponía en pullup y el tercero era que no estaba utilizando bien la comunicación SPI, mezclaba instrucciones del sPI por software y de hardware.
Y gracias por la buena vibra.
Se te desea lo mismo
Gracias por tu interes Edgar, se agradece.
Saludos Colega 😉
Enhorabuena Edgar.
¿Descubriste a ciencia cierta que tenía?
Suerte con tus proyectos.
Saludos cordiales.
No te preocupes muchas gracias por la atención
Pero por fin pude hacer funcionar el max
gracias
no de hecho no veo nada siempre aparece cero, aun cuando pongo la punta del termopar en una flama.
Ya intente por un librería del max6675 de ccs (y creo que es por SPI por software) y tambien por SPi por hardware y tampoco.
Crees que el max se halla dañado ya? y como podría probar si el max esta dañado o no ?
gracias
Edgar:
Una disculpa por la demora.
No puedo decirte a ciencia cierta si el max esta dañado o no. Pero yo esperaría que si esta dañado enviara datos aleatorios o erráticos, no solamente cero.
Quieres decir que en el pin de datos que va desde el MAX6675 hacia el micro se mantiene estático mientras aplicas pulsos de reloj al MAX??
Si el PIN se mantiene en 0 revisa que este configurado como entrada. También puedes probar desconectando el pin SO y verifica que es lo que obtienes de el manteniendo la linea CS en bajo y aplicando pulsos de reloj al MAX6675.
Saludos.
hola que tal que buen trabajo. yo estoy haciendo un control de temperatura con un pic 18f4550 y el max pero no consigo medir la temperatura en el display simpre me da cero y ya tome las graficas en un osciloscopio que manda el micro y el max y son identicas alas del datasheet. Podrias ayudarme en darme una posible oslucion o como puedo a daptar tu codigo en mi pic que utilizo. gracias
Hola, si realmente ves datos pasando por la interfaz SPI hacia el microcontrolador, posiblemente sea un error en tu programa, si tienes un depurador (ICD2, ICD3) prueba a ver lo que lees desde tu registro/buffer de SPI o si no tienes un depurador, prueba a enviar/imprimir el número entero que devuelve el MAX6675 sin aplicarle ninguna operación aritmética. El programa realmente es fácil de adaptar a cualquier microcontrolador, sin usar un RTOS. En este momento no recuerdo en nombre de los registros, pero podrías sustituir las llamadas a xQueueReceive() por una simple operación que revisa si el hardware SPI esta ocupado recibiendo: while(SPISTAT.bussyBit);.
Hola, sólo quería felicitarte por tu tutorial, está completo y bien explicado. Yo NO uso pics, en su lugar utilizo HCS08, AVR, LPC, etc.
Me dedico al diseño de embebidos y doy clases en la UNAM. Me parece que tienes un excelente nivel, y me gustaría que podamos compartir información. Tengo un par de blogs de electrónica que utilizo como una especie de memoria de diseño. Espero que estés interesado.
Sin más por el momento, recibe un cordial saludo.
Javier
Hola Javier.
Gracias por tu comentario. Claro que me interesa, cualquier intercambio de inforación es bienvenido. Por favor mandame por correo o por aquí en donde tienes tu información.
Saludos.