Bus One Wire - Sensor de temperatura DS18B20
1. Introducción:
Para desarrollar la comunicación, nos centraremos en la sonda de temperatura DS18B20 con un formato industrial:


Nota: Es una sonda económica y muy estable.
2. Características:
- 9 bits, 10 bits, 11 bits o 12 bits lectura temperatura en Celsius. Más precisión implica más tiempo de conversión.
- Tiene funciones de alarma programables.
- Puede funcionar con alimentación parasitaria.
- Puede medir desde -55 a +125 ºC con un 0.5% de error.
Alimentación parasitaria:

Alimentación directa:

Nota: Para que funcione bien en nuestra placa,
sacaremos el jumper del array de resistencias de los led si usamos un pin de esos. Me daba problemas al pasar de 26ºC.
3. Funciones:
Las funciones que aparecen en la tabla siguiente, son las necesarias para llevar a cabo la comunicación:




Nota: En verde se indica quién tiene el control del BUS.
4. Identificación de los esclavos:
Para identificar un Esclavo, se hace a través del Rom Code:

Si sólo tenemos un esclavo en el bus, podemos averiguar el Rom Code enviando el comando 0x33 (Read Rom Code).
Nuestra sonda nos devuelve la siguiente cadena: 0x61 / 0x01, 0x15, 0x52, 0x55, 0x56, 0xFF / 0x28
Si hay más de un esclavo, debemos descubrirlos a través de un proceso de eliminación usando el comando 0xF0 (Search Rom).
Nota: Para no complicarnos, los conectaremos de uno en uno para averiguar con el comando anterior (Read Rom Code)
el Rom Code y poder direccionar.
Una vez sepamos los Rom Code de cada elemento, direccionaremos usando el comando 0x55 (Match Rom).
Cuando tengamos varios elementos en el bus que necesiten un tiempo para preparar el dato, podemos indicarles que vayan realizando el trabajo
direccionando a "todos" con el comando 0xCC (Skip Rom) y luego direccionaremos uno a uno para recoger el valor.
5. Detección de errores:
Para asegurarnos que los datos recibidos están bien, se usa un sistema CRC. Si restamos al crc recibido, el crc calculado debería darnos 0.
El sistema se basa en polinomios de orden 8:
CRC = X8 + X5 + X4 + 1
- El primer paso es calcular el valor del polinomio:
- X8 + X5 + X4 + 1 = 28 + 25 + 24 + 1 = 305 = 0b 1 0011 0001.
- Eliminaos el 1 de mayor peso quedando 0b 0011 0001.
- Intercambiamos los bits de mayor y menor peso quedando 1000 1100 = 0x8C.
Inicialmente, empezamos con el valor 0, luego, cuando pasemos al siguiente byte, se inicia con el resultado de la operación anterior.
- El proceso consiste en desplazar un bit a la derecha tanto el valor inicial o de la operación anterior y el byte que toque comprobar.
Comparamos los bits de menor peso:
- Son diferentes: realizamos la operación XOR entre el valor 0x8C y el valor CRC.
- Son iguales: desplazamos una posición más los registros.

6. Leyendo temperatura:
La conversión se hace por defecto usando 12 bits de resolución; cuanta mayor precisión, más tiempo necesario en la conversión.
El siguiente registro, es el que nos permite seleccionar la resolución:


Nota: Una conversión de 12 bits, necesita cerca de un segundo para completarse.
- Número de bit y resolución:
- 9 bits resolución de 0.5ºC.
- 10 bits resolución de 0.25ºC.
- 11 bits resolución de 0.125ºC.
- 12 bits resolución de 0.0625ºC.
- Comandos destacables:
- 0x44: Indicamos que inicie la conversión.
- 0xB4: Pregunta cómo se alimenta el esclavo (Vcc directamente o tensión parasitaria).
- 0xBE: Vamos a leer los 9 registros. La temperatura se encuentra en el byte 0 y 1.
- 0x4E: Escribimos los bytes 2, 3 y 4.
6.1. Formato temperatura:
Los valores S, del bit 15 al 11 indican si la temperatura es positiva (0) o negativa (1).
Del bit 10 al 4 es el valor de la temperatura y del bit 3 al 0 los decimales.

