La práctica consiste en usar el micro 80C196KD con una placa de expansión para implementar una estación meteorológica. Mediante un panel se mostrará:
Las siguientes fotografías muestran el aspecto de la placa de ampliación una vez finalizadas todas las conexiones que hemos comentado:
En el montaje tratamos cinco entradas diferentes, tres de ellas son entradas A/D (temperatura, humedad y dirección del viento), una HSI (velocidad del viento), y por último una interrupción externa (pulsador); como interfaz de salida utilizamos un display LCD.
En el puerto P0 se encuentran las entradas analógicas; de las ocho entradas posibles, se han utilizado tres. Éstas han sido configuradas de la siguiente manera: P0.1 (patilla 29 JP2) para tomar muestras de temperatura, P0.2 (patilla 31 JP2) para muestras de humedad y P0.3 (patilla 35 JP2) para la dirección del viento. Cada vez que solicitamos una conversión, se ha de seleccionar, mediante el registro ad_command, la patilla del P0 que tiene la información que nos interesa. Como las entradas en realidad eran simuladas por potenciómetros, la Vcc y Gnd de estos se tomaron de las patillas 2 y 1 de JP2.
Para poder hacer el cálculo de la velocidad del viento, se simula mediante un generador de señal una entrada de pulsos (tics). Debido a que esta señal puede producir falsas lecturas, se utiliza también una puerta en histéresis (40106, patilla 3 entrada, patilla 4 salida), de esta forma nos aseguramos de que el ruido de la señal no provocará falsas HSI. La salida de la puerta en histéresis, al estar ya filtrada, se conecta a la entrada HSI.0 (patilla 12 JP2).
El montaje del pulsador (interrupción externa) consta de varios elementos: una resistencia y un condensador conectados en serie, un pulsador conectado en paralelo con el condensador y una puerta en histéresis (40106, patilla 1 entrada, patilla 2 salida); en este caso también es necesario filtrar la señal producida por el pulsador, ya que al conmutar, se producen una serie de flancos fantasma que podrían provocar la lectura de falsas pulsaciones. La entrada de la puerta en histéresis se conecta en el punto en el que se unen: pulsador, resistencia y condensador. La salida del trigger, al tener ya filtrada la pulsación, se conecta directamente a P2.2 (patilla 28 JP1); las Vcc y Gnd de la puerta en histéresis y del filtro formado por el condensador y la resistencia , se toman de las patillas 1 y 19 de JP1.
Para la salida de información utilizamos un display conectado al P1, cuyas Vcc y Gnd se han tomado de las patillas 2 y 1 de JP2. El display necesita además un potenciómetro para regular el contraste. Las Vcc y Gnd de este potenciómetro también se han tomado de las patillas 2 y 1 de JP2.
La primera tarea que realiza nuestro programa es la de llamar a las funciones encargadas de inicializar todos los dispositivos involucrados en el sistema (LCD, Timer, AD & HSI). Una vez establecidos los parámetros de cada dispositivo, se entra en el bucle principal del programa.
Mediante el botón se selecciona el modo activo (0, 1, 2, 3) en el gráfico. Dependiendo del modo selecionado, se lanzarán conversiones digitales o se guardarán interrupciones en de la hsi (con sus respectivos cambios de escala).
El tratamiento de medias se comentará más adelante en detalle, pero la idea básica es recoger valores cada segundo y mostrar el resultado de la media cada 10 segundos.
Paralelamente a las anteriores operaciones, se mantiene un contador mediante el timer2 que nos servirá para mostrar la hora. Más adelante también se detalla el cálculo teórico que nos permite decir que 20 overflows equivale a un segundo.
El reloj del 196KD va 20 Mhz. A razón de 1 state time = 2 ciclos, hay 10M st por segundo. Como hemos configurado el Timer2 en "modo normal", se incrementa una vez cada 8 st, o sea, 10M/8 = 10485760/8 = 1310720 incrementos por segundo. Como el Timer2 es de 16 bits, se desborda cada 2^16 incrementos, por tanto 1310720/(2^16) = 1310720/65536 = exactamente 20 veces por segundo. En total, cada 20 overflows del Timer2 representan un segundo; eso hace fácil contar el tiempo desde la propia RSI (cada 20 ejecuciones de la RSI, incrementar el número de segundos).
Para poder mostrar el valor interno recogido por el conversor AD a un valor representativo, tenemos que usar factores de conversión simples procurando que ningún término de la operación se desborde (valor >65535). Estas operaciones simples se pueden observar en el código.
El cálculo más complicado es el de "velocidad de los tics HSI" a km/h al medir la velocidad del viento. Para que no se desborde la variable, se hace el cálculo en milisegundos, y teniendo en cuenta que el Timer puede haberse desbordado varias veces entre un tic y el siguiente. En el código están detalladas las conversiones necesarias.
La temperatura y la dirección del viento se muestrean cada segundo, pero el valor mostrado sólo se actualiza cada 10 segundos, con una media de los 10 últimos valores instantáneos.
Usamos la media aritmética normal: en una variable se van acumulando los 10 valores, y cuando llega el décimo, dividimos entre 10. Con un entero (16 bits) para el sumatorio es suficiente, ya que la temperatura puede ser como mucho 50 grados (50*10=500) y la dirección del viento 360 grados (360*10=3600), por tanto el acumulador siempre valdrá menos de 65535.
Todas las RSI (botón, HSI, Timer2) inhiben las interrupciones, pero se ejecutan muy rápido. La del botón y la del reloj son triviales (unas pocas operaciones matemáticas), y la de HSI siguen siendo cálculos de tiempo constante, sin ningún bucle.
La de los HSI consiste en: 2 sumas/restas, 4 productos/divisiones, y varias asignaciones. Todo esto se tendrá que ejecutar antes de que llegue el siguiente tic por HSI para que los cálculos sean correctos. Si un tic llegase demasiado rápido, se perdería ya que las interrupciones están desactivadas mientras se ejecuta esta RSI. A velocidades razonables para un anemómetro, el código funciona bien.
Aspecto de la placa y funcionamiento:
Añadidos visuales: Hemos considerado oportuno añadir barras visuales (bloques negros que se aprecian en las fotos), para indicar el nivel de humedad y temperatura en estos dos modos.
Este fichero contiene el programa principal (main). Lo primero que hace es inicializar los registros de los diferentes componentes de la placa que se usarán (ext_int, timer2, ad y hsi's).
Las funciones relativas a cada dispositivo se encuentran en ficheros separados para facilitar su mantenibilidad. Se separan dentro de cada uno de estos ficheros, las inicializaciones y el código específico que deben ejecutar para los propósitos de la práctica (inits_ad y conversio_ad, por ejemplo).
Cabe destacar la función del bucle principal que selecciona la acción a hacer de entre los diferentes modos, en función del valor de la variable 'modo' que es actualizada por el botón conectado a ext_int.
Además, también actualiza el reloj que se muestra en todos los modos, cada segundo (mediante la espera que realizamos con el while anidado). También se aprovecha para tomar muestras de las magnitudes que necesitan media (velocidad y dirección del viento).
Obviando las inicializaciones del timer2 (inits_timer2()), la parte más relevante de este fichero es la RSI. Dicha RSI mantiene la cuenta de los overflows que se producen en el timer2, contando 1 segundo para cada 20 overflows (ver sección de cálculos teóricos para su explicación).
La temperatura, humedad y dirección usan 3 de los 8 canales del AD para realizar sus conversiones. Posteriormente se realiza un cambio de escala para adaptar el valor interno a la representación que queremos (grados centígrados, porcentaje y grados sexagesimales respectivamente).
Para medir la velocidad del viento usamos las HSI's. Como el anemómetro puede girar muy poco a poco, lo que hacemos es contar el tiempo que ha pasado entre un tic HSI y el siguiente, y luego hacer una conversión (explicada en la sección de cálculos teóricos).
Contiene las primitivas de control del display LCD, tal como se definieron en prácticas anteriores. Algunas de ellas han sido modificadas para evitar problemas al redibujar caracteres en el display y para hacerlas más genericas (siendo así usables en otras partes del proyecto).
Contiene rutinas de alto nivel para el manejo del display LCD. Usa las funciones primitivas definidas en lcd.c. También están definidas funciones que muestran barras visuales para los modos humedad y temperatura, y los cálculos necesarios para saber qué texto hay que mostrar con los nombres de velocidades y direcciones del viento.
#include <kd_sfrs.h> #define NUM_MODOS 4 //Cuantos modos queremos ? // Variables globales int modo; // Modo actual extern unsigned int timer2_seg; // Segundos extern signed int tem_disp; extern unsigned int hum_disp; extern unsigned int vel_disp; extern unsigned int dir_disp; extern unsigned int vel_inst; unsigned int vel_sum; // Sumatorio de velocidades, para hacer la media unsigned int dir_sum; // Sumatoria de ángulos de dirección del viento void main() { int temp, temp1, temp2; // Temporales // inits // Botón asm {di;} wsr=0; int_mask=int_mask|0x80; // activamos la interrupcion (EXTINT) int_mask1=int_mask1&0xDF; // Desactivamos INT13 (EXT_PIN=0) wsr=0; ioc1=ioc1 & 0xFD; //EXT_INT=0 para elegir p2.2 asm {ei;} // Otros inits_lcd(); inits_timer2(); inits_ad(); inits_hsi(); modo=0; vel_sum=0; dir_sum=0; //main loop // Este bucle se ejecuta cada segundo para tomar las muestras necesarias, // hacer medias, y mostrarlas en pantalla. while(1) { switch (modo) { // Temperatura. A/D, sin media case 0: temp=conversio_ad(1); // convertir de [0-255] a [-20 - 50] temp=((temp*70)/255)-20; tem_disp=temp; mostrar_tem(); visual_tem(); break; // Humedad. A/D, sin media case 1: temp=conversio_ad(2); // convertir de [0-255] a [0-100 %] temp=(temp*100)/255; hum_disp=temp; mostrar_hum(); visual_hum(); break; // Velocidad viento. HSI, con nombres, con media case 2: // Ya tenemos vel_disp actualizada, ya que se hace en cada // iteración del bucle (cada segundo) mostrar_vel(); break; // Dirección viento. A/D, con nombres, con media case 3: // Ya tenemos dir_disp actualizada, ya que se hace en cada // iteración del bucle (cada segundo) mostrar_dir(); break; // Modo incorrecto default: show_msg_lcd("Unimplemented mode code",2,1); break; } // La velocidad (HSI) y dirección (A/D) del viento requieren media, y por tanto // hay que mantenerla siempre actualizada, independientemente del modo en el que // estemos. temp=conversio_ad(3); // dirección del viento // convertir de [0-255] a [0-360 grados] // *(360/255). Para evitar desbordamientos, temp=(temp*72)/51; vel_sum+=vel_inst; // Acumular dir_sum+=temp; // Acumular if (timer2_seg%10==0) // Si han pasado 10 segundos { vel_disp=vel_sum/10; // Actualizar el valor mostrado dir_disp=dir_sum/10; vel_sum=0; dir_sum=0; } // Mostrar reloj, común para todos los modos show_char_lcd(':',11,2); show_char_lcd(':',14,2); temp=timer2_seg%60; //segundos show_value_lcd(temp,2,'0',16,2); temp=timer2_seg/60; //minutos temp%=60; show_value_lcd(temp,2,'0',13,2); temp=timer2_seg/60; temp/=60;//horas temp%=24;//hora: de 0 a 23 show_value_lcd(temp,2,'0',10,2); // Esperar hasta que haya pasado un segundo o cambiemos de modo. // Como tomamos muestras cada segundo, no habrá nada nuevo // que mostrar en pantalla hasta la siguiente muestra. temp1=timer2_seg; temp2=modo; while( temp1==timer2_seg && temp2==modo); // Si se ha cambiado de modo, borrar la pantalla if (temp2!=modo) CLRSCR_LCD(); // Fin del bucle principal } } //RSI's void RSIExtInt() { asm{di;} modo=(modo+1)%NUM_MODOS; asm{ei;} }
#include <kd_sfrs.h> #define OVFS_POR_SEG 20 unsigned int timer2_seg; unsigned int timer2_ovf; unsigned int auxiliar; extern unsigned int t2_ovf_hsi; void inits_timer2 () { timer2_ovf=0; timer2_seg=0; /* Deshabilitem les interrupcions i les PTS */ asm { dpts; di; } wsr=0x00; int_mask1 |= 0x10; // T2OVF wsr=0x0f; auxiliar=ioc0; wsr=0x00; auxiliar &= 0xf7; ioc0=auxiliar; // desactivo EXTRST del timer2 wsr=0x0f; auxiliar=ioc2; wsr=0x00; auxiliar |= 0x02; ioc2=auxiliar; // Configurem el TIMER2 per comptar UP en normal mode, al rang // FFFFh-0000h. wsr=0x01; ioc3 = ioc3 | 0x01; // Seleccionem el clock intern pel TIMER2 wsr=0x00; timer2=0; // Carreguem els valors inicials als comptadors asm { ei;} } void RSITimer2() { // Incrementa el comptador de interrupcions del timer i avisa que sa produit una interrupcio // i reprograma el timer2 asm {di;} t2_ovf_hsi++; // Para poder contar bien el tiempo entre HSI y HSI timer2_ovf++; // contamos en overflows pero transformamos a segundos if (timer2_ovf==OVFS_POR_SEG) { ++timer2_seg; timer2_ovf=0; } // El timer2 sigue contando, automáticamente asm {ei;} }
#include <kd_sfrs.h> void inits_ad() { unsigned int regtemp; asm { di; } wsr=0; //Aunque podemos usar cualquier ventana para int_mask/int_mask1 int_mask=int_mask & 0xFD; // Deshabilitamos int del AD (realizaremos encuesta, no int) /* Modifiquem el registre IOC2, seleccionem les opcions del conversor.*/ wsr=0xf; regtemp=ioc2; regtemp = regtemp & 0xF7; // AD_TIME_ENA=0 para modo compatible con 196KB wsr=0x0; ioc2=regtemp; wsr=0xf; regtemp=ad_command; regtemp = regtemp | 0x10; // 8 bits wsr=0x0; ad_command=regtemp; /* Finalment cal habilitar les interrupcions per poder servir-les. */ asm { ei;} } unsigned int conversio_ad(char canal) { /* Este bucle espera a que se haya acabado la interrupción que lee el contenido del registro de resultado del conversor AD (rutina RSIADDone). Una vez acabada se muestra el resultado por el array de leds de la placa placa */ unsigned int regtemp; wsr=0x0; // Para leer ad_result while (ad_result&0x08); // Esperar a que haya acabado (encuesta) asm { di; } wsr=0xf; regtemp=ad_command; if (canal==1) { regtemp&=0xF9; regtemp|=1; } // ...001 else if (canal==2) { regtemp&=0xFA; regtemp|=2; } // ...010 else if (canal==3) { regtemp&=0xFB; regtemp|=3; } // ...011 regtemp = regtemp | 0x08; // GO=1 wsr=0x0; ad_command=regtemp; asm { ei; } while (ad_result&0x08); // Esperar a que haya acabado (encuesta) asm { di; } regtemp=ad_result; regtemp=regtemp>>8; regtemp &= 0xFF; asm { ei; } return regtemp; }
#include <kd_sfrs.h> unsigned int instant_ant; // Momento en el que ha llegado la HSI anterior unsigned int t2_ovf_hsi; // Contador de overflows unsigned int vel_inst; // Velocidad instantánea, en km/h // 2=2, K=0.15, PI=3.14. Per tant, #define DOS_K_PI 1 static unsigned int auxiliar; void inits_hsi() { /* Deshabilitem les interrupcions i les PTS */ asm { dpts; di; } wsr = 0x00; int_mask |= 0x04; // HSIData Available, cal configurar IOC1.7 wsr = 0x0f; auxiliar = ioc0; wsr = 0x00; auxiliar |= 0x01; ioc0 = auxiliar; // activo hsi0 wsr = 0x0f; auxiliar = ioc1; wsr = 0x00; auxiliar &= 0x7f; ioc1 = auxiliar; // La Hsi Data Available (HSIINT) es generada quan // es carrega el Holding Register wsr = 0x0f; auxiliar = hsi_mode; wsr = 0x00; auxiliar |= 0x01; // cada transició positiva HSI.0 provocarà interrupció. hsi_mode = auxiliar; instant_ant=0; t2_ovf_hsi=0; vel_inst=0; asm { ei; } } void RSI_Hsi() { unsigned int instant; // Valor del Timer2 (en tics) cuando llega la HSI signed int ha_passat; // 'signed' para contabilizar bien en caso de overflow // Ej con 2 díg: sube de 45 a "15" (en realidad 115), num_ovf=1 // El total será 1*100 + (15-45) = 70 extern int vel_disp; // Comptar els numero de tics que arriven asm { di; } // Leer hsi_time para cargar el siguiente valor wsr = 0x0; instant = hsi_time; ha_passat=instant-instant_ant; // Ara tenim el número de tics que han passats, // juntament amb el número d'overflows que hi ha hagut. // 20 OVFs del T2 == 1 segon. // Per tant, 50 ms per OVF. // // 20*2^16 = 1310720 tics = 1 segon // Per tant, 1310 tics per ms. // // mspertic = num_ovf*50+ha_passat/1310 // Aquest és el nombre de mseg que ha trigat el tic: mseg/tic // El seu invers, tics/mseg, és la freqüència // En tics/seg, f=1000/mspertic // metrperseg=2*k*pi*f // kmperh=metrperseg*3600/1000 // // En total queda: kmperh=3600/1000 * 2*k*pi * 1000/mspertic // Simplificat: kmperh= 3600/mspertic * 2*k*pi vel_inst=t2_ovf_hsi*50+ha_passat/1310; // mspertic vel_inst=3600/vel_inst; // ... vel_inst*=DOS_K_PI; // kmperhora // En el bucle principal ya se cogerá esta velocidad instantánea (cada segundo), // y se hará la media cada 10. // Preparar-nos pel següent tic instant_ant=instant; // (encara que s'ignora el que ha trigat en executar-se la RSI) t2_ovf_hsi=0; asm { ei; } }
#include <kd_sfrs.h> #include "lcd.h" /****************************************************************************** ; Declaració de constants del display ;****************************************************************************** ;conexionat: ; p1.4..p1.7 = D4..D7 ; p1.0 = RS ; p1.2 = E ; 0 = R/W siempre escribimos */ #define LCD_INS 0xfe #define LCD_DAT 0x01 #define LCD_ENA 0x04 #define LCD_LINEA2 0xC0 #define LCD_LINEA1 0x80 #define LCD_CLRSCR 0x01 /* RETARD DE 1.64 ms */ void WRITE_LCD_4BITS(); void INICIALITZA_LCD() { /* ;0 X000 0000B = 00H -> IOPORT1 ;1 X000 0001B = 01H ;15 X000 1111B = 0FH */ DELAY_10mS(); DELAY_10mS(); wsr=0; ioport1=0x30 & LCD_INS; WRITE_LCD_4BITS(); DELAY_10mS(); wsr=0; ioport1=0x30 & LCD_INS; WRITE_LCD_4BITS(); DELAY_10mS(); wsr=0; ioport1=0x30 & LCD_INS; WRITE_LCD_4BITS(); DELAY_10mS(); wsr=0; ioport1=0x20 & LCD_INS; /* interface de 4 bits */ WRITE_LCD_4BITS(); DELAY_10mS(); /*AHORA YA PODEMOS USAR LA FUNCION DE INSTRUCION LCD*/ /*FUNCTION SET- 4 bits dades, 2 lines, caracter font*/ INSTRUCCIO_LCD(0x28); /*DISPLAY ON/OFF-DISPLAY ON, CURSOR OFF, CURSOR NO BLINK*/ INSTRUCCIO_LCD(0x0C); INSTRUCCIO_LCD(0x06); /*ENTRY MODE SET-INCREMENT CURSOR, CURSOR MOVEMENT*/ INSTRUCCIO_LCD(0x01); /*CLEAR DISPLAY*/ DELAY_10mS(); /*ESTE RETARDO DEBERIA DE SER DE 1.64mS*/ } void WRITE_LCD_4BITS() { /*AQUESTA FUNCIO ES NOMES PER US INTERN DE LA LLIBRERIA DEL DISPLAY*/ asm{NOP;NOP;} wsr=0; ioport1 = ioport1 ^ LCD_ENA; /*XORB IOPORT1,#LCD_ENA*/ asm{ NOP;NOP;NOP;NOP;NOP; NOP;NOP;NOP;NOP;NOP; } wsr=0; ioport1 = ioport1 ^ LCD_ENA; /*XORB IOPORT1,#LCD_ENA*/ asm{NOP;} } void INSTRUCCIO_LCD(unsigned char inst) { wsr=0; ioport1=0xF0 & inst & LCD_INS ; /* RS=0 */ WRITE_LCD_4BITS(); inst<<=4; asm{ NOP;NOP; NOP;NOP;NOP; } wsr=0; ioport1 =0xF0 & inst & LCD_INS; WRITE_LCD_4BITS(); DELAY_40uS(); } void DADA_LCD(unsigned char dato) { wsr=0; ioport1=0xF0 & dato; /*parte alta primero*/ ioport1=ioport1|LCD_DAT; /*RS=1*/ WRITE_LCD_4BITS(); dato<<=4; asm{ NOP;NOP; NOP;NOP;NOP; } wsr=0; ioport1=0x0F0 & dato; ioport1=ioport1|LCD_DAT; WRITE_LCD_4BITS(); DELAY_40uS(); } void CLRSCR_LCD() { INSTRUCCIO_LCD(LCD_CLRSCR); DELAY_10mS(); } void DELAY_10mS() { unsigned int TIMER_LCD; TIMER_LCD=2000; while(TIMER_LCD!=0) { TIMER_LCD--; } } void DELAY_40uS() { unsigned int TIMER_LCD; TIMER_LCD=40; while(TIMER_LCD!=0) { TIMER_LCD--; } } void GOTOXY(unsigned char x, unsigned char y) /* 1<=x<=16 , 1<=Y<=2 */ { unsigned char gotopos; gotopos=0x80 | ((y-1)*0x40) | (x-1); INSTRUCCIO_LCD(gotopos); }
#include <kd_sfrs.h> #include <lcd.h> #include <string.h> #define LCD_INS 0xfe #define LCD_DAT 0x01 #define LCD_ENA 0x04 #define LCD_LINEA2 0xC0 #define LCD_LINEA1 0x80 #define LCD_CLRSCR 0x01 /* RETARD DE 1.64 ms */ // Las variables con los valores correctos, listos para ser mostrados en pantalla signed int tem_disp; unsigned int hum_disp; unsigned int vel_disp; unsigned int dir_disp; void WRITE_LCD_4BITS(); void INICIALITZA_LCD() { /* ;0 X000 0000B = 00H -> IOPORT1 ;1 X000 0001B = 01H ;15 X000 1111B = 0FH */ DELAY_10mS(); DELAY_10mS(); wsr=0; ioport1=0x30 & LCD_INS; WRITE_LCD_4BITS(); DELAY_10mS(); wsr=0; ioport1=0x30 & LCD_INS; WRITE_LCD_4BITS(); DELAY_10mS(); wsr=0; ioport1=0x30 & LCD_INS; WRITE_LCD_4BITS(); DELAY_10mS(); wsr=0; ioport1=0x20 & LCD_INS; /* interface de 4 bits */ WRITE_LCD_4BITS(); DELAY_10mS(); /*AHORA YA PODEMOS USAR LA FUNCION DE INSTRUCION LCD*/ /*FUNCTION SET- 4 bits dades, 2 lines, caracter font*/ INSTRUCCIO_LCD(0x28); /*DISPLAY ON/OFF-DISPLAY ON, CURSOR OFF, CURSOR NO BLINK*/ INSTRUCCIO_LCD(0x0C); INSTRUCCIO_LCD(0x06); /*ENTRY MODE SET-INCREMENT CURSOR, CURSOR MOVEMENT*/ INSTRUCCIO_LCD(0x01); /*CLEAR DISPLAY*/ DELAY_10mS(); /*ESTE RETARDO DEBERIA DE SER DE 1.64mS*/ } void WRITE_LCD_4BITS() { /*AQUESTA FUNCIO ES NOMES PER US INTERN DE LA LLIBRERIA DEL DISPLAY*/ asm{NOP;NOP;} wsr=0; ioport1 = ioport1 ^ LCD_ENA; /*XORB IOPORT1,#LCD_ENA*/ asm{ NOP;NOP;NOP;NOP;NOP; NOP;NOP;NOP;NOP;NOP; } wsr=0; ioport1 = ioport1 ^ LCD_ENA; /*XORB IOPORT1,#LCD_ENA*/ asm{NOP;} } void INSTRUCCIO_LCD(unsigned char inst) { wsr=0; ioport1=0xF0 & inst & LCD_INS ; /* RS=0 */ WRITE_LCD_4BITS(); inst<<=4; asm{ NOP;NOP; NOP;NOP;NOP; } wsr=0; ioport1 =0xF0 & inst & LCD_INS; WRITE_LCD_4BITS(); DELAY_40uS(); } void DADA_LCD(unsigned char dato) { wsr=0; ioport1=0xF0 & dato; /*parte alta primero*/ ioport1=ioport1|LCD_DAT; /*RS=1*/ WRITE_LCD_4BITS(); dato<<=4; asm{ NOP;NOP; NOP;NOP;NOP; } wsr=0; ioport1=0x0F0 & dato; ioport1=ioport1|LCD_DAT; WRITE_LCD_4BITS(); DELAY_40uS(); } void CLRSCR_LCD() { INSTRUCCIO_LCD(LCD_CLRSCR); DELAY_10mS(); } void DELAY_10mS() { unsigned int TIMER_LCD; TIMER_LCD=2000; while(TIMER_LCD!=0) { TIMER_LCD--; } } void DELAY_40uS() { unsigned int TIMER_LCD; TIMER_LCD=40; while(TIMER_LCD!=0) { TIMER_LCD--; } } void GOTOXY(unsigned char x, unsigned char y) /* 1<=x<=16 , 1<=Y<=2 */ { unsigned char gotopos; gotopos=0x80 | ((y-1)*0x40) | (x-1); INSTRUCCIO_LCD(gotopos); } void show_value_lcd(unsigned int wValor, int lon, char rell, int x, int y) { unsigned char xifra; unsigned int valor; int i=0; valor=wValor; while (valor > 0 || wValor==0) { GOTOXY(x-i,y); xifra=valor%10+'0'; valor=valor/10; DADA_LCD(xifra); wValor=1; //para no volver a entrar en el bucle i++; } while (i<lon) { GOTOXY(x-i,y); DADA_LCD(rell); i++; } } void show_char_lcd(unsigned int wValor, int x, int y) { GOTOXY(x,y); DADA_LCD(wValor); } void inits_lcd() { INICIALITZA_LCD(); CLRSCR_LCD(); } void show_msg_lcd(char missatge[], int x, int y) { unsigned int i=0; GOTOXY(x,y); while (i<strlen(missatge)) { DADA_LCD(missatge[i]); i++; } } /**Funciones para mostrar valores de los modos**/ void mostrar_tem() { signed int tem_abs; char signo; tem_abs=tem_disp; // La temperatura puede ser negativa if (tem_abs<0) { signo='-'; // '-' tem_abs=-tem_abs; } else { // El ' ' sobreescribirá al '-' si pasamos de tem. negativa a positiva signo=' '; // ' ' } show_msg_lcd("temperatura",1,1); show_char_lcd(signo,12,1); show_value_lcd(tem_abs,2,' ',14,1); show_char_lcd('C',16,1); show_char_lcd(0xDF,15,1); } void visual_tem() { signed int i=-2; signed int conv=(tem_disp)/10; GOTOXY(1,2); while(i<0) { DADA_LCD((conv<=i)?0xDB:' ' ); i++; } while(i<5) { DADA_LCD( (conv>=i)?0xFF:' ' ); i++; } } void mostrar_hum() { show_msg_lcd("humedad",1,1); show_value_lcd(hum_disp,3,' ',15,1); show_char_lcd('%',16,1); } void visual_hum() { int i=0; int conv=(7*hum_disp)/100; GOTOXY(1,2); while(i<conv) { DADA_LCD(0xFF); i++; } while(i<7) { DADA_LCD(' '); i++; } } void mostrar_vel() { char* velocitats[] = { "calm ", "light air ", "light breeze ", "gentle breeze ", "moderate breeze ", "fresh breeze ", "strong breeze ", "near gale ", "gale ", "strong gale ", "storm ", "violent storm ", "hurricane " }; unsigned int i; show_value_lcd(vel_disp,3,' ',3,2); show_msg_lcd("km/h",4,2); // vel_disp en km/h i=0; if (vel_disp>1) i=1; if (vel_disp>6) i=2; if (vel_disp>11) i=3; if (vel_disp>19) i=4; if (vel_disp>29) i=5; if (vel_disp>39) i=6; if (vel_disp>50) i=7; if (vel_disp>61) i=8; if (vel_disp>74) i=9; if (vel_disp>87) i=10; if (vel_disp>101) i=11; if (vel_disp>118) i=12; show_msg_lcd(velocitats[i],1,1); } void mostrar_dir() { char* direccions[] = { "tramunt.", "gregal ", "llevant ", "xaloc ", "migjorn ", "llebeig ", "ponent ", "mestral " }; signed int i=0; show_msg_lcd("direccion",1,1); show_value_lcd(dir_disp,3,' ',15,1); show_char_lcd(0xDF,16,1); // Transformación para sacar el índice del vector: i=dir_disp+22; // Hacer que 0 grados sea donde empieza el viento norte i=i/45; // Clasificar en uno de los 8 vientos i=i%8; // Para conseguir rango [0-7] show_msg_lcd(direccions[i],1,2); }
; ; This is an example STARTUP file. ; @(#)cstart.a96 1.5 ; STARTUP MODULE CMAIN RSEG SP EQU 018H:WORD cseg at 0C000H ; ------------------------ ; Interrupt service routine addresses to be used in RISM EPROM. ; Note: ; Of all these interrupt vectors, only the SERIAL, NMI and TRAP vectors are ; required for operation of the RISM. The other vectors are provided as ; fixed entry points for routines which may be loaded into RAM in the ; diagnostic mode. ; In the diagnostic mode memory at the interrupt vectors is mapped to EPROM ; so it is not possible to write into the vector table. EXTRN main:NULL EXTRN RSIExtInt:NULL EXTRN RSITimer2:NULL EXTRN RSI_Hsi:NULL cstart: PUBLIC cstart DI LD SP,#100H ; pila al final variables EI ; Some applications might want to do a CALL to main, ; and also have exit() calls. ; In that case enable the following: LCALL main _exit: PUBLIC _exit exit: PUBLIC exit _cstop: PUBLIC _cstop ; public for debuggers to set a breakpoint BR _cstop ; keep on looping EXTINT: PUSHA LCALL RSIExtInt POPA RET HSIINT: PUSHA LCALL RSI_Hsi POPA RET TIMER2_OVF: PUSHA LCALL RSITimer2 POPA RET ;; Button (INT7) cseg at 0d0e0h br EXTINT ;; Interrupcio de "Data available" del HSI (INT2) cseg at 0d040h br HSIINT ;; Interrupció de "Timer 2 Overflow" (INT12) cseg at 0d180h br TIMER2_OVF END
#include <kd_sfrs.h> #include "lcd.h" /****************************************************************************** ; Declaració de constants del display ;****************************************************************************** ;conexionat: ; p1.4..p1.7 = D4..D7 ; p1.0 = RS ; p1.2 = E ; 0 = R/W siempre escribimos */ #define LCD_INS 0xfe #define LCD_DAT 0x01 #define LCD_ENA 0x04 #define LCD_LINEA2 0xC0 #define LCD_LINEA1 0x80 #define LCD_CLRSCR 0x01 /* RETARD DE 1.64 ms */ void WRITE_LCD_4BITS(); void INICIALITZA_LCD() { /* ;0 X000 0000B = 00H -> IOPORT1 ;1 X000 0001B = 01H ;15 X000 1111B = 0FH */ DELAY_10mS(); DELAY_10mS(); wsr=0; ioport1=0x30 & LCD_INS; WRITE_LCD_4BITS(); DELAY_10mS(); wsr=0; ioport1=0x30 & LCD_INS; WRITE_LCD_4BITS(); DELAY_10mS(); wsr=0; ioport1=0x30 & LCD_INS; WRITE_LCD_4BITS(); DELAY_10mS(); wsr=0; ioport1=0x20 & LCD_INS; /* interface de 4 bits */ WRITE_LCD_4BITS(); DELAY_10mS(); /*AHORA YA PODEMOS USAR LA FUNCION DE INSTRUCION LCD*/ /*FUNCTION SET- 4 bits dades, 2 lines, caracter font*/ INSTRUCCIO_LCD(0x28); /*DISPLAY ON/OFF-DISPLAY ON, CURSOR OFF, CURSOR NO BLINK*/ INSTRUCCIO_LCD(0x0C); INSTRUCCIO_LCD(0x06); /*ENTRY MODE SET-INCREMENT CURSOR, CURSOR MOVEMENT*/ INSTRUCCIO_LCD(0x01); /*CLEAR DISPLAY*/ DELAY_10mS(); /*ESTE RETARDO DEBERIA DE SER DE 1.64mS*/ } void WRITE_LCD_4BITS() { /*AQUESTA FUNCIO ES NOMES PER US INTERN DE LA LLIBRERIA DEL DISPLAY*/ asm{NOP;NOP;} wsr=0; ioport1 = ioport1 ^ LCD_ENA; /*XORB IOPORT1,#LCD_ENA*/ asm{ NOP;NOP;NOP;NOP;NOP; NOP;NOP;NOP;NOP;NOP; } wsr=0; ioport1 = ioport1 ^ LCD_ENA; /*XORB IOPORT1,#LCD_ENA*/ asm{NOP;} } void INSTRUCCIO_LCD(unsigned char inst) { wsr=0; ioport1=0xF0 & inst & LCD_INS ; /* RS=0 */ WRITE_LCD_4BITS(); inst<<=4; asm{ NOP;NOP; NOP;NOP;NOP; } wsr=0; ioport1 =0xF0 & inst & LCD_INS; WRITE_LCD_4BITS(); DELAY_40uS(); } void DADA_LCD(unsigned char dato) { wsr=0; ioport1=0xF0 & dato; /*parte alta primero*/ ioport1=ioport1|LCD_DAT; /*RS=1*/ WRITE_LCD_4BITS(); dato<<=4; asm{ NOP;NOP; NOP;NOP;NOP; } wsr=0; ioport1=0x0F0 & dato; ioport1=ioport1|LCD_DAT; WRITE_LCD_4BITS(); DELAY_40uS(); } void CLRSCR_LCD() { INSTRUCCIO_LCD(LCD_CLRSCR); DELAY_10mS(); } void DELAY_10mS() { unsigned int TIMER_LCD; TIMER_LCD=2000; while(TIMER_LCD!=0) { TIMER_LCD--; } } void DELAY_40uS() { unsigned int TIMER_LCD; TIMER_LCD=40; while(TIMER_LCD!=0) { TIMER_LCD--; } } void GOTOXY(unsigned char x, unsigned char y) /* 1<=x<=16 , 1<=Y<=2 */ { unsigned char gotopos; gotopos=0x80 | ((y-1)*0x40) | (x-1); INSTRUCCIO_LCD(gotopos); }
;****************************************************************************** ; Declaració de constants del display ;****************************************************************************** ;conexionat: ; p1.4..p1.7 = D4..D7 ; p1.0 = RS ; p1.2 = E ; 0 = R/W LCD_INS SET 11111110B LCD_DAT SET 00000001B LCD_ENA SET 00000100B LCD_LINEA2 SET 0C0H LCD_LINEA1 SET 080H LCD_CLRSCR SET 001H ;RETARDO DE 1.64 mS ;******************************************************************************************* INICIALITZA_LCD:;*************************************************************************** ;******************************************************************************************* ;0 X000 0000B = 00H -> IOPORT1 ;1 X000 0001B = 01H ;15 X000 1111B = 0FH PUSH WSR LDB WSR,#00h CALL DELAY_10mS CALL DELAY_10mS LDB IOPORT1,#30H AND LCD_INS CALL WRITE_LCD_4BITS CALL DELAY_10mS LDB IOPORT1,#30H AND LCD_INS CALL WRITE_LCD_4BITS CALL DELAY_10mS LDB IOPORT1,#30H AND LCD_INS CALL WRITE_LCD_4BITS CALL DELAY_10mS LDB IOPORT1,#20H AND LCD_INS CALL WRITE_LCD_4BITS CALL DELAY_10mS ;AHORA YA PODEMOS USAR LA FUNCION DE INSTRUCION LCD LDB bAux,#28H ;FUNCTION SET- 4 bits dades, 2 lines, caracter font CALL INSTRUCCIO_LCD LDB bAux,#0FH ;DISPLAY ON/OFF-DISPLAY ON, CURSON ON, CURSOR BLINK CALL INSTRUCCIO_LCD LDB bAux,#06H ;ENTRY MODE SET-INCREMENT CURSOR, CURSOR MOVEMENT CALL INSTRUCCIO_LCD LDB bAux,#01H ;CLEAR DISPLAY- CALL INSTRUCCIO_LCD CALL DELAY_10mS ;ESTE RETARDO DEBERIA DE SER DE 1.64mS POP WSR RET ;******************************************************************************************* WRITE_LCD_4BITS:;*************************************************************************** ;******************************************************************************************* ;AQUESTA FUNCIO ES NOMES PER US INTERN DE LA LLIBRERIA DEL DISPLAY NOP NOP XORB IOPORT1,#LCD_ENA NOP NOP NOP NOP NOP NOP NOP NOP XORB IOPORT1,#LCD_ENA NOP RET ;******************************************************************************************* INSTRUCCIO_LCD:;**************************************************************************** ;******************************************************************************************* PUSH WSR LDB WSR,#00H LDB IOPORT1,#0F0H ANDB IOPORT1,bAux ANDB IOPORT1,#LCD_INS ;RS=0 CALL WRITE_LCD_4BITS SHL bAux,#4H NOP NOP NOP NOP NOP LDB IOPORT1,#0F0H ANDB IOPORT1,bAux ANDB IOPORT1,#LCD_INS CALL WRITE_LCD_4BITS CALL DELAY_40uS POP WSR RET ;******************************************************************************************* DADA_LCD:;********************************************************************************** ;******************************************************************************************* PUSH WSR LDB WSR,#00H LDB IOPORT1,#0F0H ANDB IOPORT1,bAux ;parte alta primero ORB IOPORT1,#LCD_DAT ;RS=1 CALL WRITE_LCD_4BITS SHL bAux,#4H NOP NOP NOP NOP NOP LDB IOPORT1,#0F0H ANDB IOPORT1,bAux ORB IOPORT1,#LCD_DAT CALL WRITE_LCD_4BITS CALL DELAY_40uS POP WSR RET ;******************************************************************************************* CLRSCR_LCD:;******************************************************************************** ;******************************************************************************************* LDB bAux,#LCD_CLRSCR CALL INSTRUCCIO_LCD CALL DELAY_10mS RET ;******************************************************************************************* DELAY_10mS:;******************************************************************************** ;******************************************************************************************* LD TIMER_LCD,#2000D DELAY_10mS_NEXT: DEC TIMER_LCD CMP TIMER_LCD,0 JNE DELAY_10mS_NEXT RET ;******************************************************************************************* DELAY_40uS:;******************************************************************************** ;******************************************************************************************* LD TIMER_LCD,#40D DELAY_40uS_NEXT: DEC TIMER_LCD CMP TIMER_LCD,0 JNE DELAY_10mS_NEXT RET ;******************************************************************************************* _GOTOXY MACRO X,Y;************************************************************************** ;******************************************************************************************* ; 1<=x<=16 , 1<=Y<=2 LDB bAux,#080H OR ((Y-1)*040H) OR (X-1) CALL INSTRUCCIO_LCD endm
Para coordinar y versionar el código de la práctica hemos usado un repositorio de software llamado SubVersion (http://subversion.tigris.org). Esta herramienta nos ha facilitado mucho el mantenimiento del código entre las tres personas del grupo de trabajo.
Un sistema de control de versions (Version Control System) és una aplicació que s'utilitza per a mantenir un conjunt d'arxius en un servidor central (anomenat repositori) i permetre l'accés a ells de forma concurrent des de diversos clients.
SVN (SubVersioN), com ja us podeu imaginar, és un sistema de control de versions.
Les funcions principals (més usades) d'aquest tipus de programes són:
En altres paraules, a nosaltres ens pot facilitar la feina perquè no haurem de preocupar-nos de quí te els arxius més recents, de comprovar quins canvis hi ha entre els fitxers de cadascú, etc. És a dir, tots els arxius finals de la pràctica estaran guardats en una única màquina, a la qual podrem accedir des de qualsevol punt per a baixar-nos el nostre codi o per a fer modificacions.
This work is licensed under the Creative Commons Attribution-NonCommercial-ShareAlike License. To view a copy of this license, visit http://creativecommons.org/licenses/by-nc-sa/2.1/es/ or send a letter to Creative Commons, 559 Nathan Abbott Way, Stanford, California 94305, USA.