How to interface DS18B20 with STM32

I have covered few temperature sensors in the past for eg- DHT11, DHT22 and also the internal temperature sensor of the STM32 itself. Today in this tutorial we will see how to interface DS18B20 temperature sensor with STM32.

The DS18B20 digital thermometer provides 9-bit to 12-bit Celsius temperature measurements and has an alarm function with non-volatile user-programmable upper and lower trigger points. Like DHT11 and DHT22, DS18B20 also communicates over a 1-Wire bus that by definition requires only one data line for communication with the micro controller.

If you want to interface multiple DS18B20 sensors using only 1 wire, check out this tutorial.

If you want to understand how to use the UART half duplex mode to generate one-wire protocol, check out this tutorial.

NOTE:- This code works with STM32CUBEIDE and it uses TIMER to create delay in microsecond. If you don’t know how to do that, check out https://controllerstech.com/create-1-microsecond-delay-stm32/ .

I am going to skip the Cube mx setup process as it’s usual one only. I am using pin PA1 as the data pin for the sensor and controller is running at it’s maximum frequency.

void delay (uint32_t us)
{
    __HAL_TIM_SET_COUNTER(&htim6,0);
    while ((__HAL_TIM_GET_COUNTER(&htim6))<us);
}

INITIALISATION

Below is the image showing the initialisation timing of the DS18B20.

As show above, we need to pull the line LOW for around 480us and then release it. Then the sensor waits for around 60us and pull the line LOW for 60 – 240us to show its presence on the line.

NOTE:- You might need to connect pull-up resistor to the data line or else DS18B20 will not be able to pull the line HIGH.


Write Timing

Below is the timing required to write a single bit to the sensor.

write bit 0
write bit 1
  • To generate a Write 0 time slot, after pulling the line low, the master must continue to hold the line low for the duration of the time slot (at least 60µs).
  • To generate a Write 1, after pulling the line low, the master must release the line within 15µs.
  • When the bus is released, the 5kΩ pullup resistor will pull the bus high.

Read Timing

Below is the Timing to read a single bit from the sensor.

  • A read time slot is initiated by the master device pulling the 1-Wire bus low for a minimum of 1µs and then releasing the bus.
  • After the master initiates the read time slot, the DS18B20 will begin transmitting a 1 or 0 on bus.
  • It transmits a 1 by leaving the bus high and transmits a 0 by pulling the bus low.
  • When transmitting a 0, the sensor will release the bus by the end of the time slot, and the bus will be pulled back to its high idle state by the pull-up resister.


Connection & Configuration

Below is the image showing the connection between the sensor and the nucleo F446.

Since the DS18B20 only uses 1 wire to communicate to the MCU, I have connected it to the pin PA1. The sensor is powered with 3.3V from the nucleo board itself.

NOTE:- You might need to connect pull-up resistor to the data line or else the sensor will not be able to pull the line HIGH.


clock configuration

Below is the image showing the clock configuration in the cubeMX.

The system is clocked from the external 8MHz crystal and the HCLK is set to 50MHz. Note that the APB1 Timer clock is also at 50MHz. This is important because we will use the TIM6 to generate the delays in microseconds and the TIM6 is connected to the APB1 bus.

Timer Configuration

Below is the image showing the configuration of the TIM6.

Since the APB1 Timer clock is at 50MHz, we will use the prescaler of 50 to bring the TIM6 clock to 1 MHz. This is already explained in the tutorial which explains how to generate the delays in micro/nanoseconds.

The pin PA1 is set as output, this is where the DS18B20 data pin is connected to.

I2C Configuration

We are using the LCD1602 to display the Temperature and Humidity data. The LCD is connected using the PCF8574 I2C extender. Below is the image showing the I2C configuration.

I am using the I2C1 to connect the LCD. The I2C is configured in the standard mode with the clock speed set to 100KHz. The pins PB8 and PB9 are configured as the SCL and SDA pins.

We have already covered how to interface the LCD via I2C with STM32. You can check out the tutorial for more details.



Some Insight into the CODE

Initialisation

  1. Set the pin (data) as output.
  2. Pull the pin low and wait for 480us.
  3. set the pin as input for receiving the data.
uint8_t DS18B20_Start (void)
{
	uint8_t Response = 0;
	Set_Pin_Output(DS18B20_PORT, DS18B20_PIN);   // set the pin as output
	HAL_GPIO_WritePin (DS18B20_PORT, DS18B20_PIN, 0);  // pull the pin low
	delay (480);   // delay according to datasheet

	Set_Pin_Input(DS18B20_PORT, DS18B20_PIN);    // set the pin as input
  1. wait for 80us.
  2. Check if the pin is pulled LOW by the sensor. If it is, then the sensor is responding fine with the presence pulse.
  3. Wait for another 400us, so to complete the entire timing of 480us.
        delay (80);    // delay according to datasheet
	if (!(HAL_GPIO_ReadPin (DS18B20_PORT, DS18B20_PIN))) Response = 1;    // if the pin is low i.e the presence pulse is detected
	else Response = -1;

	delay (400); // 480 us delay totally.

	return Response;
}