He encontrada esta fórmula para realizar el cálculo de forma sencilla:
float value = ((MS Byte & 0x0F) * 256) + MS Byte
- Si el bit 0x08 es 1 -> temperatura negativa
float Temperatura = Value / 16 - 256
- Si el bit 0x08 es 0 -> temperatura positiva
float Temperatura = Value / 16
Para cambiar la resolución, escribiremos el valor 0x1F (para 9 bits) o 0x7F (para 12 bits) por ejemplo.
Nota: Una vez modificado el registro, debemos indicarle que guarde los cambios en la Eeprom
usando el siguiente comando: scratchpad command (48h)
6.2. Aplicación ejemplo y salida por terminal:
Hacemos uso de la librería "OneWire.h" creada anteriormente.
/* * File: main.c * Author: Eduardo * * Created on 5 de junio de 2017, 17:16 */ #include#include "config.h" #include "typedefs.h" #include "usart.h" #include "stdio.h" #include "OneWire.h" //PROTOTIPO DE LAS FUNCIONES USADAS void delay_ms(uint16_t); void imprimirValorInt (int valor); void imprimirValorFloat (float valor); uint8_t crc8 (uint8_t *valores, size_t len); unsigned char read_OW_code (); void read_OW_temp (); void write_OW_conf (uint8_t conf); void main(void) { USART_Initialize(); // Inicializamos el puerto Serie delay_ms(500); // Hacemos un reset y comprobamos si hay respuesta // Si nadie responde, esperamos 5 segundos y volvemos a preguntar while (!reset_OW ()) { printf ("No se detectan esclavos \n"); delay_ms(5000); } // Al salir del bucle anterior, preguntamos el Rom Code printf ("Hay un esclavo \n"); read_OW_code (); while (true) { ////////////////////////////////////////////////////////////////////////// // Cuando solo hay un esclavo en el bus, podemos omitir el Rom Code // El esclavo nos devuelve la temperatura /* if (reset_OW ()) { write_OW_byte (0xCC); // No enviamos id, solo hay un esclavo en el bus write_OW_byte (0x44); // Indicamos que inicie la conversion delay_ms(1000); // Esperamos hasta que la conversion se realice reset_OW (); //Provocamos otro reset write_OW_byte (0xCC); //No enviamos id, solo hay un esclavo en el bus write_OW_byte (0xBE); //Indicamos que queremos leer read_OW_temp (); //Leemos temperatura y calculamos CRC } else printf ("Error comunicacion \n"); */ ////////////////////////////////////////////////////////////////////////// // Cuando tenemos mas de un esclavo, debemos direccionar usando el Rom Code // Respondera el esclavo que corresponda // Como el tiempo de conversion usando 12 bits es de 750mS, podemos indicar // a todos los esclavos que realicen la conversion y luego vamos preguntando // uno a uno usando el direccionamiento if (reset_OW ()) { write_OW_byte (0xCC); // Nos dirigimos a todos los esclavos write_OW_byte (0x44); // Indicamos que inicie la conversion delay_ms(1000); reset_OW (); // Provocamos otro reset write_OW_byte (0x55); // Ahora preguntamos al esclavo que nos interese write_OW_byte (0x28); write_OW_byte (0xFF); write_OW_byte (0x56); write_OW_byte (0x55); write_OW_byte (0x52); write_OW_byte (0x15); write_OW_byte (0x01); write_OW_byte (0x61); write_OW_byte (0xBE); //Indicamos que queremos leer read_OW_temp (); //Leemos temperatura y calculamos CRC } else printf ("Error comunicacion \n"); ////////////////////////////////////////////////////////////////////////// // Esta funcion cambia la resolucion de la conversion. Puede ser de // 9 bits, 10 bits, 11 bits o 12 bits. // Mayor resolucion implica mayor tiempo en la conversion // - 12 bits = 0x7F // - 11 bits = 0x2F // - 10 bits = 0x1F // - 9 bits = 0x0F //write_OW_conf (0x7F); delay_ms(1000); // Retardo antes de volver a iniciar } } /****************************************************************************** * FUNCION IMPRIMIR INT * * - Funcion que permite imprimir un int por la uart *****************************************************************************/ void imprimirValorInt (int valor) { unsigned char intStringVal[4]; itoa( intStringVal, valor,10); printf(intStringVal); printf("\n"); } /****************************************************************************** * FUNCION IMPRIMIR HEX * * - Funcion que permite imprimir un hex por la uart *****************************************************************************/ void imprimirValorHex (int valor) { unsigned char intStringVal[4]; itoa( intStringVal, valor,16); printf(intStringVal); printf("\n"); } /****************************************************************************** * FUNCION IMPRIMIR FLOAT * * - Funcion que permite imprimir un float por la uart *****************************************************************************/ void imprimirValorFloat (float valor) { int bytesWritten; char myString[50]; bytesWritten = sprintf(myString, "%7.4f", valor); UART_Write_Text(myString); printf("\n"); } /****************************************************************************** * FUNCION RETARDO DE mS * * - Funcion que realiza un retardo en mS segun el valor que recibe *****************************************************************************/ void delay_ms(uint16_t i) { for ( uint16_t x=0; x 0; i--) //El proceso se aplica a todos los bytes del array { uint8_t byte = valores[i]; //Cargamos en byte, el valor del array correspondiente for (uint8_t j = 0; j < 8; j++) //Recorremos los 8 bits que componen el byte { uint8_t crc_carry = crc & 1; //Almacenamos el bit de menor peso en crc_carry para compararlo crc >>= 1; //Desplazamos un bit a la derecha uint8_t byte_carry = byte & 1; //Almacenamos el bit de menor peso en byte_carry para compararlo byte >>= 1; //Desplazamos un bit a la derecha if (crc_carry ^ byte_carry) //Si los bits de menor peso son diferentes, XOR = 1. crc ^= div; //Realizamos la operación entre los valores } } return crc; //Retorna el valor calculado crc que debemos restar del recibido. }

- Comunicación OneWire DS18B20: