;********************************************************************** ; Este programa se distribuye según esta licencia: * ; * ; http://creativecommons.org/licenses/by-nc-sa/2.5/es/ * ; * ; léala antes de seguir adelante o de hacer uso de él * ; * ;********************************************************************** ; * ; Filename: AireAcondicionado.asm * ; Date: 17-06-07 * ; * ; Author: Emilio Florido * ; * ;********************************************************************** ; This file is a basic code template for assembly code generation * ; on the PIC12F675. This file contains the basic code * ; building blocks to build upon. * ; * ; If interrupts are not used all code presented between the ORG * ; 0x004 directive and the label main can be removed. In addition * ; the variable assignments for 'w_temp' and 'status_temp' can * ; be removed. If the internal RC oscillator is not implemented * ; then the first four instructions following the label 'main' can * ; be removed. * ; * ; Refer to the MPASM User's Guide for additional information on * ; features of the assembler (Document DS33014). * ; * ; Refer to the respective PIC data sheet for additional * ; information on the instruction set. * ; * ;********************************************************************** ; * ; Filename: AireAcondicionado.asm * ; Date: * ; File Version: * ; * ; Author: * ; Company: * ; * ; * ;********************************************************************** ; * ; Files required: * ; 12F629.lkr * ; * ;********************************************************************** ; Notas: este programa comprueba la temperatura ambiente, y, en * ; función de su valor envía comandos infrarrojos al aparato de * ; aire acondicionado para que arranque o pare. * ; * ; * ;********************************************************************** list p=12f675 ; list directive to define processor #include ; processor specific variable definitions errorlevel -302 ; suppress message 302 from list file __CONFIG _CP_OFF & _CPD_OFF & _BODEN_OFF & _MCLRE_OFF & _WDT_OFF & _PWRTE_ON & _INTRC_OSC_NOCLKOUT ; Con _MCLRE_OFF además que se puede usar GP3 como entrada y no como Reset, hace que al dejar ; "al aire" la patilla 5 del uC arranque sin problemas (si se pone _MCLRE_ON y la patilla ; 5 se deja suelta da problemas al arrancar) ; así es más fácil leer el programa MuySensible equ d'20' ; a más valor, más vueltas da antes de enviar un comando PocoSensible equ d'250' ; se ajusta externamente con GP3 (0->poco, 1->muy sensible) ; prácticamente es lineal, 250 son 25 segundos, 100 son 10 seg ; variables del programa Temporal1 equ 0x20 ; variables usadas para las pausas, retardos y varios Temporal2 equ 0x21 Temporal3 equ 0x22 Sensibilidad equ 0x23 ; como de rápido envia los comandos al detectar cambios de temperatura LimiteEncender equ 0x24 ; cuando estas variables se igualan al valor de la sensibilidad LimiteApagar equ 0x25 ; es cuando se envía el comando que corresponda al aire acondicionado ;-------------------------------------------------- Encender udata_shr 0x30 DatosEncender res 9 ; db 4,3,3,3,7,4,2,1,6,0xff (22º) ; db 4,3,3,3,7,4,3,6,0xff (20º) ; db 4,3,3,3,7,4,2,7,0xff (18º) ; db 4,3,3,3,7,4,9,0xff (16º) ;-------------------------------------------------- Apagar udata_shr 0x40 DatosApagar res 9 ; db 4,3,3,3,5,3,5,1,6,0xff (22º) ; db 4,3,3,3,5,3,6,6,0xff (20º) ; db 4,3,3,3,5,3,5,7,0xff (18º) ; db 4,3,3,3,5,3,12,0xff (16º) ;-------------------------------------------------- ;********************************************************************** RESET_VECTOR CODE 0x000 ; vector de reset call 0x3FF ; así tomaría el valor "grabado" en fábrica ; movlw 0x20 ; y así uno leído previamente :-) bsf STATUS,RP0 ; selecciona el banco 1 de registros movwf OSCCAL ; ajuste fino del oscilador interno ; configuración bcf STATUS,RP0 ; selecciona el banco 0 de registros movlw b'00000111' ; desactiva el comparador y lo pone además en bajo consumo movwf CMCON movlw b'0000001' ; ADFM a la izquierda -0-, Vdd como referencia -0- , no usados -00- movwf ADCON0 ; GP0 es la entrada -00- conversión terminada -0- convertidor activo -1- bsf STATUS,RP0 ; selecciona el banco 1 de registros movlw b'00000000' ; (1) por ahora no uso el voltaje de referencia interno ; movlw b'10000110' ; (2) Vref, high, 0110b= 6d => 2.03 voltios si Vdd=4.6 ; movlw b'10100111' ; (2) Vref, low , 0111b= 7d => 1.34 voltios si Vdd=4.6 movwf VRCON movlw b'01010001' ; (1) Fosc/16 y entrada en GP0 movwf ANSEL movlw b'00001001' ; las entradas para el ADC obligatoriamente deben ser entradas :-) movwf TRISIO ; GP1 y GP2 salidas para indicar la temperatura con los LEDs ; GP4 y GP5 salidas (emision infrarrojo, salida LED infrarrojo) ; GP3 siempre es _entrada_ (0 -> salida, 1 -> entrada) bcf STATUS,RP0 ; por defecto, selecciona el banco 0 de registros movlw 0xff ; se asegura que todo quede "en alto" movwf GPIO ; por defecto ; ajusta los valores de la sensibilidad movlw MuySensible ; supone que va a estar muy sensible btfss GPIO,3 ; aunque al leer GP3 puede que cambie de opinión ;-) movlw PocoSensible movwf Sensibilidad ; almacena el valor de la sensibilidad que corresponda clrf LimiteEncender ; se asegura de comenzar por cero clrf LimiteApagar ; antes de comenzar, no está de más esperar un poco :-) movlw d'250' call pausa_en_milisegundos movlw d'250' call pausa_en_milisegundos ; carga los datos de enceder (a 20º) movlw 4 movwf 0x30 movlw 3 movwf 0x31 movlw 3 movwf 0x32 movlw 3 movwf 0x33 movlw 7 movwf 0x34 movlw 4 movwf 0x35 movlw 3 movwf 0x36 movlw 6 movwf 0x37 movlw 0xff ; fin de comando movwf 0x38 ; carga los datos de apagar (a 20º) movlw 4 movwf 0x40 movlw 3 movwf 0x41 movlw 3 movwf 0x42 movlw 3 movwf 0x43 movlw 5 movwf 0x44 movlw 3 movwf 0x45 movlw 6 movwf 0x46 movlw 6 movwf 0x47 movlw 0xff ; fin de comando movwf 0x48 ; aqui comienza realmente el programa vueltas movlw d'100' call pausa_en_milisegundos movlw b'00110110' ; apaga _todos_ los LEDs, por si acaso :-) iorwf GPIO,1 ; el resultado queda en GPIO bsf ADCON0,1 ; comienza la conversión espera_conversion nop ; espera un poco... no tiene sentido "agobiar" al comparador para nada :-) nop btfsc ADCON0,1 ; y en tanto no termine, espera goto espera_conversion ; para 1.80 voltios: 1.80/4.5e-3 = 400, en binario ADRESH=01100100 : ADRESL=00xxxxxx ; para 1.75 voltios: 1.75/4.5e-3 = 388, en binario ADRESH=01100001 : ADRESL=00xxxxxx ; para 1.70 voltios: 1.70/4.5e-3 = 377, en binario ADRESH=01011110 : ADRESL=01xxxxxx ; para 1.65 voltios: 1.65/4.5e-3 = 366, en binario ADRESH=01011011 : ADRESL=10xxxxxx ; algo más estrecho el intervalo ; para 1.76 voltios: 1.76/4.5e-3 = 391, en binario ADRESH=01100001 : ADRESL=11xxxxxx ; para 1.74 voltios: 1.74/4.5e-3 = 387, en binario ADRESH=01100000 : ADRESL=11xxxxxx ; para 1.72 voltios: 1.72/4.5e-3 = 383, en binario ADRESH=01011111 : ADRESL=11xxxxxx ; para 1.70 voltios: 1.70/4.5e-3 = 379, en binario ADRESH=01011110 : ADRESL=11xxxxxx ; XXXXXXXXXXXXXXXXXXXXXX PARA SIMULAR TEMPERATURAS XXXXXXXXXXXXXXXXXXXXXXXX ;movlw b'00000001' ; valor que se quiere probar ;movwf ADRESH ; XXXXXXXXXXXXXXXXXXXXXX PARA SIMULAR TEMPERATURAS XXXXXXXXXXXXXXXXXXXXXXXX ; primero se gestionan los dos casos de "más temperatura" movlw b'01100001' subwf ADRESH,0 ; resta y deja el resultado en W btfss STATUS,C ; si hay acarreo es que es mayor o igual goto mas_pequeno1 bcf GPIO, 2 ; enciende los dos LEDs fijos bcf GPIO, 1 incf LimiteEncender ; se acerca más al límite para enviar el comando de encendido clrf LimiteApagar ; y se aleja completamente del comando de apagar goto comprobaciones mas_pequeno1 clrf LimiteEncender ; se aleja del envío del comando de encender movlw b'01100000' subwf ADRESH,0 ; resta y deja el resultado en W btfss STATUS,C ; si hay acarreo es que es mayor o igual goto mas_pequeno2 bcf GPIO, 2 ; enciende los dos LEDs fijos bcf GPIO, 1 movlw d'200' ; y los mantiene así un poco call pausa_en_milisegundos bsf GPIO, 2 ; pero el de más peso lo apaga y deja fijo solo el de menos movlw d'200' ; y se mantiene así un poco call pausa_en_milisegundos bcf GPIO, 2 ; enciende de nuevo el de más peso goto comprobaciones ; y ahora se gestionan los dos casos de "menos temperatura" ; primero se comprueba "el más bajo" y luego el otro mas_pequeno2 movlw b'01011110' subwf ADRESH,0 ; resta y deja el resultado en W btfsc STATUS,C ; si no hay acarreo es que es menor goto mas_grande1 incf LimiteApagar ; se acerca más al límite para enviar el comando de apagar goto comprobaciones ; los LEDs no se tocan pues deben estar ambos apagados mas_grande1 clrf LimiteApagar ; se aleja del comando de apagar movlw b'01011111' subwf ADRESH,0 ; resta y deja el resultado en W btfsc STATUS,C ; si no hay acarreo es que es menor goto mas_grande2 bcf GPIO, 1 ; enciende el LED de menos peso -el de más está ya apagado- movlw d'200' ; se mantiene así un poco call pausa_en_milisegundos bsf GPIO, 1 ; apaga también el de menos peso goto comprobaciones mas_grande2 bcf GPIO, 1 ; enciende el LED de menos peso comprobaciones ; ahora hay que comprobar si es el momento de enviar el comando de encender o de apagar ; teniendo en cuenta el valor que se haya fijado de sensibilidad comprobar_encendido movfw LimiteEncender subwf Sensibilidad,0 ; el resultado a W btfsc STATUS,C ; si hay acarreo, es que es mayor o igual goto comprobar_apagado bcf GPIO,4 ; enciende el LED de aviso de envio de comando infrarrojo movlw DatosEncender call enviar_comando clrf LimiteEncender ; prepara una nueva "vuelta" goto fin_sensibilidad comprobar_apagado movfw LimiteApagar subwf Sensibilidad,0 ; el resultado a W btfsc STATUS,C ; si hay acarreo, es que es mayor o igual goto fin_sensibilidad bcf GPIO,4 ; enciende el LED de aviso de envio de comando infrarrojo movlw DatosApagar call enviar_comando clrf LimiteApagar ; prepara una nueva "vuelta" fin_sensibilidad goto vueltas enviar_comando ; en W debe venir donde comienza el comando movwf FSR ; FSR hace de puntero call trama_guia call sincronismo bcf GPIO,5 ; enciende el LED infrarrojo justo después del sincronismo mas_tramas movfw INDF ; toma el número de veces que se repite la trama de 27 useg movwf Temporal1 ; y la almacena xorlw 0xff ; si es 0xff termina el comando btfss STATUS,Z goto pulsos bsf GPIO,5 ; apaga el LED infrarrojo justo antes de volver return pulsos call pulso600useg call pausa727useg ; durante esta pausa el LED permanece encendido decfsz Temporal1 goto pulsos call pausa1m273useg ; y durante esta también incf FSR ; avanza el puntero goto mas_tramas pulso27useg bsf GPIO,5 ; apaga el LED infrarrojo movlw d'3' ; para que tarde 11 useg movwf Temporal2 pulsoespera1 decfsz Temporal2 goto pulsoespera1 nop ; afina un poco hasta los 12 ó 13 useg nop bcf GPIO,5 ; enciende el LED infrarrojo movlw d'2' ; para que de tiempo a los calculos que se realizan a la vuelta movwf Temporal2 pulsoespera2 decfsz Temporal2 goto pulsoespera2 return trama_guia ; como es muy larga, hay que hacer un par de llamadas a la rutina de los pulsos de 27 useg ; en total deben ser 340 vueltas, así que debería dar 84 primero y 256 después... pero para ; compensar los retrados, en lugar de 84 da unas cuantas menos movlw d'63' movwf Temporal3 trama_guiaespera1 call pulso27useg decfsz Temporal3 goto trama_guiaespera1 ; no es necesario recargar los registros pues "dan la vuelta" :-) trama_guiaespera2 call pulso27useg decfsz Temporal3 goto trama_guiaespera2 return sincronismo movlw d'248' ; lo he calculado con el reloj del similador :-), además, le movwf Temporal3 ; he dejado unos microsegundos de menos -8- para compensar los sincronismoespera ; cálculos que hay que hacer a la vuelta goto $+1 ; cada uno "pierde" 2 ciclos goto $+1 goto $+1 goto $+1 goto $+1 goto $+1 goto $+1 goto $+1 decfsz Temporal3 goto sincronismoespera return pulso600useg ; 22 veces 27 useg son aproximadamente 600 useg, pero es algo menos para compensar los cálculos movlw d'21' movwf Temporal3 pulso600espera call pulso27useg decfsz Temporal3 goto pulso600espera return pausa727useg movlw d'179' ; lo he calculado con el reloj del similador :-), además, le movwf Temporal3 ; he dejado unos microsegundos de menos -10- para compensar los pausa727espera ; cálculos que hay que hacer a la vuelta nop decfsz Temporal3 goto pausa727espera return pausa1m273useg movlw d'180' ; lo he calculado con el reloj del similador :-), además, le movwf Temporal3 ; he dejado unos microsegundos de menos -8- para compensar los pausa1m273espera ; cálculos que hay que hacer a la vuelta nop nop nop nop decfsz Temporal3 goto pausa1m273espera return pausa_en_milisegundos movwf Temporal2 pausa_2 movlw d'110' ; vueltas para un milisegundo movwf Temporal1 pausa_1 nop nop nop nop nop nop decfsz Temporal1,1 ; ¿ha pasado ya un milisegundo? goto pausa_1 decfsz Temporal2,1 ; ¿han pasado ya los milisegundos fijados? goto pausa_2 return ; un poco de propaganda para la posteridad :-) data '*','E','m','i','l','i','o','*' data '(','1','7','-','0','6','-','0','7',')' end ; fin del programa