INFORMÁTICA INDUSTRIAL. 3º ITI ELECTRÓNICA. Práctica 3: Microcontrolador 8051: Programación en C para monitorización a través de display LCD, de temperatura y tensión. Objetivos. Con esta práctica se pretende ilustrar el uso de la programación en C para microcontroladores usando interrupciones. Así como el funcionamiento de los displays LCDs, de los convertidores A/D y de los "timers" en los microcontroladores. El alumno deberá traer a la práctica tanto el enunciado como la documentación del microcontrolador Cygnal 8051 entregada durante el curso, y la documentación adicional de esta práctica referente al display LCD. Durante la práctica el alumno hará una primera toma de contacto con la placa ATC-C8051A, encendiendo y apagando los 2 leds de que dispone dicha placa, a una frecuencia determinada sin hacer uso de ningún recurso del micro. Posteriormente realizará la misma operación haciendo uso de los timers para controlar los tiempos. Y en la última etapa de la práctica realizará un código que mediante interrupción, activada por fin de conversión del ADC, y que muestre por pantalla LCD la temperatura del sensor interno del microcontrolador, y la tensión que entra por una patilla del C8051, usando para visualizar los resultados un display LCD. La práctica se puede dividir en dos grandes apartados, con diversas fases cada uno: 1. Control básico con el microcontrolador C8051 de Cygnal. 1.1. Control de LEDs a una frecuencia determinada mediante polling. 1.2. Control de LEDs a una frecuencia marcada por Timer2 sin interrupciones. 2. Control avanzado con el microcontrolador C8051 de Cygnal. 2.1. Control de LEDs mediante interrupción lanzada por el ADC en fin de conversión. El ADC se activa cuando se desborda Timer2. 2.2. Ampliación de la rutina de interrupción para que cambie la fuente de ADC, de forma periódica, entre el termómetro interno y una entrada analógica de los GPIO. 2.3. Mostrar por pantalla LCD los resultados del ADC capturados por la interrupción. Displays de cristal líquido. Los visualizadores más usados en los entornos de los computadores son de dos tipos: de tubo de rayos catódicos (CRT) o de cristal líquido. Los primeros son los clásicos monitores con tecnología similar a la usada por los televisores domésticos. Las principales ventajas de este tipo de sistemas es su relativo bajo coste, consiguiéndose muy buena resolución y una gran gama de colorido (con enormes posibilidades gráficas). Sin embargo este tipo de visualizadores tienen dos grandes inconvenientes: el consumo y el tamaño. Si además se tiene en cuenta que en muchas ocasiones no son necesarias unas prestaciones gráficas tan elevadas, se entiende como han surgido con tanta fuerza los visualizadores tipo LCDs. Los "displays" LCD están compuestos por un "cristal liquido" retenido entre dos placas transparentes conductoras. Dicho cristal cambia su estado de visualización dependiendo del potencial a que se somete las placas conductoras, estas deben estar divididas en pequeñas celdas controlables independientemente para poder activar cada elemento que compone el visualizador. Según se puede deducir de esta descripción básica, la primera característica 1 que destaca de los LCDs es su bajo consumo, las placas conductoras se comportan como condensadores que se cargan o descargan. La segunda característica a resaltar es la posibilidad de miniaturización, en la actualidad hay televisores portátiles del tamaño de un reloj de pulsera con pantallas LCDs en color. El "cristal líquido" del que están compuestos los LCDs puede actuar de dos formas diferentes: cambiando la reflectividad o la transparencia. En el primer caso es la luz que incide y se refleja sobre la pantalla la que permite ver la información, en el segundo es la luz que pasa a través de la pantalla la que descubre el mensaje. Todos los LCD usan ambos métodos de visualización, pero dependiendo de cual es el principal se puede distinguir dos tipos de LCD: de iluminación frontal y retroiluminados. Los primeros son los más normales, su uso se ha extendido en relojería, el principal problema de este tipo de LCDs es que el ángulo de visualización suele ser pequeño. Los retroiluminados son menos frecuentes, disponen de una superficie reflectora en la que se apoyan las dos placas que contienen el "cristal líquido", además suelen incluir un sistema de iluminación trasero artificial mediante lámpara. La ventaja principal de este tipo de LCDs es que el ángulo de visualización es mucho mayor, además al tener luz propia pueden visualizarse en lugares de escasa iluminación, sin embargo el coste de este tipo de LCD es mucho mayor. La tensión que se debe aplicar a cada una de las placas conductoras no puede ser continua (posibilidad de rotura), debe ser una onda cuadrada, si las dos placas tienen dicha señal en fase el "cristal liquido" es transparente, si no están en fase es opaco. Por tanto, en principio, para gobernar un display LCD se necesitarían dos señales para cada elemento activo celdilla). Lo normal es que el control de cada celdilla y de las señales necesarias se lleve a cabo por un controlador específico que independice completamente la visualización de la aplicación que se pretenda desarrollar. En la mayoría de los casos se encuentran el controlador y la pantalla LCD integrado en una sola pieza, de forma que basta una "interface hard" simple y unos pocos comandos para gobernar completamente un visualizador, por muy compleja que sea la pantalla LCD. Antecesores de los LCD son los visualizadores basados en diodos "LEDs", en este tipo de dispositivos el mensaje se compone según se encuentre encendido o apagado un diodo; las pantallas de los primeros relojes digitales de pulsera eran de este tipo ("números rojos o verdes"), la principal dificultad de los visualizadores LEDs es el consumo y la miniaturización. Realmente se puede conseguir pantallas LEDs pequeñas pero de muy poca resolución, es por ello que el uso de LEDs en visualizadores pequeños se limita a aquellas aplicaciones en que la información a representar es pequeña, como son visualizadores de 7 segmentos (relojes digitales), barras (equipos de audio), etc. Sin embargo en aplicaciones en las que el tamaño de la pantalla es apreciable es preferible el uso de sistemas basados en diodos LEDs: anuncios comerciales de gran tamaño, marcadores electrónicos en instalaciones deportivas, anuncios en autobuses, oficinas, ventanillas, etc. En estos casos la información puede ser compleja, inclusive información gráfica de bastante resolución. En el mercado se pueden encontrar una amplia gama de visualizadores LCD. Los más simples son los usados por los relojes digitales, que incluyen dígitos de 7 segmentos más unos cuantos símbolos o caracteres fijos (AM, PM, ALM, etc.). Otros son muy complejos, como las pantallas de los ordenadores portátiles, que pueden tener características similares a las usadas en los ordenadores de sobremesa, pero con un coste más elevado. Diversos fabricantes de LCDs ofertan diseños específicos para aquellas aplicaciones empotradas con suficiente número de unidades a construir (por ejemplo displays LCDs que usan: los GPS de SONY, algunas impresoras láser, voltímetros digitales, automóviles, etc.). 2 Esta gran diversidad de visualizadores LCDs podría hacer pensar que también hay una gran variedad de controladores. Sin embargo, en lo que se refiere a displays alfanuméricos de uso general, se ha impuesto un sistema controlador desde hace varios años, de forma que en la actualidad es prácticamente un "estándar": el Hitachi HD44780 (forma sistema con el HD44100). Este circuito puede gobernar una gran diversidad de LCDs, en principio desde pantallas de 1 línea por 16 caracteres hasta de 2 líneas por 40. Extensiones del HD44780 permiten gobernar displays alfanuméricos más complejos. Con respecto a los displays LCD gráficos hay una mayor variedad de controladores, los más tradicionales son el MSM6255 y el HD64645 (software compatible con el más clásico de los controladores para CRT, el HD6845). En esta práctica se usará un display alfanumérico retroiluminado (el LM091LN, de 2 líneas por 20 caracteres de 5x7 "dots"), controlado por el HD44780. Al final del texto explicativo de la práctica se pueden encontrar fotocopias de los manuales del LM091LN y del HD44780. El LCD LM091LN y el controlador HD44780. El HD44780 dispone de 192 caracteres en ROM, más la posibilidad de definir otros 8 por parte del diseñador (en RAM). Dispone de dos tipos de RAM: una donde se almacena el carácter que se visualiza en cada posición del LCD (DD RAM) y otra que sirve para definir nuevos símbolos (CG RAM). La cantidad de memoria para la visualización de caracteres es de 80 bytes, de forma que cada posición del display se asocia a una posición de memoria en la DD RAM, por tanto un display como el usado en la práctica de 2 x 20 sólo usa la mitad de la DD RAM, siendo la otra mitad inútil para la visualización (a no ser que se produzcan desplazamientos que pasen los datos a la mitad visible). De forma general siempre la primera línea empieza en la posición $80 (realmente 00) y la segunda, si la hubiese, en la $C0 (realmente $40). Si fuese un display de 4 líneas por 20, la tercera línea empezaría después de la primera y la cuarta después de la segunda. La transferencia entre el sistema procesador (68HC11 en nuestro caso) y el HD44780 puede ser mediante un bus de 8 o 4 bits. Con los microcontroladores en modo "single-chip" es tradicional usar la "interface" de 4 bits para ahorrar puertos, sin embargo en sistemas con bus de datos, direcciones y control externos es común usar 8 bits. El controlador de LCD dispone de dos registros accesibles desde el exterior, el registro de instrucciones (IR) y el de datos (DR), la señal RS indica cuando se accede a uno u otro. Estos se pueden leer o escribir dependiendo de la señal R/W. Por tanto las cuatro operaciones básicas son: Escribir en el IR: En este caso se envía un comando al LCD, que puede ser: • Borrar Display (pone cursor en HOME): b 0 0 0 0 0 0 0 1 • Poner cursor en Home: b 0 0 0 0 0 0 1 X • Modo de entrada: b 0 0 0 0 0 1 I/D S S=1 Display se desplaza al escribir un nuevo carácter S=0 Display no se desplaza " .... I/D=1 Incremento automático del AC (contador de direcciones, apuntador) I/D=0 Decremento automático del AC 3 • Control del display: b 0 0 0 0 1 D C B B=1 Activa parpadeo cursor B=0 Desactiva parpadeo cursor C=0 Cursor no visible C=1 Cursor visible D=0 Apaga LCD D=1 Enciende LCD • Desplazamiento del cursor/display: b 0 0 0 1 S/C R/L X X R/L=0 Desplazamiento a la izquierda R/L=1 Desplazamiento a la derecha S/C=0 Desplazamiento se aplica sobre el cursor S/C=1 Desplazamiento se aplica sobre todo el display • Control por 4 u 8 bits: b 0 0 1 D/L N F X X D/L=0 Control 4 bits (se usan las líneas de datos DB4-DB7), se manda primero parte alta, seguida de parte baja. D/L=1 Control con 8 bits. N=0 display con una línea activa N=1 display con dos líneas activas F=0 caracteres de 5x10 "dots" F=1 caracteres de 5x7 "dots" • Poner dirección CG RAM: b 0 1 CG5 CG4 CG3 CG2 CG1 CG0 Después de este comando los datos que se escriban se cargarán en la CG RAM. • Poner dirección DD RAM: b 1 DD6 DD5 DD4 DD3 DD2 DD1 DD0 Después de este comando los datos que se escriban se cargarán en la DD RAM y se visualizarán por el LCD. Leer del IR: cuando se lee este registro informa con el bit 7 si está o no listo para recibir un nuevo dato o comando (BF = BUSY FLAG), este bit hay que "testearlo" antes de escribir en el HD44780 (BF=1 ocupado, BF=0 desocupado y listo para recibir otro dato o comando). Los restantes bits (del 0 al 6) informan del AC (contador de dirección), éste apunta a la dirección actual de la DD RAM (posición del cursor en el LCD) y de la CG RAM. Escribir en el DR: permite escribir en la posición actual (apuntada por AC) un dato o en la DD RAM (pantalla LCD) o en la CG RAM. Leer del DR: Permite leer de la posición apuntada por AC un dato de la DD RAM o de la CG RAM. El AC (contador de dirección) cada vez que se escribe un dato se incrementa o decrementa dependiendo del modo seleccionado. El código de los caracteres que visualizan por el LCD se corresponde con el ASCII (ver fotocopias adjuntas, Apéndice 3), siendo los 8 primeros caracteres (del 0 al 7) los correspondientes a los símbolos definidos en la CG RAM (tabla 3.4 de manual del HD44780). Para cargar un símbolo en la CG RAM debe de colocarse en que posición de la CG RAM desea cargarse, y posteriormente enviar 8 datos con el formato del símbolo. Hay 4 64 posiciones de CG RAM, para formar 8 posibles símbolos con 8 bytes de formato (ver fotocopias del HD44780). La placa ATC-C8051-A. Esta placa ha sido desarrollada por el departamento de Arquitectura y Tecnología de Computadores. El núcleo de la placa en un microcontrolador Cygnal 8051, que está alimentado a través del bus USB, y que es accesible a través de un pin en la placa. El micro tiene sus puertos P0-P2 accesibles desde el exterior por medio de arrays de pines. Dispone además de un puerto JTAG para programar y depurar el microcontrolador. En la siguiente figura se muestra un esquema de dicha placa. 2 1 L1 L2 J1 USB 2 1 1 3 J4 2 J2 JTAG J3 1 2 1 2 Figura 1: Esquema de la placa ATC-C8051-A Descripción de los conectores: - JTAG: Interfaz con el adaptador serie EC2 de depuración del Cygnal. - USB: Conector USB esclavo. Sólo usado para alimentación de 5v en esta práctica. - L1 y L2: LEDs conectados a los puertos J1.1 y J1.3 respectivamente. - J1, J2 y J3: Terminales de GPIO. Únicamente los pines impares corresponden con líneas de entrada/salida del microcontrolador. Todas las líneas pares son GND. Corresponden con los puertos P0 a P2 según la siguiente tabla: J1 P2 1 7 3 6 5 5 7 4 9 3 11 2 13 1 15 0 - J2 1 3 5 7 9 11 13 15 P1 7 6 5 4 3 2 1 0 J3 1 3 5 7 9 11 13 15 P0 7 6 5 4 3 2 1 0 J4: Reset y alimentación, según la siguiente tabla: J4 Descripción 5 1 2 3 USB Vdd (5v) GND Reset (si conectado a GND) Desarrollo de la práctica. 1. Control básico con el microcontrolador C8051 de Cygnal. 1.1. Control de LEDs a una frecuencia determinada mediante polling. En este primer apartado se pretende encender y apagar los LEDs de la placa ATCC8051-A a una frecuencia determinada por el número de iteraciones de un bucle en el main de nuestro programa. Haciendo uso de la siguiente plantilla rellene el código necesario dentro del main para conseguir el parpadeo de los LEDs. Tenga en cuenta que un número pequeño de iteraciones puede implicar un parpadeo tan rápido que el propio LED no tenga tiempo de encenderse y apagarse, con lo que se vería un poco atenuado, pero sin parpadeo. /* INFORMÁTICA INDUSTRIAL 3º ITI ELECTRÓNICA. EUP DPTO. ARQUITECTURA Y TECNOLOGÍA DE COMPUTADORES UNIVERSIDAD DE SEVILLA PROF. ALEJANDRO LINARES BARRANCO ******************************************/ #include <c8051f320.h> // Declaraciones SFR // System clock selections (SFR CLKSEL) #define SYS_INT_OSC 0 //-----------------------------------------------------------------------// Constantes Globales //-----------------------------------------------------------------------sbit LED3 = P2^6; // LED='1' significa encendido sbit LED4 = P2^7; //-----------------------------------------------------------------------// Prototipos de funciones globales //-----------------------------------------------------------------------void PORT_Init (void); void SYSCLK_Init (void); //-----------------------------------------------------------------------// Variables globales //-----------------------------------------------------------------------unsigned long int i; //-----------------------------------------------------------------------// Routina principal //-----------------------------------------------------------------------void main (void) { PCA0MD &= ~0x40; // Deshabilita el Watchdog timer PORT_Init (); // Inicializa Crossbar y GPIO SYSCLK_Init (); // Inicializa oscilador LED3=0; LED4=0; while (1){ // Bucle infinito. // Poner led3 a 1 y led4 a 0. // Esperar un tiempo con un bucle for. // Poner led3 a 0 y led4 a 1. // Esperar el mismo tiempo que antes. } } //-----------------------------------------------------------------------// Rutinas de Inicialización //-----------------------------------------------------------------------//--------------------------- 6 // SYSCLK_Init //--------------------------// Inicialización de SYSLCK // - Inicializa el reloj del sistema. void SYSCLK_Init (void) { unsigned char delay = 100; OSCICN |= 0x83; CLKMUL = 0x00; CLKMUL |= 0x80; while (delay--); CLKMUL |= 0xC0; while(!CLKMUL & 0x20); CLKSEL |= SYS_INT_OSC; // // // // // // // // // Configura el oscilador interno a su frecuencia máxima Selecciona al oscilador interno como entrada del multiplicador de reloj Habilita el multiplicador de reloj Espera algo más de 5us Inicializa el multiplicador de reloj Espera a que el multiplicador esté preparado Selecciona el reloj del sistema } //--------------------------// PORT_Init //--------------------------// Inicialización de Puertos // - Configuración del Crossbar y los puertos GPIO. void PORT_Init (void) { P2MDIN = 0xDE; // P2.0 y P2.5: Entrada analógica P0MDOUT = 0xFF; // P0.0-P0.6: push-pull P1MDOUT = 0xFF; // P1.0-P1.7: push-pull P2MDOUT = 0xCC; // P2.3-P2.2: push-pull P2SKIP = 0x01; // P0.7 saltado en el Crossbar XBR0 = 0x00; // XBR1 = 0x40; // Habilitar el Crossbar } 1.2. Control de LEDs a una frecuencia marcada por Timer3 sin interrupciones. En este apartado de la práctica hemos de añadir la inicialización del timer3 del C8051, e invocarlo cada vez que queramos hacer una espera y resetearlo cuando concluya dicha espera. Para la realización de este apartado usar la plantilla anterior añadiendo la función que inicialize el timer3 para ser usado con 16 bits y autorecarga, sin interrupción. Modificar la plantilla anterior y sustituir los bucles for por activaciones del timer 3 y esperas de fin de cuenta. //--------------------------// Timer3_Init //--------------------------// Inicialización del Timer 3 // - Timer3 configurado para desbordarse a la frecuencia // definida por <SAMPLERATE> para las conversiones del ADC // void Timer3_Init () { TMR3CN = 0x00; // Detener Timer3; Limpiar TF3; CKCON &= 0xBF; // SYSCLK/12 como fuente de reloj del Timer3 TMR3RL = 0x0000; // Tope de cuenta de Timer3 TMR3 = 0x0000; // y se recarga inmediatamente. } El tope de la cuenta del Timer3 se ha puesto al máximo que se permite con el reloj interno, que implica una espera de 65.5 ms antes de cambiar el estado de los LEDs. 2. Control avanzado con el microcontrolador C8051 de Cygnal. 2.1. Control de LEDs mediante interrupción lanzada por el ADC por fin de conversión. El ADC se activa cuando se desborda Timer2. 7 En este apartado de la práctica vamos a programar el timer2 para que se incremente con la frecuencia de SYSCLK y que se desborde cada –(SYSCLK/SAMPLERATE), siendo SAMPLERATE la cadencia de muestreo que deseamos para el convertidor. El timer2 se programará con autorecarga para ahorrar operaciones en la rutina de interrupción. El convertidor ADC se programará para que inicie la conversión cada vez que se desborde el timer2, usando un reloj SAR de 2MHz, con los datos justificados a la derecha, con medición single-ended (el polo negativo a GND), y con interrupción de fin de conversión habilitada. La rutina de interrupción corresponde con la IRQ 10 del microcontrolador y en ella vamos a incrementar un contador interno. Cada vez que el contador llegue a 20000 volverá a 0. Si la cuenta es menor de 10000 el estado de los LEDs debe ser opuesto a si la cuenta es superior o igual a 10000. El programa principal permanecerá ciclado en un bucle infinito sin hacer nada: while (1); La rutina de inicialización del timer2 y del ADC se muestran a continuación, así como la plantilla para la rutina de interrupción. sfr16 TMR2RL = 0xca; sfr16 TMR2 = 0xcc; #define SYSCLK 23560000 #define SAMPLERATE 100000 #define AMX_TEMP_SENSE 0x1E #define AMX_P2_0 0x08 #define AMX_GND 0x1F // Timer2 reload // Timer2 counter // SYSCLK frequency in Hz // ADC0 Sample Rate // Temperature sensor // P2.0 (potentiometer) // Ground //-----------------------------------------------------------------------// Routina principal //-----------------------------------------------------------------------void main (void) { PCA0MD &= ~0x40; // Desactiva Watchdog timer PORT_Init (); // Inicializa Crossbar and GPIO SYSCLK_Init (); // Inicializa oscillator Timer2_Init (); // Inicializa timer2 ADC_Init (); // Inicializa ADC EA = 1; // Enable global interrupciones LED3=1; LED4=1; cnt=0; while (1){ // Bucle infinito. } } //-----------------------------------------------------------------------// Rutina de servicio de interrupción //-----------------------------------------------------------------------//--------------------------// ADC0_ISR //--------------------------// - Guarda el dato convertido por el ADC // - Actualiza el multiplexor para la próxima conversión // - Cada 10000 llamadas a la interrupción se modifica el estado de los LEDs // void ADC0_ISR (void) interrupt 10 { // Incremento del contador // Cada 10000 desbordamientos de timer2 se modifica el estado de los LEDs } 8 //-----------------------------------------------------------------------// Rutinas de Inicialización //-----------------------------------------------------------------------//--------------------------// ADC_Init //--------------------------// Inicialización del ADC // - Modo Single-ended // - Comienza la conversión cuando el Timer2 se desborda (overflows) // - Datos justificados a la izquierda // - Activa el modo trackign a baja potencia debido a la variación analógica // de la entrada (para apartado 2.2) // - Habilita las Interrupciones void ADC_Init (void) { REF0CN = 0x0E; // Habilita el sensor de Temperatura, // el bias interno // y la tensión de referencia interna // (para apartado 2.2) AMX0P = AMX_TEMP_SENSE; // Conecta el sensor de temp. como entrada positiva AMX0N = AMX_GND; // y la tierra (GND) como la negativa // (single-ended) ADC0CF = (SYSCLK/2000000) << 3; // SAR clock configurado a 2MHz ADC0CF &= 0xFB; // Datos justificados a la derecha ADC0CN = 0xC2; // ADC0 habilitado, con modo // tracking de baja potencia y // convirtiendo cuando se desborda Timer2 EIE1 |= 0x08; // Habilita la interrupción de conversión // finalizada } //--------------------------// Timer2_Init //--------------------------// Inicialización del Timer 2 // - Timer2 configurado para desbordarse a la frecuencia // definida por <SAMPLERATE> para las conversiones del ADC // void Timer2_Init () { TMR2CN = 0x00; // Detener Timer2; Limpiar TF2; CKCON |= 0x10; // SYSCLK como fuente de reloj del Timer2 TMR2RL = -(SYSCLK/SAMPLERATE); // Timer2 se desborda con <SAMPLERATE> TMR2 = 0xFFFF; // y se recarga inmediatamente. TR2 = 1; // Comienza Timer2 } 2.2. Ampliación de la rutina de interrupción para que cambie la fuente de ADC de forma periódica. En este apartado simplemente hemos de tocar la rutina de interrupción para que modifique la fuente del convertidor ADC cada vez que se cambia el estado del LED3. Para ello hemos de añadir una sentencia ‘if’ que modifique el valor de AMX0P para que alterne entre el sensor de temperatura y la patilla 2.0 de los GPIO. 2.3 Mostrar por pantalla LCD los resultados del ADC capturados por la interrupción. Por último, se pretende que nuestra placa entrenadora ATC-8051-A se conecte con un display LDC para mostrar los valores de temperatura y tensión que estamos midiendo al tiempo que cambiamos los LEDs de estado. La temperatura obtenida en el convertidor ADC se convierte a grados centígrados siguiendo la siguiente fórmula: 9 T= 4+((33000*ADC/1024)-77600)/286 Y la tensión se convierte en mili voltios siguiendo la siguiente fórmula: V=3300*ADC/1024; Para ello, se ofrece una serie de funciones que permiten la interacción con el display: - LCD_Init() inicializa el display activando las dos filas de caracteres, borrando la pantalla y llevando el cursor al inicio (Home) y apagándolo. LCD_DisplayString(int fila, int columna, char *texto) Escribe en la posición que se indique (fila,columna) la cadena de texto correspondiente. Se debe tener presente que sólo tenemos 2 filas y 20 columnas. Esta rutina llama a otras de más bajo nivel que interactúan con el display. Para ello hace uso del timer3, el cual debe estar configurado para que haga una espera de 32,7 ms cada vez que se active. Utilice la siguiente plantilla para realizar este apartado: //-----------------------------------------------------------------------// Routina principal //-----------------------------------------------------------------------void main (void) { char ch_temp[20],ch_volt[20]; unsigned int t,v; PCA0MD &= ~0x40; // Desactiva Watchdog timer PORT_Init (); SYSCLK_Init (); Timer2_Init (); Timer3_Init (); ADC_Init (); EA = 1; VDD = 1; TEST=1; LCD_Init(); // // // // // // Inicializa Crossbar and GPIO Inicializa oscillator Inicializa timer2 Inicializa timer3 Inicializa ADC Enable global interrupciones // Inicialización del display LED3=1; LED4=1; cnt=0; LCD_DisplayString(1,1,"Temperatura:"); LCD_DisplayString(2,1,"Tension:"); while (1){ // Bucle infinito. Mostramos por Display LCD la temperatura // y el valor de tensión de P1.7 // que la rutina de interrupción irá modificando. t=4+(((long int)IN_PACKET[0]*330000/1024)-77600)/286; sprintf(ch_temp,"%4d",t); LCD_DisplayString (1,14, ch_temp); LCD_DisplayString (1,19,"oC"); v=(3300*(unsigned long int)IN_PACKET[1])/1024; sprintf(ch_volt,"%4d",v); LCD_DisplayString (2,14, ch_volt); LCD_DisplayString (2,19,"mV"); } } /* ** LCD_Init: Initialize the LCD. 10 */ void LCD_Init (void) { LCD_InitDriver(); LCD_Clear(); LCD_CursorOff(); } /* ** LCD_DisplayCharacter: Display a single character, ** at the current cursor location. */ void LCD_DisplayCharacter (char a_char) { LCD_WriteData (a_char); } /* ** LCD_DisplayString: Display a string at the specified row and column. */ void LCD_DisplayString (char row, char column, char *string) { LCD_Cursor (row, column); while (*string) LCD_DisplayCharacter (*string++); } /* ** LCD_CursorOff: Turn the cursor off. */ void LCD_CursorOff (void) { LCD_WriteControl (0x0c); } /* ** LCD_Cursor: Position the LCD cursor at "row", "column". */ void LCD_Cursor (char row, char column) { switch (row) { case 1: LCD_WriteControl (0x80 + column - 1); break; case 2: LCD_WriteControl (0xc0 + column - 1); break; default: break; } } /* ** LCD_Clear: Clear the LCD screen (also homes cursor). */ void LCD_Clear (void) { LCD_WriteControl(0x01); } /* ** LCD_WriteControl: Write a control instruction to the LCD */ void LCD_WriteControl (unsigned char dat) { int i; // RS=0, R/W=0, E=0 RS=0; RW=0; EN=0; DB0=dat%2; DB1=(dat/2)%2; DB2=(dat/4)%2; DB3=(dat/8)%2; DB4=(dat/16)%2; DB5=(dat/32)%2; DB6=(dat/64)%2; DB7=(dat/128)%2; // RS=0, R/W=0, E=1 11 RS=0; RW=0; EN=1; // RS=0, R/W=0, E=0 RS=0; RW=0; EN=0; // RS=0, R/W=1, E=0 RS=0; RW=1; EN=0; TMR3CN |= 0x04; // Comienza Timer3 i=0; while (!(TMR3CN & 0x80)) i++; // Espera a que se desborde el timer TMR3CN = 0; // Detiene el Timer3 //TMR3 = 0x8000; // Inicializa la cuenta del timer. } /* ** LCD_WriteData: Write one byte of data to the LCD */ void LCD_WriteData (unsigned char dat) { int i; // RS=1, R/W=0, E=0 RS=1; RW=0; EN=0; DB0=dat%2; DB1=(dat/2)%2; DB2=(dat/4)%2; DB3=(dat/8)%2; DB4=(dat/16)%2; DB5=(dat/32)%2; DB6=(dat/64)%2; DB7=(dat/128)%2; // RS=1, R/W=0, E=1 RS=1; RW=0; EN=1; // RS=1, R/W=0, E=0 RS=1; RW=0; EN=0; // RS=1, R/W=1, E=0 RS=1; RW=1; EN=0; TMR3CN |= 0x04; // Comienza Timer3 while (!(TMR3CN & 0x80)); // Espera a que se desborde el timer TMR3CN = 0; // Detiene el Timer3 //TMR3 = 0x8000; // Inicializa la cuenta del timer. } 12