lunes, 1 de octubre de 2012

MSP430: LM35 + 7Seg

Esta entrada pretende ser una demostración de como un microcontrolador puede controlar y representar la temperatura obtenida por el sensor LM35, tal y como se describió en el anterior capítulo: MSP430: LM35 DZ.

Vamos a trabajar sobre la placa MSP430 Launchpad y volvemos a emplear el microcontrolador MSP430G2452.

Como viene siendo habitual, vamos a emplear un programa sencillo, donde lo que predomine sean los conceptos del manejo y manipulación del dato recogido por el sensor LM35 y en este caso, además, se va a exponer de una manera clara como trabajar con el módulo WatchDog pero en su versión de timer (temporizador).

El material que vamos a necesitar y su función es la que se muestra a continuación:

· MSP430 Launchpad: Evidentemente, nuestra placa de desarrollo con el microcontrolador MSP430G2452.
· LM35: Dispositivo de temperatura.
· Display de 7 Segmentos: Para este caso, emplearemos un módulo (de reloj) de 4 displays de 7 segmentos de cátodo común de la empresa Vishay, cuya referencia y hoja de características es: TDCY1060M.
· Decodificador BCD a 7-Seg: Usaremos un decodificador que controle displays de 7 segmentos de cátodo común, en este caso, hemos elegido el siguiente: MC14511B (este tipo de integrados lo podemos encontrar en el catálogo de varios fabricantes: Motorola, ON Semiconductor, etc).
· Transistores y Resistores: Transistores del tipo NPN para elevar la intensidad que circula por el display de 7 Segmentos.

El funcionamiento es simple, realizaremos un programa donde obtengamos la temperatura del sensor LM35 por la patita P1.5 y que estará conectada al módulo interno ADC 10-bit para realizar la conversión necesaria. Una vez obtenida, la manipularemos para representarla por los displays.

Pero como suele pasar con este tipo de sensores analógicos, el ruido será un acompañante habitual, es por ello que realizaremos un filtro digital que consiste en obtener un número de muestras en un tiempo determinado para posteriormente, realizar su media y éste, será el valor que mostremos por los displays. De esta forma, reduciremos la incidencia del ruido.

Para una explicación más detallada, se recomienda que se lean los comentarios del programa que está más abajo para disposición del usuario su descarga.

El código del programa principal, es el siguiente:

/*----------------------------------------------------------------------
     AqueronteBlog@gmail.com            
                                                      
Este archivo es propiedad intelectual del blog Aqueronte,  
cuya dirección web, es la siguiente:                       
                                                       
    http://unbarquero.blogspot.com/           
                                                       
Se permite cualquier modificación del archivo siempre y cuando
se mantenga la autoría del autor.                          
                                                      
----------------------------------------------------------------------
                                                       
Filename:      main.c                              
Date:          01-October-12                                
File Version:  vs0.0                                     
Compiler:      IAR 5.50.1 [Kickstart]   

Author:        Manuel Caballero                           
Company:       Hades                          
                       
----------------------------------------------------------------------
                                                      
Notes:         En este programa vamos a mostrar como trabajar con el 
               dispositivo de temperatura LM35, mostrando la temperatura
               ambiente por displays de 7 segmentos mediante un decodificador
               BCD a 7 segmentos. Aparte, el programa sirve de ejemplo para
               ver como se usa el WatchDog en su configuración como timer.

               Se implementará un filtro digital, realizando una lectura total
               de 35 mediciones y promediando su valor.


               La lectura de las 35 mediciones y por ende, la actualización de 
               la temperatura a mostrar por los displays se realizarán
               cada ~1.49 segundos.
*/

#include "io430.h"
#include "variables.h"
#include "funciones.c"

