all
Chapter 13 of 20

Wire Master Module (1-Wire) — Tiva C

Eslam El Hefny Apr 13, 2025 7 min read
65% done

Wire Master Module (1-Wire) — Tiva C

Overview

The TM4C123GH6PM does not include a native 1-Wire hardware peripheral. However, 1-Wire is a simple single-wire protocol that is straightforward to implement in software using a GPIO pin and precise microsecond delays. This chapter covers the complete 1-Wire protocol implementation as a bit-bang GPIO driver and demonstrates reading temperature from a DS18B20 sensor.


Beginner Level — What & Why

What is 1-Wire?

1-Wire (developed by Dallas/Maxim) is a serial protocol that uses a single data line for both power and bidirectional communication. Devices are identified by a unique 64-bit ROM code. The bus allows multiple devices on one wire — hence the name OneWire or 1-Wire.

Real-World Analogy

1-Wire is like a party telephone line where everyone shares one phone line. The master (TM4C123) speaks first with a loud reset “shout”, then listens for everyone to respond (presence pulse). It then calls a specific person by their unique name (ROM code) and asks a question (function command like “read temperature”).

What Problem Does It Solve?

  • Reduces wiring to a single pin per sensor (plus ground)
  • Enables multiple temperature sensors on one GPIO pin
  • Low-cost sensor ICs (DS18B20) with no need for I2C or SPI

Key Terms

Term Meaning
Reset pulse Master pulls line LOW for >480 µs, then releases
Presence pulse Slave pulls LOW for 60-240 µs after reset to say “I’m here”
Write 1 bit Master pulls LOW for 1-15 µs, releases, total 60-120 µs slot
Write 0 bit Master pulls LOW for 60-120 µs
Read bit Master pulls LOW for 1-15 µs, releases, then samples at 15 µs
ROM code 64-bit unique ID: family code (8b) + serial (48b) + CRC (8b)

Intermediate Level — How It Works

1-Wire Timing Summary

All timings are in microseconds (µs):

Operation Duration
Reset LOW 480–640 µs
Recovery after reset 15–60 µs
Presence pulse (from slave) 60–240 µs
Write 1 slot 60–120 µs (LOW for 6 µs, then HIGH)
Write 0 slot 60–120 µs (LOW entire slot)
Read slot 60–120 µs (LOW 6 µs, sample at ~15 µs)
Recovery between slots 1 µs minimum

DS18B20 Command Sequence for Temperature

  1. Reset + presence check
  2. Send 0xCC (Skip ROM — talk to all devices) or 0x55 + 64-bit ROM code
  3. Send 0x44 (Convert T — start temperature conversion)
  4. Wait 750 ms (12-bit conversion time)
  5. Reset + presence check
  6. Send 0xCC (Skip ROM)
  7. Send 0xBE (Read Scratchpad)
  8. Read 9 bytes of scratchpad data
  9. Bytes 0 and 1 = temperature LSB and MSB

GPIO Open-Drain Configuration

1-Wire requires the GPIO pin to act as open-drain: actively driven LOW, but released (set as input) for HIGH. On TM4C123, you achieve this by switching between output (LOW) and input (high-impedance) modes with an external pull-up resistor (4.7 kΩ to 3.3 V).

/* Set 1-Wire pin LOW */
#define OW_DRIVE_LOW()  do { \
    GPIOPinTypeGPIOOutput(OW_PORT, OW_PIN); \
    GPIOPinWrite(OW_PORT, OW_PIN, 0); \
} while(0)

/* Release 1-Wire pin (float high via pull-up) */
#define OW_RELEASE()    GPIOPinTypeGPIOInput(OW_PORT, OW_PIN)

/* Read 1-Wire pin state */
#define OW_READ()       (GPIOPinRead(OW_PORT, OW_PIN) ? 1 : 0)

Advanced Level — Deep Dive

Microsecond Delay Implementation

SysCtlDelay burns 3 CPU cycles per iteration. At 80 MHz:

/* Delay n microseconds at 80 MHz
 * 80 MHz / 3 cycles = 26,666,666 iterations/second
 * Per microsecond: 26.667 iterations → use 27 */