Write Data

Below is the function to write a single byte to the sensor.

void DS18B20_Write (uint8_t data)
{
	Set_Pin_Output(DS18B20_PORT, DS18B20_PIN);  // set as output

	for (int i=0; i<8; i++)
	{
		if ((data & (1<<i))!=0)  // if the bit is high
		{
			// write 1
			Set_Pin_Output(DS18B20_PORT, DS18B20_PIN);  // set as output
			HAL_GPIO_WritePin (DS18B20_PORT, DS18B20_PIN, 0);  // pull the pin LOW
			delay (1);  // wait for 1 us

			Set_Pin_Input(DS18B20_PORT, DS18B20_PIN);  // set as input
			delay (50);  // wait for 60 us
		}

		else  // if the bit is low
		{
			// write 0
			Set_Pin_Output(DS18B20_PORT, DS18B20_PIN);
			HAL_GPIO_WritePin (DS18B20_PORT, DS18B20_PIN, 0);  // pull the pin LOW
			delay (50);  // wait for 60 us

			Set_Pin_Input(DS18B20_PORT, DS18B20_PIN);
		}
	}
}

Here we will extract each bit from the data byte and check if it is a 1 or a 0. Then write the bit to the sensor.

The write timings for 1 and 0 are already explained above.

To write a 1, we will

  • Set the pin as output, pull it LOW and wait for 1us.
  • Then release the pin immediately by setting it as input.

To write a 0, we will

  • Set the pin as output, pull it LOW and wait for 50us.
  • Then release the pin by setting it as input.

Read Data

Below is the function to Read a data byte from the sensor.

uint8_t read (void)
{
	uint8_t value=0;
	gpio_set_input ();

	for (int i=0;i<8;i++)
	{
		gpio_set_output ();   // set as output
		HAL_GPIO_WritePin (GPIOA, GPIO_PIN_1, 0);  // pull the data pin LOW
		delay (2);  // wait for 2 us
		gpio_set_input ();  // set as input
		if (HAL_GPIO_ReadPin (GPIOA, GPIO_PIN_1))  // if the pin is HIGH
		{
			value |= 1<<i;  // read = 1
		}
		delay (60);  // wait for 60 us
	}
	return value;
}

I have already explained the Read timings in the beginning of this tutorial. Basically we need to pull the pin low for >1us and then release it. Now we will check the state of the pin.

  • If the pin is HIGH, that means the sensor did not pulled it low, so the bit is a 0.
  • If the pin is LOW, the sensor must have pulled it LOW, so the bit is a 1.

Then combine all the bits received to make a single byte.


The Main Function

Inside the main function, we will continuously read the temperature after a particular interval. This is why the code is inside the while loop.

int main ()
{
  ...
  ...
  while (1)
  {
     Presence = DS18B20_Start ();
     DS18B20_Write (0xCC);  // skip ROM
     DS18B20_Write (0x44);  // convert t

     Presence = DS18B20_Start ();
     DS18B20_Write (0xCC);  // skip ROM
     DS18B20_Write (0xBE);  // Read Scratch-pad

     Temp_byte1 = DS18B20_Read();
     Temp_byte2 = DS18B20_Read();
     TEMP = ((Temp_byte2<<8))|Temp_byte1;
     Temperature = (float)TEMP/16.0;  // resolution is 0.0625

     HAL_Delay(3000);
     Display_Temp(Temperature);
  }
}

We will read the data as according to the information given in the datasheet.

Basically the master will send the reset sequence and detect the presence pulse. Then it will issue the skip rom command followed by the convert t command.
It will again send the reset condition and read the presence pulse. It will then send the skip rom command followed by the read scratchpad command.

Then the master will request 2 bytes data from the sensor. It will first read the Least Significant Byte followed by the Most Significant Byte of the temperature data.

The sensor is configured with 12 bit resolution by default. Hence we will have a total of 12 bits of the data. The 2 bytes of the temperature data will be combined to form a single 12 bit data.
With the resolution of 12 bit, the temperature is set to 0.0625 °C per bit value. Hence to convert the 12 bit temperature data to actual temperature, we will multiply the 12 bit value with 0.0625 or divide by 16.

Since we are also displaying the data on the LCD, the LCD is initialised in the main function itself. We have already covered how to use the LCD1602 via I2C to display strings, numbers, floats etc. Below are the functions for displaying the data on the LCD.

void Display_Temp (float Temp)
{
	char str[20] = {0};
	lcd_put_cur(0, 0);
	sprintf (str, "TEMP:- %.2f ", Temp);
	lcd_send_string(str);
	lcd_send_data('C');
}


Result

Below is the image showing the output temperature data on the LCD.

Check out the Video Below




Info

You can help with the development by DONATING Below.
To download the project, click the DOWNLOAD button.

Subscribe
Notify of

34 Comments
Newest
Oldest Most Voted
Inline Feedbacks
View all comments
keyboard_arrow_up