void main( void )
{  
   unsigned char i = 0;       // Variable que controla que display mostrar
  
   WDTCTL = WDTPW + WDTHOLD;  // Stop watchdog timer
   
   conf_CLK ();                                                     
   conf_ADC10 ();             // Configura módulo ADC 10-bit
   conf_IO ();                // Configura Entradas/Salidas
   conf_TimerA ();            // Configura Timer A
  
   __enable_interrupt();      // Interrupt enable
   conf_WDT ();               // Configura WatchDog
  
  
   ADC10CTL0 |= ENC + ADC10SC; // Sampling and conversion start
  
   do{
   // Bucle for para la representación y control de los 4 dígitos de 7Seg.
     for (i = 0; i < 4; i++)
     {    
      while (TA0CTL_bit.TAIFG == 0);
      P1OUT = (num >> (12 - 4*i)) & (BIT0 + BIT1 + BIT2 + BIT3 + BIT4);
    
      if (i == 2)               // Encender o Apagar pto. decimal
        P1OUT |= BIT4;
      else
        P1OUT &= ~BIT4;
    
      P2OUT = BIT2 << i;        // Cambia de display
      TA0CTL_bit.TAIFG = 0;     // Reset flag TAIFG
    
      if (CalcTempFlag == 1)    // ¿Nueva temperatura? Calcúlala.
      {      
        num = INT_To_BCD ((int)(temp/TempCont));
      
        temp = 0;               // Reset de ...
        CalcTempFlag = 0;
        TempCont = 0;           // ... variables.
      }
     } 
   }while(1);
}

/*
· ADC10 interrupt service routine

· Description:  Se produce una interrupción cada ~42.67 ms, donde en la variable
                temp, vamos obteniendo el valor acumulado de la lectura, ya
                acondicionada para su posterior tratamiento, del LM35.
                Daremos orden de actualizar el dato mostrado por displays cada
                0.4267·35 ~ 1.493 segundos, mediante el flag CalcTempFlag.
*/  
#pragma vector=ADC10_VECTOR
__interrupt void ADC10_ISR (void)
{  
   WDTCTL = WDTPW + WDTHOLD;   // Stop watchdog timer
  
   if (TempCont == 35)    // 35 Muestras de temperatura                           
   {
     CalcTempFlag = 1;
   }
   else
   {
     temp += ADC10MEM*2.444;  // ~ ((ADC10MEM*2500)/1023);
     TempCont++;
   }
  
   conf_WDT ();
}

/*
· Watchdog Timer interrupt service routine

· Description:  Se produce una interrupción cada ~42.67 ms donde se activa y da
                permiso para una lectura y conversión del módulo ADC10.
*/ 
#pragma vector=WDT_VECTOR
__interrupt void watchdog_timer(void)
{  
   ADC10CTL0 |= ENC + ADC10SC;   // Sampling and conversion start 
}
Un vídeo que demuestra lo explicado anteriormente se presenta a continuación:

Os pongo a vuestra disposición el programa en lenguaje C (IAR y MSPGCC) para que lo podáis descargar y probar:
MSP430: LM35 + 7 Segmentos
Compilador IARCompilador MSPGCC
CC
MSP430: LM35 + 7Seg
MSP430: LM35 + 7Seg
MSP430: LM35 + 7Seg
MSP430: LM35 + 7Seg
MSP430: LM35 + 7Seg
MSP430: LM35 + 7Seg

Como se ha podido ver en el transcurso de esta entrada, controlar y representar a posterior, información de dispositivos analógicos suele ser una tarea idónea para un microcontrolador.

29 comentarios:

Anónimo dijo...

hola buenas tardes y gracias por la informacion mostrada acerca del termometro solo tengo un par de dudas...¿como van estructuradas las entradas y salidas del circuito? y en el video se ven varios circuitos en la parte inferior dereche ¿que son? xfa urge gracias

Unknown dijo...

Buenas:

Si te descargas los archivos del proyecto, podrás ver en la librería de funciones.c, concretamente en la función de configuración de entradas/salidas cómo están estructuradas las salidas y entradas.

De manera esquemática:

- Puerto 1: Se encarga de entregar el dato de temperatura.
· P1.0: Salida. A (Entrada del deco BCD a 7Seg MC14511BCP)
· P1.1: Salida. B (Entrada del deco BCD a 7Seg MC14511BCP)
· P1.2: Salida. C (Entrada del deco BCD a 7Seg MC14511BCP)
· P1.3: Salida. D (Entrada del deco BCD a 7Seg MC14511BCP)
· P1.4: Salida. Punto decimal (Conectado al segmento 7Seg TDCY1060M)
· P1.5: Entrada Analógica. AN5 (Conectado al LM35)

- Puerto 2: Se encarga de seleccionar el dispay
· P2.2: Salida. Dígito 1 encendido
· P2.3: Salida. Dígito 2 encendido
· P2.4: Salida. Dígito 3 encendido
· P2.5: Salida. Dígito 4 encendido

Con respecto a lo que mencionas que ves en la parte inferior derecha, son, simplemente, transistores BJT NPN y es para potenciar la luminosidad del display de 7 segmento.

Un saludo.

Anónimo dijo...

si muchas gracias,no me habia percatado de esta libreria,en verdad te lo agradesco y reitero el saludo...salud y muchos mas proyectos!! :-)

Anónimo dijo...

hola a todos tengo dudas de como configurar un pin de un puerto como entrada para prender un led en el msp430 este es el código(no funciona):

#include "msp430g2553.h"

int main(void)
{
WDTCTL = WDTPW + WDTHOLD; // Stop watchdog timer
P1DIR |= BIT0; // Set P1.0 to output direction
P1DIR &= ~BIT5; // Set P1.0 to output direction
P1IN= BIT5; // P1.5 se pone como entrada
P1OUT=BIT0; //set P1.0 se pone como salida


BCSCTL3 |= LFXT1S_2; // clock system setup
IFG1 &= ~OFIFG;
_bis_SR_register(SCG1 + SCG0);
BCSCTL2 |= SELM_3 + DIVM_3;

while(1)
{
if(P1IN == 1)
P1OUT = 1;
else
P1OUT = 0;
}
}

Unknown dijo...

Buenas:

Mirando por encima tú código, configuras el uC de la siguiente manera:

· WacthDog Desactivado.
· Patita P1.0 Salida Digital
· Patita P1.5 Entrada Digital
· Oscilador externo.

Y lo que creo que quieres hacer es monitorizar la entrada P1.5 para cuando esté activa (P1.5 = 1), la salida P1.0 se ponga activa, en caso contrario, la salida se pondrá en bajo.

Pues bien, creo que no entiendes bien cómo configurar el módulo de entrada/salida del uC, para configurar una patita como entrada tenemos al registro PxDIR para tal fin, en tú caso:

· P1DIR &= ~BIT5;

Hasta aquí todo bien, pero la línea de código siguiente:

· P1IN = BIT5;

Debes eliminarla, estás dándole un valor a la entrada, dicho registro es de lectura de la siguiente manera:

while (1){
if ((BIT5 & P1IN) == 1)
P1OUT |= BIT0; // P1.0 = 1
else
P1OUT &= ~BIT0; // P1.0 = 0
}

Por ello, la línea de código siguiente:

· P1OUT = BIT0;

Debes quitarla también, ya que no configura la patita P1.0 como salida, sino que activa la patita P1.0, es decir: P1.0 = 1.

Mírate bien el manual de los módulos para la familia de tu uC para comprender bien cómo se configuran.

Un saludo y espero haberte ayudado.

Anónimo dijo...

hola buen día gracias por contestar sigo en duda, ya seguí los pasos que dijiste pero aun así no funciona, funciona solo si le pongo BIT5 en la condicion así(es correcto??. ya que mi objetivo es leer un encoder y mostrarla en la pc por eso ando haciendo pruebas para ver lo de configurar entradas ya que tendré dos entradas A y B del encoder al uC):

int main(void)
{
WDTCTL = WDTPW + WDTHOLD;//
Stop watchdog timer
P1DIR |= BIT0; // Set P1.0 to output direction
P1DIR &= ~BIT5; // Set P1.0 to input direction

BCSCTL3 |= LFXT1S_2; // clock system setup
IFG1 &= ~OFIFG;
_bis_SR_register(SCG1 + SCG0);
BCSCTL2 |= SELM_3 + DIVM_3;

while(1)
{
if((BIT5&P1IN) == BIT5)
P1OUT |= BIT0;
else
P1OUT &= ~BIT0;
}
}

Unknown dijo...

Buenas:

Si es cierto, se me metió el uno dichoso donde no era, en otras palabras, la comparación debe ser la siguiente:

while (1){
if (BIT5 & P1IN)
P1OUT |= BIT0; // P1.0 = 1
else
P1OUT &= ~BIT0; // P1.0 = 0
}

Teniendo en cuenta que la operación del if:

· if (BIT5 & P1IN)

Realiza una operación AND bit a bit del puerto P1, por lo tanto, tenemos que monitorizar que el bit 5 está activo.

Un saludo y disculpa la errata.

Anónimo dijo...

