Wire Master Module (1-Wire) — Tiva C
- Eslam El Hefny
- Tutorials, Tiva c
- April 13, 2025
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
- Reset + presence check
- Send
0xCC(Skip ROM — talk to all devices) or0x55+ 64-bit ROM code - Send
0x44(Convert T — start temperature conversion) - Wait 750 ms (12-bit conversion time)
- Reset + presence check
- Send
0xCC(Skip ROM) - Send
0xBE(Read Scratchpad) - Read 9 bytes of scratchpad data
- 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 |