4/4
;SUBRUTINAS PARA MANEJO DEL BUS I2C
; Subrutina "SDA_Bajo" -------------------------------------------------------------------------------
;
SDA_Bajo
bsf STATUS,RP0 ; Configura la línea SDA como salida.
bcf SDA
bcf STATUS,RP0
bcf SDA ; SDA en bajo.
return
;
; Subrutina "SDA_AltaImpedancia" --------------------------------------------------------
;
SDA_AltaImpedancia
bsf STATUS,RP0 ; Configura la línea SDA entrada.
bsf SDA ; Lo pone en alta impedancia y, gracias a la
bcf STATUS,RP0 ; Rp de esta línea, se mantiene a nivel alto.
return
;
; Subrutina "SCL_Bajo" ------------------------------------------------------------------
;
SCL_Bajo
bsf STATUS,RP0
bcf SCL ; Configura la línea SCL como salida.
bcf STATUS,RP0
bcf SCL ; La línea de reloj SCL en bajo.
return
;
; Subrutina "SCL_AltaImpedancia" --------------------------------------------------------
;
SCL_AltaImpedancia
bsf STATUS,RP0 ; Configura la línea SCL entrada.
bsf SCL ; Lo pone en alta impedancia y, gracias a la Rp
bcf STATUS,RP0 ; de esta línea, se mantiene a nivel alto.
SCL_EsperaNivelAlto
btfss SCL ; Si algún esclavo mantiene esta línea en bajo
goto SCL_EsperaNivelAlto ; hay que esperar.
return
;
; Subrutina "I2C_EnviaStart" ------------------------------------------------------------
;
; Esta subrutina envía una condición de Start o inicio.
;
I2C_EnviaStart
call SDA_AltaImpedancia ; Línea SDA en alto.
call SCL_AltaImpedancia ; Línea SCL en alto.
call Retardo_5micros ; Tiempo tBUF del protocolo.
call SDA_Bajo ; Flanco de bajada de SDA mientras SCL está alto.
call Retardo_4micros ; Tiempo tHD;STA del protocolo.
call SCL_Bajo ; Flanco de bajada del reloj SCL.
call Retardo_5micros
return
;
; Subrutina "I2C_EnviaStop" -------------------------------------------------------------
;
; Esta subrutina envía un condición de Stop o parada.
;
I2C_EnviaStop
call SDA_Bajo
call SCL_AltaImpedancia ; Flanco de subida de SCL.
call Retardo_5micros ; Tiempo tSU;STO del protocolo.
call SDA_AltaImpedancia ; Flanco de subida de SDA.
call Retardo_5micros ; Tiempo tBUF del protocolo.
return
;
; Subrutina "I2C_EnviaByte" -------------------------------------------------------------
;
; El microcontrolador maestro transmite un byte por el bus I2C, comenzando por el bit
; MSB. El byte a transmitir debe estar cargado previamente en el registro de trabajo W.
; De la subrutina ejecutada anteriormente I2C_EnviaStart o esta misma I2C_EnviaByte,
; la línea SCL se debe encontrar a nivel bajo al menos durante 5 µs.
;
I2C_EnviaByte
movwf I2C_Dato ; Almacena el byte a transmitir.
movlw 0x08 ; A transmitir 8 bits.
movwf I2C_ContadorBits
I2C_EnviaBit
rlf I2C_Dato,F ; Chequea el bit, llevándolo previamente al Carry.
btfsc STATUS,C
goto I2C_EnviaUno
I2C_EnviaCero
call SDA_Bajo ; Si es "0" envía un nivel bajo.
goto I2C_FlancoSCL
I2C_EnviaUno
call SDA_AltaImpedancia ; Si es "1" lo activará a alto.
I2C_FlancoSCL
call SCL_AltaImpedancia ; Flanco de subida del SCL.
call Retardo_4micros ; Tiempo tHIGH del protocolo.
call SCL_Bajo ; Termina el semiperiodo positivo del reloj.
call Retardo_5micros ; Tiempo tHD;DAT del protocolo.
decfsz I2C_ContadorBits,F ; Lazo para los ocho bits.
goto I2C_EnviaBit
call SDA_AltaImpedancia ; Libera la línea de datos.
call SCL_AltaImpedancia ; Pulso en alto de reloj para que el esclavo
call Retardo_5micros ; pueda enviar el bit ACK.
call SCL_Bajo
call Retardo_5micros
return
;
; Subrutina "I2C_LeeByte" ---------------------------------------------------------------
;
; El microcontrolador maestro lee un byte desde el esclavo conectado al bus I2C. El dato
; recibido se carga en el registro I2C_Dato y lo envía a la subrutina superior a través
; del registro W. Se empieza a leer por el bit de mayor peso MSB.
; De alguna de las subrutinas ejecutadas anteriormente I2C_EnviaStart, I2C_EnviaByte
; o esta misma I2C_LeeByte, la línea SCL lleva en bajo al menos 5 µs.
I2C_LeeByte
movlw 0x08 ; A recibir 8 bits.
movwf I2C_ContadorBits
call SDA_AltaImpedancia ; Deja libre la línea de datos.
I2C_LeeBit
call SCL_AltaImpedancia ; Flanco de subida del reloj.
bcf STATUS,C ; En principio supone que es "0".
btfsc SDA ; Lee el bit
bsf STATUS,C ; Si es "1" carga 1 en el Carry.
rlf I2C_Dato,F ; Lo introduce en el registro.
call SCL_Bajo ; Termina el semiperiodo positivo del reloj.
call Retardo_5micros ; Tiempo tHD;DAT del protocolo.
decfsz I2C_ContadorBits,F ; Lazo para los 8 bits.
goto I2C_LeeBit; Chequea si este es el último byte a leer para enviar o no el bit de reconocimiento ACK en consecuencia.
btfss I2C_UltimoByteLeer ; Si es el último, no debe enviar el bit de reconocimiento ACK.
call SDA_Bajo ; Envía el bit de reconocimiento ACK porque todavía no es el último byte a leer.
call SCL_AltaImpedancia ; Pulso en alto del SCL para transmitir el
call Retardo_4micros ; bit ACK de reconocimiento. Este es tHIGH.
call SCL_Bajo ; Pulso de bajada del SCL.
call Retardo_5micros
movf I2C_Dato,W ; El resultado se manda en el registro de
return ; de trabajo W.
;SUBRUTINAS DE RETARDOS PARA CUARZO DE 20 MHZ QUE SON 5 MHZ/S. 0,2us POR CICLO MAQUINA
;RETARDOS DE 1,4,5,10,20,50 Y 100 us
Retardo_100micros;RETARDO DE 100 MICROS
nop
movlw 0XA4;164 en decimal
goto RetardoMicros
Retardo_50micros;RETARDO DE 50 MICROS
movlw 0X51;81 en decimal
goto RetardoMicros
Retardo_20micros;RETARDO DE 20 MICROS
movlw 0X1F;31en decimal
goto RetardoMicros
Retardo_10micros;RETARDO DE 10 MICROS
nop
movlw 0X0E
goto RetardoMicros
Retardo_5micros;RETARDO DE 5 MICROS
nop
nop
nop
movlw 0X05
goto RetardoMicros
Retardo_4micros;RETARDO DE 4 MICROS
movlw 0X05
RetardoMicros
movwf R_ContA
Rmicros_Bucle
decfsz R_ContA,F
goto Rmicros_Bucle
return
Retardo_1micro;RETARDO DE 1 MICRO
nop
return
;RETARDOS DE 500 micros y 1,2,5 y 10 milisegundos
Retardo_500micros
movlw 0X05
goto Retardos_ms
Retardo_1ms
movlw 0X0B
goto Retardos_ms
Retardo_2ms
movlw 0X17
goto Retardos_ms
Retardo_5ms
movlw 0X3B
goto Retardos_ms
Retardo_10ms
movlw 0X77
goto Retardos_ms
Retardos_ms
movwf R_ContB
Bucle_ext
movlw 0X68
movwf R_ContA
Bucle_int
decfsz R_ContA,F
goto Bucle_int
decfsz R_ContB,F
goto Bucle_ext
return
; Estas subrutinas permiten realizar las tareas de manejo del reloj-calendario DS1307
; Este dispositivo transmite la información vía serie a través de un bus I2C.
; Subrutina "DS1307_Inicializa" ---------------------------------------------------------
; Configura la señal cuadrada que genera el DS1307 en su pin SQW/OUT a 1 Hz.
DS1307_Inicializa
call I2C_EnviaStart ; Envía condición de Start.
movlw DS1307_DireccionEscritura ; Indica al DS1307 que el byte a escribir,
call I2C_EnviaByte ; está en la posición 07h, que corresponde
movlw 0x07 ; al control de la señal cuadrada.
call I2C_EnviaByte
movlw b'00010000' ; Escribe en el registro de control para
call I2C_EnviaByte ; configurar onda cuadrada del DS1307 a 1 Hz.
call I2C_EnviaStop ; Termina de enviar datos.
return
; Subrutina "DS1307_Lee" ----------------------------------------------------------------
;
; Se leen las variables de tiempo del DS1307 y se guardan en los registros correspondientes.
DS1307_Lee
bcf I2C_UltimoByteLeer
call I2C_EnviaStart ; Envía condición de Start.
movlw DS1307_DireccionEscritura ; Indica al DS1307 que el primer byte
call I2C_EnviaByte ; a leer está en la posición 00H, que corresponde
movlw 0x00 ; a los segundos.
call I2C_EnviaByte
call I2C_EnviaStop ; Envía condición de Stop. Ahora va a leer el DS1307.
call I2C_EnviaStart ; Envía condición de Start.
movlw DS1307_DireccionLectura
call I2C_EnviaByte
call I2C_LeeByte ; Lee los segundos.
movwf Segundo ; Lo carga en el registro correspondiente.
call I2C_LeeByte ; Lee el resto de los registros utilizando
movwf Minuto ; el mismo procedimiento.
call I2C_LeeByte
movwf Hora
call I2C_LeeByte
movwf DiaSemana
call I2C_LeeByte
movwf Dia
call I2C_LeeByte
movwf Mes
bsf I2C_UltimoByteLeer ; Para terminar.
call I2C_LeeByte
movwf Anho
call I2C_EnviaStop ; Acaba de leer.
return
; Subrutina "DS1307_CargaInicial" -------------------------------------------------------
;
; Realiza una carga inicial en los registros internos del reloj-calendario DS1307 a fecha
; Lunes, 1 de Enero de 2004 a las 0:00:00.
;
DS1307_CargaInicial
movlw 0X0F; Inicializa todos los datos del reloj: Año, mes,
movwf Dia
movlw 0X0A ; día, día de la semana, hora, minuto y segundo.
movwf Mes
movlw 0X07
movwf DiaSemana
movlw 0X17
movwf Anho ; Inicializa en el año 2004.
clrf Hora
clrf Minuto
clrf Segundo ; Después lo graba en el DS1307 para
call DS1307_Escribe ; ponerlo en marcha.
return
;
; Subrutina "DS1307_Escribe" ----------------------------------------------------------------
;
; Carga los datos de los registros Anho, Mes, etc., dentro del DS1307.
DS1307_Escribe
call I2C_EnviaStart ; Envía condición de Start.
movlw DS1307_DireccionEscritura ; Indica al DS1307 que el primer byte a escribir
call I2C_EnviaByte ; está en la posición 00h que corresponde
movlw 0x00 ; a los segundos.
call I2C_EnviaByte
movf Segundo,W ; Pasa los segundos de la memoria del PIC16F84A al
call I2C_EnviaByte ; DS1307.
movf Minuto,W ; Y se repite el proceso para el resto.
call I2C_EnviaByte
movf Hora,W
call I2C_EnviaByte
movf DiaSemana,W
call I2C_EnviaByte
movf Dia,W
call I2C_EnviaByte
movf Mes,W
call I2C_EnviaByte
movf Anho,W
call I2C_EnviaByte
call I2C_EnviaStop ; Termina de enviar datos.
return
;**************************** Librería "BUS_1LIN.INC" *********************************
; Estas subrutinas permiten realizar las tareas básicas de control del bus de una sola línea
; según el protocolo del fabricante de semiconductores Dallas Semiconductors.
; Subrutina "Bus1Linea_AltaImpedancia" --------------------------------------------------
;
; Configura la línea única de 1-Wire Bus como entrada. Lo pone en alta impedancia.
Bus1Linea_AltaImpedancia
bsf STATUS,RP0
bsf Bus1Linea ; La línea como entrada, por tanto, en alta
bcf STATUS,RP0 ; impedancia.
return
; Subrutina "Bus1Linea_Bajo" ------------------------------------------------------------
;
; Mantiene el pin de datos del Bus de 1 línea en bajo.
Bus1Linea_Bajo
bsf STATUS,RP0
bcf Bus1Linea ; La línea como salida.
bcf STATUS,RP0
bcf Bus1Linea ; Pasa a bajo.
return
; Subrutina "Bus1Linea_Reset" -----------------------------------------------------------
;
; Envía un impulso de Reset al dispositivo conectado en el bus de 1 línea y después
; espera recibir un impulso de Presence". Esto consiste en:
; 1º. El PIC manda un impulso en bajo durante unos 500 µs. (El protocolo permite entre 480
; y 960 µs).
; 2º. El PIC pasa a modo entrada, espera 20 µs. (El protocolo fija entre 15 y 60 µs).
; 3º. El PIC lee la línea de entrada y si es bajo está correcto (pulso de "Presence").
; Se interpreta como que el procedimiento está correctamente inicializado.
Bus1Linea_Reset
call Bus1Linea_AltaImpedancia ; Empieza en alto para conseguir el flanco de
; bajada con la siguiente instrucción.
call Bus1Linea_Bajo ; El bus de 1 línea en bajo durante 500 µs.
call Retardo_500micros
call Bus1Linea_AltaImpedancia ; Permanece en alta impedancia (nivel alto por
call Retardo_20micros ; las resistencias de Pull-Up) durante unos µs.
Bus1Linea_EsperaLeerBajo
btfsc Bus1Linea ; Espera a que el dispositivo le envíe el pulso
goto Bus1Linea_EsperaLeerBajo ; en bajo de "Presence".
Bus1Linea_EsperaLeerAlto
btfss Bus1Linea ; Y ahora el dispositivo lo debe poner en alto.
goto Bus1Linea_EsperaLeerAlto
return
; Subrutina "Bus1Linea_EscribeByte" -----------------------------------------------------
;
; El maestro escribe un byte al esclavo conectado al bus de 1 línea comenzando por el bit LSB.
; Consiste en enviar 8 bits, repitiendo 8 veces la siguiente secuencia:
; 1º El PIC manda un impulso en bajo durante unos microsegundos. (El protocolo fija un
; mínimo de 1 µs y sin exceder con lo que viene a continuación de 60 µs) A continuación:
; - Si va a enviar un "0" permanece en bajo durante 50 µs.
; - Si va a escribir un "1" se pone en alta impedancia durante 50 µs. (El protocolo
; permite unos valores entre 15 y 60 µs para ambos casos).
; 2º Para escribir otro bit se pone en alta impedancia durante unos µs. (El protocolo
; establece un mínimo de 1 µs).
;
; Entrada: En (W) el dato a enviar.
Bus1Linea_EscribeByte
movwf Bus1Linea_Dato ; Guarda el byte a transmitir.
movlw 0x08 ; 8 bits a transmitir.
movwf Bus1Linea_ContadorBits
call Bus1Linea_AltaImpedancia ; Empieza en alta impedancia.
Bus1Linea_EnviaBit
call Bus1Linea_Bajo ; Flanco de bajada.
;
; Pregunta por el valor del bit a transmitir.
;
rrf Bus1Linea_Dato,F ; Lleva el bit de menor peso LSB al Carry.
btfss STATUS,C ; ¿Es un "1" el bit a transmitir?.
goto Bus1Linea_EnviaCero ; No, pues envía un "0".
Bus1Linea_EnviaUno
call Bus1Linea_AltaImpedancia ; Transmite un "1".
goto Bus1Linea_Espera50us
Bus1Linea_EnviaCero
call Bus1Linea_Bajo ; Transmite un "0".
Bus1Linea_Espera50us
call Retardo_50micros
call Bus1Linea_AltaImpedancia ; Flanco de subida.
decfsz Bus1Linea_ContadorBits,1 ; Comprueba que es el último bit.
goto Bus1Linea_EnviaBit ; Como no es el último bit repite la operación.
return
;
; Subrutina "Bus1Linea_LeeByte" ---------------------------------------------------------
;
; El microcontrolador maestro lee un byte del dispositivo esclavo conectado al bus de
; 1 línea. Consiste en leer 8 bits, repitiendo 8 veces la siguiente secuencia:
; 1º El PIC manda un pulso en bajo durante unos µs. (El protocolo fija un mínimo de 1 µs).
; 2º El PIC se pone en entrada (alta impedancia) durante otros 10 µs (con un total de 15 µs
; desde el flanco de bajada) para proceder a la lectura de la línea.
; 3º El PIC espera 50 µs para realizar la siguiente lectura (total unos 65 µs desde el
; flanco de bajada). El protocolo fija que las lecturas se harán cada 60 µs ó más.
;
; Salida: En (W) el dato leído.
Bus1Linea_LeeByte
movlw 0x08 ; 8 bits a recibir.
movwf Bus1Linea_ContadorBits
call Bus1Linea_AltaImpedancia ; Empieza en alta impedancia.
Bus1Linea_LeeBit
call Bus1Linea_Bajo ; Flanco de bajada.
call Bus1Linea_AltaImpedancia
call Retardo_10micros ; Añadiendo las instrucciones anteriores, supone
; un total de 15 µs desde el flanco de bajada.
bcf STATUS,C ; Ahora lee el pin. En principio supone que es 0.
btfsc Bus1Linea ; ¿Realmente es cero?.
bsf STATUS,C ; No, pues cambia a uno.
rrf Bus1Linea_Dato,1 ; Introduce el bit en el registro.
call Retardo_50micros ; Espera.
call Bus1Linea_AltaImpedancia ; Flanco de subida del pulso positivo.
decfsz Bus1Linea_ContadorBits,1 ; Comprueba que es el último bit.
goto Bus1Linea_LeeBit ; Si no es el último bit pasa a leer el siguiente.
movf Bus1Linea_Dato,W ; El resultado en el registro W.
return
;**************************** Librería "DS1820.INC" ***********************************
; Estas subrutinas permiten realizar las tareas de manejo del sensor de temperatura DS1820.
; Este sensor transmite la información vía serie a través de un 1-Wire Bus.
; Subrutina "DS1820_Inicializa" ---------------------------------------------------------
;
; Inicializa el DS1820 para que comience la conversión de temperatura.
;
DS1820_Inicializa
call Bus1Linea_Reset ; Primero los pulsos de Reset y Presence.
movlw DS1820_Skip_ROM_Command ; Salta los comandos de ROM ya que hay
call Bus1Linea_EscribeByte ; conectado un único dispositivo al bus.
movlw DS1820_Convert_T_Command ; Para que comience la conversión de temperatura.
call Bus1Linea_EscribeByte
return
; Subrutina "DS1820_LeeTemperatura" -----------------------------------------------------
;
; El DS1820 lee la temperatura en un formato ejemplarizado como sigue:
;
; +125,0 ºC 00000000 11111010
; + 25,5 ºC 00000000 00110011
; + 25,0 ºC 00000000 00110010
; + 0,5 ºC 00000000 00000001
; + 0,0 ºC 00000000 00000000
; - 0,5 ºC 11111111 11111111
; - 25,0 ºC 11111111 11001110
; - 55,0 ºC 11111111 10010010
;
; Se observa que:
; - El formato es de dos bytes. El primer byte es el de signo.
; - Las temperaturas positivas comienzan con 1 byte todo a cero: "00000000 xxxxxxxx".
; - Las temperaturas negativas comienzan con 1 byte todo a uno : "11111111 xxxxxxxx".
; - Las temperaturas "xx.0 ºC" terminan en cero: "xxxxxxx0".
; - Las temperaturas "xx.5 ºC" terminan en uno : "xxxxxxx1".
; - El valor de la temperatura está contenido en los 7 bits de mayor peso del byte bajo.
; Por ejemplo: +25ºC = xxxxxxxx 0011001x = 25 en decimal. Por tanto, para obtener el
; valor de la temperatura habrá que desplazar un lugar hacia la derecha los bits del
; byte bajo.
; - Las temperaturas negativas vienen dadas en complemento a 2. Por tanto para obtener
; su valor absoluto habrá que negarlas.
;
; Esta subrutina lee la temperatura proporcionando tres datos:
;
; Salida: - En el registro DS1820_Temperatura, el valor de la temperatura en valor absoluto.
; - En el registro DS1820_TemperaturaDecimal, la parte decimal del valor de la temperatura.
; - En (DS1820_TemperaturaSigno)=b'11111111' si la temperatura es negativa y
; (DS1820_TemperaturaSigno)=b'00000000' si es positiva.
;
DS1820_LeeTemperatura
call Bus1Linea_Reset ; Primero los pulsos de "Reset" y "Presence".
movlw DS1820_Skip_ROM_Command ; Salta los comandos de ROM ya que hay
call Bus1Linea_EscribeByte ; conectado un único dispositivo al bus.
movlw DS1820_Read_Scratchpad_Command ; Va a leer la primera posición de memoria,
call Bus1Linea_EscribeByte ; donde se localiza el valor de la temperatura.
call Bus1Linea_LeeByte ; Lee la temperatura en
movwf DS1820_Temperatura ; complemento a 2.
call Bus1Linea_LeeByte ; Ahora lee el segundo byte que indica el signo
movwf DS1820_TemperaturaSigno ; de la temperatura.
call Bus1Linea_Reset ; Y para terminar, resetea el dispositivo.
;
; Ahora deduce si la temperatura es positiva o negativa y halla su valor absoluto.
;
btfss DS1820_TemperaturaSigno,7 ; Si cualquier bit es "1": temperatura negativa.
goto DS1820_FinLeeTemperatura ; La temperatura es positiva y salta.
DS1820_TemperaturaNegativa
movf DS1820_Temperatura,W ; La niega para convertirla a su valor
sublw 0x00 ; absoluto. Para ello, simplemente le resta de
movwf DS1820_Temperatura ; cero.
DS1820_FinLeeTemperatura
bcf STATUS,C ; Para no corromper luego el MSB.
rrf DS1820_Temperatura,F ; Obtiene el valor absoluto de la temperatura.
movlw .5
btfss STATUS,C ; Si el bit LSB es "1", la temperatura
clrw ; medida es "xx.5ºC" sino "xx.0ºC".
movwf DS1820_TemperaturaDecimal
movf DS1820_Temperatura,W ; En (W) la parte entera del valor absoluto de la
return ; temperatura.
END