No sirven los links :(, un saludo y gracias por el post

Unknown dijo...

Buenas:

El último enlace si funciona (servidor box)

Un saludo.

Rodrigo dijo...

Hola, estoy corriendo el codigo en Code composer studio, y tengo problemas con esta instruccion TA0CTL_bit.TAIFG, me marca una advertencia, hay error de sintaxis, no me acepta el punto (.) y he estado investigando otra manera de escribir esa instruccion y no he encontrado, podrias decirme que es lo que significa el punto?? GRACIAS...

Unknown dijo...

Buenas Rodrigo:

Tienes que tener en cuenta que el código mostrado está desarrollado con el compilador IAR, es por ello que ciertas definiciones puedan dar problemas en otros compiladores.

Aún así, es posible su adaptación.

En este caso, la instrucción que te da problemas es la siguiente:

· TA0CTL_bit.TAIFG

Dicha instrucción lo que realiza es inspeccionar el bit TAIFG (flag de interrupción del Timer A) que está dentro del registro TA0CTL.

La cuestión del punto, es debido a que el compilador IAR tiene declarado como estructura los bits del registro TA0CTL.

Las estructuras, no es más que un tipo especial de declaración de variables en C.

Un saludo.

Rodrigo dijo...

Hola! Muchas gracias... quedo resuelto. Solo que tengo problemas en el despliegue, mi display solo tiene 12 pines, y estoy teniendo problemas en que no me muestra el (g) la linea de en medio, por ejemplo cuando debe de poner 8, pone 0, pero eso solo me pasa con el D4 y D2, alguna sugerencia? no creo que deba de ver con el código! mi duda es que se supone la pata que prende el g deberia de prender para todos no es así? y porque solo me enciende para uno solo. El D2 parpadea un poco la raya de en medio, pero no se llega a prender completamente.

Unknown dijo...

Buenas Rodrigo:

Ten presente que nosotros usamos un decodificador BCD de 7-Seg. Las conexiones las puedes ver en los comentarios del código (cuando te lo descargues) o en el segundo comentario de esta página.

Te recomiendo que compruebes las conexiones de tu circuito, y si todo está bien, prueba con aumentar la frecuencia de refresco del display.

Un saludo.

Rodrigo dijo...

Muchas gracias, se ha arreglado, pense que ya habia comentado mi agradecimiento pero no se publico. Una ultima duda, quisiera dejar prendido un led cada vez que esta en un rango de temperatura de 36.5 a 37 grados, pero lo unico que hace es switcharme el led y solo parpadea!! el codigo que introduje fue este:
if((temp<370.123)&&(temp>365.123)){
P1OUT|=BIT6;
}

y lo hice en el main, dentro del do while despues de preguntar si i==2.

Gracias por la atención.

Rodrigo

Unknown dijo...

Buenas Rodrigo:

Haz una depuración paso a paso (o mejor, mediante breakpoints) e inspecciona el valor de la variable temp.

Quizás, dicha variable oscila entre los valores que estás monitorizando (ya que tu rango es pequeño, solo 0.5).

El problema con el sensor de temperatura LM35 es que debes asegurarte que su salida esté estabilizada mediante filtros, ya sea por hardware o software.

Un saludo.

Unknown dijo...

buenas trat de correr el programa y solo me tira 2 errores que son los siguientes
Error[e46]: Undefined external "__enable_interrupt" referred in main ( C:\Users\Weaver\Desktop\LM35DZ\Debug\Obj\ main.43)


Error while running Linker

si alguien sabe por favor ayuda

Unknown dijo...

Buena Rubén:

Acabo de compilar el código que te puedes descargar desde el enlace ofrecido y no ofrece ningún error.

¿Qué compilador estás usando? En este caso, el que se usa es el de la compañía IAR.

En caso de usar CCS, la función para activar las interrupciones globales es la siguiente:

· _BIS_SR(GIE);


Un saludo.

Unknown dijo...

IAR Embedded Workbench
ya trate de la nueva forma con la parte _BIS_SR(GIE); pero aun me tira el mismo error a no ser que este haciendo algo mal. Si es así por favor hacérmelo notar


http://imageshack.us/a/img600/4378/1dom.jpg

Unknown dijo...

Buenas Alfonso:

Por lo que puedo ver en la imagen que has adjuntado, el IDE de IAR no te ha incluido la siguiente librería:

· "intrinsics.h"

Dicha librería es la que contiene la definición de: __enable_interrupt() y por ello, te da el error.

Vamos a ver, dicha librería debería incluirse automáticamente cuando creas y configuras un proyecto, ya que en la propia definición del dispositivo debería estar presente.

Vamos a ir paso a paso para detectar el problema.

· 1. En el IDE, parte izqueirda, donde pone Files, están los siguientes archivos, entre otros, main.c, Output, funciones.c, etc.

Bien, abre el archivo que pone: io430g2452.h.

· 2. Cuando lo hagas, dentro de ese archivo, en la cuarta línea (más o menos) debería aparecer la siguiente instrucción:

· #include "intrinsics.h"

· 3. En caso de no aparecer, deberás ponerla tú mismo, al principio del archivo main.c.

· 4. En caso de que dicha instrucción esté presente (que debe de estar), quizás no hayas configurado el proyecto adecuadamente para el microcontrolador que se va a usar, para ello, te recomiendo que para configurar el IDE adecuadamente, sigas los pasos del siguiente capítulo:

· http://unbarquero.blogspot.com.es/2010/10/ejemplo-iar-kickstart-con-launchpad.html

En caso de que nada consiga solucionarlo, crea el proyecto tú mismo e introduce el archivo main.c.

No se me ocurre nada más, es un error raro ya que el propio IDE debe agregarte dicha librería, y no se porqué en tú caso no está presente.

Un saludo.

Unknown dijo...

ya quedo el archivo io430g2452.h. no deja tuve que agregarlo en el main como indicabas tenias razón muy buena practica muchas gracias por la ayuda un saludo.

Unknown dijo...

oye Manuel me surgieron unas cuantas dudas en cuanto a los puertos libres es posible configurarlos para que a cierta temperatura se mande un pulso? y otra es si es posible me pase el diagrama de conexión creo que no eh conectado bien el decodificador y no cuadran los números tal cual deberían ser saludos y racias

Unknown dijo...

Buenas Rubén:

Lo que quedan libres son algunas patitas del microcontrolador, no puertos. Y por supuesto que puedes configurarlas como tu quieras, por ejemplo, la patita P1.7, que está conectada al LED2 de la Launchpad, podrías usarla para que a cierta temperatura se encienda.

Respecto al conexionado del sistema completo, te recomiendo que revises la librería funciones.c, donde aparece todo descrito.

Aún así, en los comentarios anteriores, alguien me pregunto por lo mismo y se los puse por escrito, para no repetir, te dejo el enlace directo al comentario:

· Conexión del sistema.

· PD: Por cierto, revisando mensajes veo que te llamé Alfonso, lo siento mucho.


Un saludo.

Unknown dijo...

si ya vi como se conecta no vi el comentario muchas gracias y en cuanto a lograrlo encender con alguna patita no eh podido seguiré intentando. saludos no te preocupes la intención fue ayudar.

Unknown dijo...

Buenas Manuel me podría ayudar a tratar de configurarlo para que mande un pulso a cierta temperatura encienda o mande un pulso ya trate un rato pero no eh tenido exito un saludo gracias

Unknown dijo...

Buenas Rubén:

Para empezar, deberás declarar o definir una patita del microcontrolador como salida (será la encargada de encender el LED).

Una vez tengamos nuestra patita establecida para encender el LED a una cierta temperatura, solo nos falta decidir en que parte del código vamos a realizar tal operación.

Por ejemplo, siguiendo con el programa expuesto en este capítulo, en el siguiente trozo de código:

if (CalcTempFlag == 1)
{
num = INT_To_BCD ((int)(temp/TempCont));

if (num > EL_NÚMERO_QUE QUIERAS)
P1OUT |= BIT0;
else
P1OUT &= ~BIT0;

...
}

Como verás, en la variable num se almacena la temperatura en bcd-compactado, una vez tengamos dicho número, lo podemos comprobar mediante la instrucción if.

En el ejemplo que te he puesto, si la variable num es mayor a cierta temperatura, la patita P1.0 se pone en alta, en caso contrario, estará en baja.

Ahora, en tú programa, deberás definir que patita es la que quieres usar y que temperatura es la que quieres comprobar.

Un saludo.

Unknown dijo...

gracias una vez mas me has salvado un saludo

Anónimo dijo...

Hola, ya cheque todas las conexiones y no consigo que me salga la temperatura, me marca siempre en 0 los 4 digitos

Unknown dijo...

Buenas:

Si no has modificado el programa original, que te puedes descargar gratuitamente desde los enlaces disponibles, debe funcionar perfectamente si has seguido todas las recomendaciones tanto de este capítulo como las que se describen en los archivos descargados.

Y como muestra, está el vídeo.

Un saludo.

Unknown dijo...

Hola alguien me puede ayudar como programar un LM 35 con un display de 7 segmento en arduino