static void delay_us(uint32_t us)
{
    SysCtlDelay(us * 27);  // 27 * 3 cycles = 81 cycles ≈ 1.0125 µs at 80 MHz
}

Complete 1-Wire Driver

#include <stdint.h>
#include <stdbool.h>
#include "inc/hw_memmap.h"
#include "driverlib/sysctl.h"
#include "driverlib/gpio.h"

/* Configure for PE0 — choose any available GPIO */
#define OW_PORT     GPIO_PORTE_BASE
#define OW_PIN      GPIO_PIN_0

static void delay_us(uint32_t us)
{
    SysCtlDelay(us * 27);
}

static void OW_DriveLow(void)
{
    GPIOPinTypeGPIOOutput(OW_PORT, OW_PIN);
    GPIOPinWrite(OW_PORT, OW_PIN, 0);
}

static void OW_Release(void)
{
    GPIOPinTypeGPIOInput(OW_PORT, OW_PIN);
    GPIOPadConfigSet(OW_PORT, OW_PIN,
                     GPIO_STRENGTH_2MA, GPIO_PIN_TYPE_STD_WPU);
}

static int OW_ReadBit_raw(void)
{
    return (GPIOPinRead(OW_PORT, OW_PIN) != 0) ? 1 : 0;
}

/* Reset: returns 1 if a device is present, 0 if no device */
int OneWire_Reset(void)
{
    OW_DriveLow();
    delay_us(480);    // hold LOW >= 480 µs
    OW_Release();
    delay_us(70);     // wait 70 µs then sample for presence pulse
    int presence = !OW_ReadBit_raw();  // LOW = device present
    delay_us(410);    // complete 480 µs recovery
    return presence;
}

/* Write one bit */
void OneWire_WriteBit(int bit)
{
    OW_DriveLow();
    if (bit)
    {
        delay_us(6);      // drive LOW for 6 µs
        OW_Release();
        delay_us(64);     // remainder of 70 µs slot
    }
    else
    {
        delay_us(60);     // drive LOW for 60 µs (write 0)
        OW_Release();
        delay_us(10);     // recovery
    }
}

/* Read one bit */
int OneWire_ReadBit(void)
{
    OW_DriveLow();
    delay_us(6);          // initiate read slot: drive LOW 6 µs
    OW_Release();
    delay_us(9);          // wait 9 µs (sample at ~15 µs from start)
    int bit = OW_ReadBit_raw();
    delay_us(55);         // complete the 70 µs slot
    return bit;
}

/* Write one byte (LSB first) */
void OneWire_WriteByte(uint8_t byte)
{
    int i;
    for (i = 0; i < 8; i++)
    {
        OneWire_WriteBit(byte & 0x01);
        byte >>= 1;
    }
}

/* Read one byte (LSB first) */
uint8_t OneWire_ReadByte(void)
{
    uint8_t byte = 0;
    int i;
    for (i = 0; i < 8; i++)
    {
        if (OneWire_ReadBit())
            byte |= (1 << i);
    }
    return byte;
}

DS18B20 Temperature Read

#define DS18B20_SKIP_ROM    0xCC
#define DS18B20_CONVERT_T   0x44
#define DS18B20_READ_SCRPAD 0xBE

/* Returns temperature in 1/16 degree units (raw 16-bit signed value) */
int16_t DS18B20_ReadRaw(void)
{
    if (!OneWire_Reset()) return 0x7FFF;  // no device

    OneWire_WriteByte(DS18B20_SKIP_ROM);
    OneWire_WriteByte(DS18B20_CONVERT_T);

    /* Wait for 12-bit conversion: 750 ms */
    SysCtlDelay(SysCtlClockGet() / 3 * 750 / 1000);

    if (!OneWire_Reset()) return 0x7FFF;

    OneWire_WriteByte(DS18B20_SKIP_ROM);
    OneWire_WriteByte(DS18B20_READ_SCRPAD);

    uint8_t lsb = OneWire_ReadByte();
    uint8_t msb = OneWire_ReadByte();
    /* Bytes 2–8 discarded for simplicity */

    return (int16_t)((msb << 8) | lsb);
}

