Reading the [DS18B20](https://www.ampheo.com/product/ds18b20-26808200) [temperature sensor](https://www.onzuu.com/category/temperature-sensors) with a 51 [microcontroller](https://www.ampheo.com/c/microcontrollers) (like the classic [AT89C51](https://www.ampheo.com/search/AT89C51) or STC89C52) is a classic project. It involves precise timing because the sensor uses the 1-Wire communication protocol.

Here is a detailed breakdown of the process and code.
**1. The 1-Wire Protocol (Simplified Overview)**
The DS18B20 uses a single data line for communication, which makes it pin-efficient but timing-critical. The key operations are:
1. Initialization: The master (MCU) sends a reset pulse, and the slave (DS18B20) responds with a presence pulse.
2. ROM Commands: Like searching for devices or, more commonly, skipping them if only one is on the bus.
3. Function Commands: Telling the sensor to perform a temperature conversion and to read the scratchpad memory.
**2. Hardware Connection**
The connection is very simple:
* The DQ (Data) pin of the DS18B20 is connected to a GPIO pin on the 51 MCU (e.g., P3.7).
* A 4.7kΩ pull-up resistor is required between the DQ pin and VCC.
* VDD of the DS18B20 can be connected to an external power supply (5V) or in "parasite power" mode (connected to GND). This example uses external power.
```
text
+5V (VCC)
|
|
4.7kΩ
|
|
+--- DQ (P3.7 of MCU)
|
+--- DQ (Pin 2 of DS18B20)
|
GND --- VSS (Pin 1 of DS18B20)
+5V --- VDD (Pin 3 of DS18B20)
```
**3. Code Implementation (for AT89C51/52)**
Here is the code, heavily commented to explain each step. The code is written for the Keil C51 compiler.
```
c
#include <reg52.h> // Include the header file for your specific 51 MCU
#include <intrins.h> // For _nop_() (no-operation) for precise delays
sbit DQ = P3^7; // Define the data pin for DS18B20 on Port 3, pin 7
// Function Declarations
void DelayUs(unsigned int us);
void DS18B20_Init();
void DS18B20_WriteByte(unsigned char dat);
unsigned char DS18B20_ReadByte();
float DS18B20_ReadTemp();
/**
* @brief Rough microsecond delay function.
* @param us: approximate number of microseconds to delay.
* @note This is highly dependent on the MCU's clock frequency.
* You must calibrate this for your specific crystal (e.g., 11.0592 MHz or 12 MHz).
*/
void DelayUs(unsigned int us) {
while (us--) {
_nop_(); _nop_(); _nop_(); _nop_(); // Adjust these NOPs for your clock speed
}
}
/**
* @brief Initializes the DS18B20 and checks for its presence.
* @retval 1 if sensor is present, 0 if error.
*/
unsigned char DS18B20_Init() {
unsigned char presence = 0;
DQ = 1; // Pull the bus high
DelayUs(8);
DQ = 0; // MCU pulls the bus low (reset pulse)
DelayUs(500); // Wait ~500us (min. 480us)
DQ = 1; // MCU releases the bus (pull-up resistor pulls it high)
DelayUs(60); // Wait 60us for the sensor to respond
presence = DQ; // Read the bus. 0 = sensor present, 1 = no sensor
DelayUs(240); // Wait for the end of the presence pulse (min. 240us)
return !presence; // Return 1 if sensor is present
}
/**
* @brief Writes one byte to the DS18B20.
* @param dat: The byte to be written.
*/
void DS18B20_WriteByte(unsigned char dat) {
unsigned char i;
for (i = 0; i < 8; i++) {
DQ = 0; // Start by pulling the bus low
_nop_(); _nop_(); // A brief delay (~2us)
DQ = dat & 0x01; // Set the bus to the value of the LSB
DelayUs(60); // Hold the value for ~60us (min. 60us)
DQ = 1; // Release the bus
dat >>= 1; // Shift data to get the next bit
// A short recovery time between writes is needed
DelayUs(10);
}
}
/**
* @brief Reads one byte from the DS18B20.
* @retval The byte read from the sensor.
*/
unsigned char DS18B20_ReadByte() {
unsigned char i, value = 0;
for (i = 0; i < 8; i++) {
value >>= 1; // Shift the received value right
DQ = 0; // MCU pulls the bus low to initiate a read timeslot
_nop_(); _nop_(); // Very short pulse (~2us)
DQ = 1; // MCU releases the bus
_nop_(); _nop_(); // Wait a moment for the sensor to put data on the bus
if (DQ) { // Read the bus state
value |= 0x80; // If the bus is high, set the MSB of the value
}
DelayUs(60); // Wait for the end of the timeslot
}
return value;
}
/**
* @brief Main function to read the temperature as a float.
* @retval Temperature in degrees Celsius.
*/
float DS18B20_ReadTemp() {
unsigned char tempL, tempH;
int temp;
float value;
DS18B20_Init(); // 1. Initialize the bus
DS18B20_WriteByte(0xCC); // 2. Skip ROM command (use if only one sensor)
DS18B20_WriteByte(0x44); // 3. Issue Convert T command (start conversion)
// You can now wait ~750ms for 12-bit conversion, or poll the bus.
// Simple delay waiting for conversion (blocking).
// For a 12-bit conversion, this can take up to 750ms.
DelayUs(100000); // This is a rough delay. A real project would use a timer or poll.
DS18B20_Init(); // 4. Re-initialize the bus
DS18B20_WriteByte(0xCC); // 5. Skip ROM command again
DS18B20_WriteByte(0xBE); // 6. Issue Read Scratchpad command
tempL = DS18B20_ReadByte(); // 7. Read low byte of temperature (LSB)
tempH = DS18B20_ReadByte(); // 8. Read high byte of temperature (MSB)
// Combine the two bytes into a 16-bit integer
temp = (tempH << 8) | tempL;
// Process the reading to get a float value in Celsius
// The value is in 0.0625°C increments (1/16)
value = temp * 0.0625; // Simply multiply by the resolution
// Alternatively, you can do integer math for fixed-point:
// value = temp >> 4; // Integer part (°C)
// value += (temp & 0x0F) * 0.0625; // Add fractional part
return value;
}
/**
* @brief Main function
*/
void main() {
float temperature;
// Initialize your UART for printing here (not shown in this example)
// UART_Init();
while(1) {
temperature = DS18B20_ReadTemp(); // Read the temperature
// Print the temperature via UART to a serial monitor
// printf("Temp: %.3f C\r\n", temperature);
// Alternatively, display it on an LCD...
// Wait before reading again
DelayUs(1000000); // Wait ~1 second
}
}
```
**4. Key Steps Explained:**
1. Initialization (DS18B20_Init): This is the reset and presence pulse sequence. It's the first step of any 1-Wire communication.
2. Skipping ROM (0xCC): This command is used if you have only one DS18B20 on the bus. It tells the sensor to ignore its unique address and listen to the subsequent command.
3. Start Conversion (0x44): This tells the DS18B20 to perform a temperature measurement. For the default 12-bit resolution, this takes a maximum of 750 milliseconds. The code uses a blocking delay DelayUs(100000), but a better practice is to use a timer interrupt to avoid halting the MCU.
4. Read Scratchpad (0xBE): This command prepares the sensor to output its 9 bytes of data, including the two temperature bytes.
5. Reading Data: The MCU reads two bytes (LSB and MSB) which form a 16-bit temperature value.
6. Data Conversion: The 16-bit value is in a fixed-point format where the lower 4 bits are the fractional part (in increments of 0.0625°C). Multiplying the integer by 0.0625 converts it to a floating-point Celsius value.
**5. Critical Considerations for the 51 MCU**
* Timing is Everything: The 1-Wire protocol is extremely timing-sensitive. The DelayUs() function must be calibrated for your MCU's clock frequency (e.g., 11.0592 MHz or 12 MHz). Use an oscilloscope if possible to verify the timing.
* Disable Interrupts: During the critical read/write functions (DS18B20_ReadByte, DS18B20_WriteByte), you should disable interrupts (EA = 0;) to prevent other code (like timer ISRs) from disrupting the precise timing. Re-enable them (EA = 1;) afterwards.
* Blocking Delays: The DelayUs(100000) to wait for conversion is a poor practice in a real application. It locks up the MCU. A much better method is to:
1. Start the conversion.
2. Let the MCU do other tasks.
3. After at least 750ms, come back to read the result. This is easily done with a timer flag.
* Printing the Result: The main function shows a comment about UART. You need to implement a UART library to send the data to a PC serial monitor (e.g., PuTTY) or display it on an LCD screen.
By following this structure and paying close attention to the timing, you can successfully read temperature data from a [DS18B20](https://www.ampheoelec.de/search/DS18B20) using any 51-family microcontroller.