/* Convert raw to Celsius (multiply raw by 0.0625) */
float DS18B20_ToC(int16_t raw)
{
    return (float)raw / 16.0f;
}

Gotchas

  • Timing is critical — at 80 MHz, each delay_us(1) call must be accurate to ±2 µs. Interrupts during timing-critical sections will corrupt the bit stream. Disable interrupts during each bit operation.
  • Pull-up resistor — 4.7 kΩ is recommended. Without it, the line never goes HIGH.
  • Parasitic power mode — DS18B20 can be powered by the data line (no VDD needed), but this requires a strong pull-up transistor during conversion and is more complex to implement.
  • CRC verification — the 9th scratchpad byte is a CRC8 of the first 8. Always verify it in production code.
  • Multiple devices — require the 64-bit ROM search algorithm to enumerate all devices on the bus.

Step-by-Step Example

/*
 * ds18b20_temp.c
 * Read DS18B20 temperature and print via UART0
 * Board  : TM4C123GXL EK LaunchPad
 * SDK    : TivaWare_C_Series-2.2.x
 * Wiring : PE0 → DS18B20 DQ, 4.7kΩ pull-up to 3.3V
 */

#include <stdint.h>
#include <stdbool.h>
#include "inc/hw_memmap.h"
#include "driverlib/sysctl.h"
#include "driverlib/gpio.h"
#include "driverlib/uart.h"
#include "driverlib/pin_map.h"
#include "utils/uartstdio.h"

/* Include the 1-Wire and DS18B20 functions defined above */

void UART0_Init(void)
{
    SysCtlPeripheralEnable(SYSCTL_PERIPH_UART0);
    SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOA);
    while (!SysCtlPeripheralReady(SYSCTL_PERIPH_UART0));
    while (!SysCtlPeripheralReady(SYSCTL_PERIPH_GPIOA));
    GPIOPinConfigure(GPIO_PA0_U0RX);
    GPIOPinConfigure(GPIO_PA1_U0TX);
    GPIOPinTypeUART(GPIO_PORTA_BASE, GPIO_PIN_0 | GPIO_PIN_1);
    UARTStdioConfig(0, 115200, SysCtlClockGet());
}

int main(void)
{
    SysCtlClockSet(SYSCTL_SYSDIV_2_5 | SYSCTL_USE_PLL |
                   SYSCTL_OSC_MAIN   | SYSCTL_XTAL_16MHZ);

    UART0_Init();

    /* Enable Port E for 1-Wire on PE0 */
    SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOE);
    while (!SysCtlPeripheralReady(SYSCTL_PERIPH_GPIOE));

    OW_Release();  // start as input with pull-up

    UARTprintf("DS18B20 1-Wire Demo\r\n");

    while (1)
    {
        int16_t raw = DS18B20_ReadRaw();
        if (raw == 0x7FFF)
        {
            UARTprintf("No sensor found!\r\n");
        }
        else
        {
            /* Integer and fractional parts */
            int intPart  = raw >> 4;
            int fracPart = (raw & 0x0F) * 625 / 100;  // in 0.01 deg steps
            UARTprintf("Temperature: %d.%02d C\r\n", intPart, fracPart);
        }
        /* Read every 2 seconds */
        SysCtlDelay(SysCtlClockGet() / 3 * 2);
    }
}

Summary

Key Point Details
Hardware Bit-bang GPIO — no dedicated peripheral
Wire count 1 data + GND (parasitic power adds no extra wire)
Pull-up 4.7 kΩ to 3.3 V on data line
Reset pulse LOW for 480–640 µs
Presence pulse Slave replies LOW for 60–240 µs
Bit timing 60–120 µs per bit slot
DS18B20 conversion 750 ms for 12-bit, 375 ms for 11-bit
Temperature formula raw / 16.0 = Celsius
Interrupts Disable during bit operations to preserve timing

Next Chapter

PWM Module

Share: