Cover image for Linux One-Wire

Linux One-Wire

Words 5.4k
Views
Visitors

Timeline

Timeline

2025-12-23

init

This article introduces the One-Wire protocol from the Linux driver notes, explaining its feature of requiring only one signal line to simultaneously transmit clock and data, supporting bidirectional communication, as well as hardware components such as pull-up resistors and unique 64-bit address identification for multiple devices. The article details the communication steps of the One-Wire bus, including initialization, ROM operation commands, and function commands, and emphasizes that the host must strictly follow the timing. Additionally, using the temperature sensor DS18B20 as an example, it introduces its 9- to 12-bit adjustable resolution, -55°C to +125°C measurement range, ±0.4°C accuracy, globally unique serial number, and multi-point distributed temperature measurement capability, and explains its pin configuration, package types, and the flexible method of powering through the data line.

Linux Driver Notes

Table of ContentsLinks
1. Linux Driver Framework
2. Linux Driver Loading Logic
3. Character Device Basics
4. Concurrency and Race Conditions
5. Advanced Character Device Topics
6. Interrupts
7. Platform Bus
8. Device Tree
9. Device Model
10. Hotplug
11. pinctrl Subsystem
12. GPIO subsystem
13. Input subsystem
14. 1-Wire
15. I2C
16. SPI
17. UART
18. PWM
19. RTC
20. Watchdog
21. CAN
22. Network devices
23. ADC
24. IIO
25. USB
26. LCD

One-Wire Bus Introduction

One-Wire Bus Overview

One-Wire is a serial communication protocol and hardware bus, used to transmit data and control signals between electronic devices. It was developed by the independent chip manufacturer Dallas Semiconductor and has been widely used in a variety of applications.

Unlike serial data communication methods such as SPI and I2C, the One-Wire bus is characterized byrequiring only one signal line, which can transmit both clock and data, and the data is bidirectional.. Therefore, the One-Wire bus has the characteristics of saving I/O pins, simple structure, and easy expansion and maintenance.

The One-Wire bus is used in various applications, includingtemperature sensors, humidity sensors, EEPROM memory, clocksetc. It has been widely used in many fields, such as industrial automation, home automation, the Internet of Things, and electronic device monitoring. This article uses the temperature sensor DS18B20 as an example to learn about the One-Wire bus.

Hardware Structure of the One-Wire Bus
Hardware Structure of the One-Wire Bus

  1. Signal line: The One-Wire bus uses a single signal line for data transmission and communication. This line is called the data line and is also the line that provides power. All devices on the One-Wire bus are connected to this signal line.
  2. Pull-up resistor: The One-Wire bus requires a pull-up resistor connected between the signal line and the power supply to ensure that the level on the signal line remains high (logic 1) when no device is sending data. The value of the pull-up resistor is usually between 4.7 kΩ and 10 kΩ (GPIO set to pull-up).
  3. device: The One-Wire bus supports multiple devices connected to the signal line. Each device has a unique 64-bit address, through which the target device for communication is identified and selected. Devices can be various types of sensors, memories, clocks, etc.
  4. Processor: The processor is the controller on the One-Wire bus. The processor is responsible for sending commands, reading responses, and controlling the slave devices on the One-Wire bus.

Communication Steps of the One-Wire Bus

The One-Wire bus ismaster-slave structureThe slave device only responds when the host calls it., soThe host must strictly follow the command timing of the One-Wire bus.. If the command timing is incorrect, the device will not respond.

The communication steps of the 1-Wire bus typically include the following phases.

  1. InitializeBefore communication begins,**the master device sends an initialization signal to ensure that no other device is communicating on the 1-Wire bus.**The initialization signal is a specific sequence, typically pulling the data line low for a period and then releasing it.
  2. ROM operation commands
  3. Function commands

DS18B20 Introduction

Reference:

Chip Overview

DS18B20 is a digital temperature sensor chip,providing temperature measurement with 9 to 12-bit resolution,and canprogrammable non-volatile memory cellsto implement low and high temperature alarms.

It is a device based on the 1-Wire communication protocol, requiring only one signal wire and one ground wire.

DS18B20 can measure temperature with high accuracy, precise to 0.0625°C (at 12-bit resolution). It has a wide measurement range, typically between -55°C and +125°C.

The DS18B20 chip can obtain power from the master device via the 1-Wire bus, or it can be powered by an external power supply. This allows flexible power supply options in some low-power applications.

Each DS18B20 has a globally unique 64-bit serial number, allowing multiple DS18B20s to be connected in series on the same 1-Wire bus for networking. Only one processor is needed to control multiple DS18B20s distributed over a large area. This networking method is particularly suitable for application fields such as HVAC environmental control, buildings, equipment, grain condition temperature measurement, industrial temperature measurement, and process monitoring and control.

Basic Performance

DS18B20 is a digital temperature sensor chip with the following basic performance characteristics:

  • Uses a 1-Wire interface, requiring only one port pin for communication
  • Each chip has a globally unique 64-bit serial number
  • Has multi-point distributed temperature measurement function without requiring external components
  • Can be powered through the data line, with a supply voltage range of 2.5V ~ 5.5V
  • Temperature measurement range is -55°C to +125°C
  • Accuracy is ±0.4°C within the range of -10°C ~ +70°C
  • Temperature resolution selectable from 9 to 12 bits
  • At maximum 12-bit resolution, temperature conversion time is less than 750ms
  • Has user-defined non-volatile temperature alarm settings
  • The alarm search command identifies and flags devices that exceed the programmed temperature setpoint
  • Strong ESD protection capability: HBM 8000V, MM 800V
  • Available in surface-mount MSOP8, SOP8 packages and 3-pin TO-92, TO-92S packages.

Pin Configuration and Packaging

DS18B20 chip pin configuration:

  • VDDPower supply pin: used to provide the positive power supply to the chip.
  • DQData pin: used for 1-Wire communication and data transmission.
  • GNDGround pin: connects to the chip’s ground (negative power supply).

The DS18B20 chip can be used in different package types, the most common being the TO-92 package and the TO-92-3 package. These packages are small three-pin packages suitable for through-hole mounting and surface mounting.

The TO-92 package is a common small plastic package with pins arranged in order: VDD, DQ, and GND.

The TO-92-3 package is similar to the TO-92 package, but the pin order is slightly different. The pin order of the TO-92-3 package is GND, DQ, and VDD.

In addition to these common package types, the DS18B20 can also be used in other package types, such as SOT-23 and TO-263 packages. These package types may have different pin arrangements and dimensions, as shown in the figure below:

DS18B20 Package Types
DS18B20 Package Types

DC Electrical Characteristics
DC Electrical Characteristics

Internal Structure

The DS18B20 is a digital temperature sensor chip. Its internal structure mainly includes the following components, as shown in the figure below:

DS18b20 Block Diagram
DS18b20 Block Diagram

  • Temperature SensorTemperature sensor: The DS18B20 integrates a temperature sensor to measure the ambient temperature. The sensor typically detects temperature based on changes in a reference voltage and converts it into a digital signal.
  • A/D ConverterA/D converter: The DS18B20 chip contains an analog-to-digital converter (A/D converter) to convert the analog temperature value measured by the sensor into a corresponding digital representation. This allows temperature data to be processed and transmitted in digital form.
  • MemoryMemory: The DS18B20 chip also has internal memory for storing configuration information and temperature measurement results. The memory can store a unique 64-bit address, temperature resolution, and other related settings.
  • Control Logic: The DS18B20 chip contains control logic circuitry for managing temperature measurement, communication, and other related functions. The control logic coordinates the operations of the various parts and communicates with the master device.
  • 1-Wire interface: The DS18B20 uses the 1-Wire communication protocol. Its internal structure includes a data line and a pull-up resistor for communicating with the master device. The 1-Wire interface simplifies the wiring for connection and communication, allowing multiple DS18B20 sensors to be conveniently daisy-chained on the same bus.

Memory Map

64-Bit Lasered ROM Code

64-Bit Lasered ROM Code
64-Bit Lasered ROM Code

only partiallymeaning
28Family code (fixed as 0x28 for DS18B20)
FF 1C 46 93 16 03Unique serial number
5ACRC check

Memory

The memory consists of 9 bytes, and its allocation is shown in the following table.

DS18B20 Memory Map
DS18B20 Memory Map

The DS18B20 has built-in CRC check. The CRC uses the polynomial X⁸ + X⁵ + X⁴ + 1. The host must recalculate the CRC itself. If the two CRCs are equal → data is correct; if not equal → it indicates a communication error. The chip does not automatically prevent errors.

Register introduction

  • Temperature Register: The temperature register stores the result of the most recent temperature measurement. It is a 16-bit register that contains the raw data of the temperature value. By reading the data in the temperature register and combining it with the resolution setting, the actual temperature value can be calculated.
  • Configuration Register: The configuration register is used to set the operating mode and temperature resolution of the DS18B20. It is an 8-bit register, with each bit corresponding to a configuration option. By writing to the configuration register, you can select the temperature resolution, trigger temperature conversion, and choose the power supply mode, etc.

Configuration Register

The configuration register is used to set the operating mode and temperature resolution of the DS18B20. It is an 8-bit register, with each bit corresponding to a configuration option, as shown in the following figure.

Configuration Register
Configuration Register

Note: The default power-on settings are R0=1, R1=1 (12-bit resolution).

**There is a direct relationship between resolution and conversion time.**Bits 5 and 6 of the configuration register (R0 and R1) are used to set the resolution, while bit 7 and bits 0 to 4 are reserved by the device and must not be written. The temperature resolution setting table is shown below:

Relationship between resolution and conversion time
Relationship between resolution and conversion time

Temperature Register

The temperature register of the DS18B20 chip is a 16-bit register used to store the raw data of the most recent temperature measurement. The bit layout of the temperature register is shown below:

Temperature Register Format
Temperature Register Format

The least significant bit (LSB) of the temperature register is the 24 2^{-4} bit, indicating that the minimum resolution of the temperature is 0.0625°C. The other bits successively represent higher temperature resolutions, as follows:

  • 23 2^{-3} (0.125)
  • 22 2^{-2} (0.25)
  • 21 2^{-1} (0.5)
  • 20 2^{0} (1)
  • 21 2^{1} (2)
  • 22 2^{2} (4)
  • 23 2^{3} (8)

The raw data stored in the temperature register of the DS18B20 chip can be used to calculate the actual temperature value through the following steps:

  1. The 16-bit data read from the temperature register can be interpreted as a signed integer, where the most significant bit (MSB) represents the sign bit. If the sign bit is 0, it indicates a positive temperature; if the sign bit is 1, it indicates a negative temperature.
  2. Extract bits 4 through 15 from the temperature register, 12 bits in total, representing the integer value of the temperature (including sign information).
  3. Combine these 12 bits of data with the sign bit to form a signed integer.
  4. According to the selected temperature resolution,multiply the signed integer by the corresponding resolution factorto obtain the actual temperature value.
  5. When the temperature is greater than 0, the sign bit is 0. Interpret these 12 bits of data directly as a signed integer (padding the high bits with 0), then multiply by the resolution factor to obtain the actual temperature.
  6. When the temperature is less than 0, the sign bit is 1. These 12 bits of data are stored in two’s complement form. Take its absolute value (invert the lower 11 bits and add 1), multiply by the resolution factor, and then negate to obtain the actual temperature.

For example, assume a 12-bit temperature resolution is selected, and the data read from the temperature register is 0x1FFF.

The binary representation of 0x1FFF is: 0001 1111 1111 1111. The most significant bit is 0, indicating a positive temperature. Extract the lower 11 bits: 111 1111 1111. Combine these 11 bits with the sign bit to get the signed integer: 0111 1111 1111 (corresponding to 0x07FF).

For 12-bit temperature resolution, the resolution factor is 0.0625°C. Multiply the signed integer 0x07FF by the resolution factor: 0x07FF * 0.0625 = 127.9375°C.

Therefore, the data 0x1FFF read from the temperature register corresponds to an actual temperature of approximately 127.94°C.

Instruction Introduction

According to the DS18B20 communication protocol, the host (microcontroller) must go through three steps to control the DS18B20 to complete a temperature conversion:

  • Before every read/write operation, a reset operation must be performed on the DS18B20.
  • After a successful reset, send a ROM command.
  • Finally, send a RAM command, so that the predetermined operation on the DS18B20 can be performed.

The reset requires the main CPU to pull the data line low for 500 microseconds, then release it. After the DS18B20 receives the signal, it waits about 16 to 60 microseconds, then sends a presence low pulse of 60 to 240 microseconds. When the main CPU receives this signal, it indicates a successful reset.

ROM Command

After the host detects the presence pulse, it can send a ROM command. These commands act on each slave device’s unique 64-bit ROM code, allowing the host to select a specific device when multiple devices exist on the 1-Wire bus.

These commands also allow the host to:

  • Determine how many devices are on the bus
  • Confirm the device type
  • Detect whether any device has triggered an alarm state.

In total, there are 5 types of ROM commands, and each command length is 8-bit

Before sending a DS18B20 function command, the host must first send an appropriate ROM command.

InstructionConvention codeFunction
Search ROM0xF0Used to determine the number of DS18B20 devices on the same bus and to identify the 64-bit ROM address, in preparation for operating each device.
Read ROM0x33Read the code (i.e., the 64-bit address) in the ROM of the DS18B20 temperature sensor.
Match ROM0x55After this command is issued, the host then sends a 64-bit ROM code to access the DS18B20 on the 1-Wire bus that matches the code, causing it to respond, in preparation for the next read/write operation on that DS18B20.
Skip ROM0xCCIgnore the 64-bit ROM address and directly send a temperature conversion command to the DS18B20. Suitable for single-device operation.
Alarm Search0xECAfter execution, only devices whose temperature exceeds the set upper or lower limit will respond.

Search ROM [F0h]

After the system is powered on, the host must identify the ROM codes of all slave devices on the bus, so that it can know:

  • How many devices
  • The type of each device

The host completes this process using an elimination method, which requires repeatedly executingSearch ROM command + data exchangemultiple times until all devices are found.

If there is only one device on the bus, you can use a simplerRead ROM [33h]instead of Search ROM.

Each time a Search ROM process is completed, the host must return to step 1 (initialization) of the communication flow.

Read ROM [33h]

This command can only be used when there is only one slave device on the bus

It allows the host to directly read the 64-bit ROM code of the device without performing the Search ROM process.

If there are multiple devices on the bus but this command is used:

  • All devices will respond simultaneously
  • Data collision will occur

Match ROM [55h]

Host sendsMatch ROM + 64-bit ROM codeThis allows precise selection of a specific device.

Only devices with an exactly matching ROM code will respond to subsequent function commands; all other devices will wait for the next reset.

Applicable to:

  • Multi-device bus
  • Single-device bus

Skip ROM [CCh]

This command will:

  • Simultaneously select all devices on the bus
  • No ROM code needs to be sent

For exampleSkip ROM+Convert T [44h]Allows all DS18B20s to start temperature conversion simultaneously.

NoteSkip ROM+Read Scratchpad [BEh]Can only be used when there is only one device on the bus.

If there are multiple devices on the bus and this command is used:

  • All devices will respond simultaneously
  • Data collision will occur

Alarm Search [ECh]

This command operates in the same way as Search ROM,

but only devices with the alarm flag set will respond. It is used to detect whether any device exceeded the alarm threshold during the most recent temperature conversion.

After each Alarm Search process is completed, the host must return to the initialization step.

ROM Commands Timing Diagram

ROM commands timing diagram
ROM commands timing diagram

Function Command

DS18B20 Function Command Set
DS18B20 Function Command Set

Convert T [44h]

This command starts a temperature measurement.

After the conversion is complete:

  • The temperature result is written to the 2-byte temperature register in the scratchpad.
  • The chip enters a low-power idle state.

If in parasitic power mode

  • Within 10µs after the host sends the command
  • must enable strong pull-up
  • for the entire conversion time tCONV

If in external power mode

The host can continuously read time slots:

  • Returns 0 → still converting
  • Returns 1 → conversion complete

In parasitic power mode, you cannot query this way because the bus is strongly pulled up.

Write Scratchpad [4Eh]

Allows the host to write 3 bytes to the scratchpad:

  • Byte 1 → TH (byte 2)
  • Byte 2 → TL (byte 3)
  • Byte 3 → configuration register (byte 4)

Data must:

  • Low bit first (LSB first)
  • The three bytes must be written completely before reset, otherwise data may be corrupted.

Read Scratchpad [BEh]

The host reads the scratchpad contents:

  • Starting from the least significant bit of byte 0
  • Read through to byte 8 (CRC)

If only partial data is needed:

  • You can reset at any time to terminate the read.

Copy Scratchpad [48h]

The scratchpad’s:

12
TH / TL / 配置寄存器(byte 2,3,4)

Copy to EEPROM.

In parasitic power mode:

  • Strong pull-up must be enabled within 10 µs.
  • Must last at least 10 ms.

Recall E2 [B8h]

Read from EEPROM:

1
TH / TL / 配置

Reload into the scratchpad.

The host can query the status:

  • 0 → Restoring
  • 1 → Complete

This operation is performed automatically at power-up, so the scratchpad has valid data as soon as power is applied.

Read Power Supply [B4h]

Detect power supply mode:

After the host sends a command, it reads a time slot:

  • Bus pulled low → Parasitic power
  • Bus held high → External power

Function Commands Timing Diagram

DS18B20 Function Commands Flowchart
DS18B20 Function Commands Flowchart

Operation Example

There is only one DS18B20 on the bus.

There is only one DS18B20 on the bus, and it uses parasitic power. The host writes TH, TL, and the configuration register to the DS18B20’s scratchpad, then reads the scratchpad and recalculates the CRC to verify the data. After that, the host copies the scratchpad contents to the EEPROM.

Host ModeData (LSB first)Description
TxResetHost sends reset pulse
RxPresenceDS18B20 returns presence pulse
TxCChHost sends Skip ROM (selects all devices on the bus)
Tx4EhHost sends Write Scratchpad
Tx3 bytes of dataWrite TH, TL, and the configuration register
TxResetHost resets again
RxPresenceDS18B20 returns presence pulse
TxCChSkip ROM
TxBEhRead Scratchpad
Rx9 bytes of dataRead scratchpad + CRC and verify
TxResetHost reset
RxPresenceDS18B20 returns presence pulse
TxCChSkip ROM
Tx48hCopy Scratchpad
TxStrong pull-up ≥ 10msStrong pull-up power during EEPROM write

Multiple DS18B20s on the 1-Wire bus

There are multiple DS18B20s on the bus, and they use parasitic power. The host initiates a temperature conversion for one specified DS18B20, then reads its scratchpad and recalculates the CRC to verify whether the data is correct.

Host ModeData (LSB first)Description
TxResetHost sends reset pulse
RxPresenceDS18B20 returns presence pulse
Tx55hHost sends Match ROM command
Tx64-bit ROM codeHost specifies target DS18B20
Tx44hHost sends Convert T (start temperature conversion)
TxStrong pull-up held highHost provides strong pull-up power during the entire conversion time tCONV
TxResetHost sends reset pulse again
RxPresenceDS18B20 returns presence pulse
Tx55hHost sends Match ROM again
Tx64-bit ROM codeSelect the same device again
TxBEhHost sends Read Scratchpad
Rx9 bytes of dataThe host reads the scratchpad (including CRC) and calculates the CRC check by itself.

Communication Timing

DS18B20 uses a strict 1-Wire communication protocol to ensure data integrity. This protocol defines several signal types:

  • reset pulse
  • presence pulse
  • write 0
  • write 1
  • read 0
  • read 1

On the 1-Wire bus, all data is LSB first (least significant bit first).

Reset timing

1-WireAll communication on the bus uses a Initialization sequence Start.

This initialization sequence includes:

  • one sent by the host reset pulse
  • Subsequently sent by the slave device presence pulse

The function of the presence pulse is to tell the host that there are slave devices (e.g., DS18B20) on the bus and they are ready to communicate.

Initialization Procedure—Reset And Presence Pulses
Initialization Procedure—Reset And Presence Pulses

During initialization:

  1. The host sends a reset pulse, pulling the 1-Wire bus low. At least 480µs
  2. The host releases the bus and enters receive mode. After release, the bus is pulled high by a 5kΩ pull-up resistor.
  3. DS18B20 detects a rising edge and waits 15µs ~ 60µs
  4. DS18B20 sends a presence pulse, pulling the bus low. 60µs ~ 240µs
  5. The bus is pulled high by a pull-up resistor (RESISTOR PULLUP)

Read/Write Timing

  • The main unit is here Write slot In the slot, write data to DS18B20,
  • Before Read slot In the slot, read data from DS18B20.

On the 1-Wire bus: each time slot transmits only 1 bit of data.

Read/Write Time Slot Timing Diagram
Read/Write Time Slot Timing Diagram

Write Time Slots

There are two types of write slots:

  • Write 1
  • Write 0

The host uses: Write 1 slot to write logic 1, Write 0 slot to write logic 0

Write Slots
Write Slots

All write slots must satisfy:

  • Each time slot ≥ 60µs and ≤ 120µs
  • Recovery time between adjacent time slots ≥ 1µs

Both types of write slots are started by the host: first pulling the 1-Wire bus low

  • Write 1 rule

    • After the host pulls the bus low: it must release it within 15µs

    • After release: the 5kΩ pull-up resistor will pull the bus high

  • Write 0 rule

    • After the host pulls the bus low: it must hold it low for ≥ 60µs

DS18B20 will detect the bus state in a sampling window: after the host starts the write slot, 15µs ~ 60µs between, if during this time:

  • Bus is high → write 1
  • Bus is low → write 0

Read Time Slots

DS18B20 Data can only be sent in read slots. So:

  • If the host wants to read data, it must actively create read slots
  • The slave cannot speak on its own.

This is a type of:master-driven synchronous communication

The master reads immediately after these commands:

  • Read Scratchpad [BEh]
  • Read Power Supply [B4h]

In addition, the status can be queried using read slots:

  • Convert T [44h] (whether temperature conversion is complete)
  • Recall E2 [B8h]

Read Slots
Read Slots

Read slots are initiated by the master:

  1. Master pulls the bus low ≥ 1µs
  2. Master releases the bus
  3. DS18B20 sends 0 or 1 in this window
    • Send 1 → do nothing → line is pulled up → high level
    • Send 0 → actively pull the bus low (DS18B20 releases the bus at the end of the slot, the pull-up resistor restores high level)

The entire Read Slot must be ≥ 60µs longRecovery between slots ≥ 1µs

Therefore, DS18B20’s data is valid within 15µs after the start of the read slot, so the master must: complete sampling within 15µs

Master Read1 Timing
Master Read1 Timing

example

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394
#include <linux/init.h>#include <linux/module.h>#include <linux/platform_device.h>#include <linux/of.h>#include <linux/cdev.h>#include <linux/slab.h>#include <linux/gpio/consumer.h>#include <linux/delay.h>#include <linux/uaccess.h>// ROM Command#define ROM_CMD_SEARCH_ROM 0xF0#define ROM_CMD_READ_ROM 0x33#define ROM_CMD_MATCH_ROM 0x55#define ROM_CMD_SKIP_ROM 0xCC#define ROM_CMD_ALARM_SEARCH 0xEC// Function Command#define FUNC_CMD_CONVERT_T 0x44#define FUNC_CMD_WRITE_SCRATCHPAD 0x4E#define FUNC_CMD_READ_SCRATCHPAD 0xBE#define FUNC_CMD_COPY_SCRATCHPAD 0x48#define FUNC_CMD_RECALL_E2 0xB8#define FUNC_CMD_READ_POWER_SUPPLY 0xB4struct ds18b20 {        dev_t dev_num;        struct cdev cdev;        struct class *class;        struct device *dev;        struct gpio_desc *gpiod;        struct fasync_struct *fa;        u8 precision;        // wait_queue_head_t wq;};int ds18b20_reset(struct gpio_desc *gpiod){        int timeout;        // host send reset pulse        gpiod_direction_output(gpiod, 0);        udelay(500); // >= 480 us        // host release the bus        gpiod_direction_input(gpiod);        udelay(60); // 15 us ~ 60 us        // ds18b20 send presence pulse        timeout = 240;        while (gpiod_get_value(gpiod)) { // 60 us ~ 240 us                if (timeout == 0)                        goto err;                udelay(1);                timeout--;        }        // resistor pullup        timeout = 240;        while (!gpiod_get_value(gpiod)) { // 60 us ~ 240 us                if (timeout == 0)                        goto err;                udelay(1);                timeout--;        }        udelay(480);        return 0;err:        return -EIO;}void ds18b20_write_bit(struct gpio_desc *gpiod, char bit){        gpiod_direction_output(gpiod, 0);        if (bit) {                udelay(6);                gpiod_direction_input(gpiod);                udelay(80); // 60 ~ 120        } else {                udelay(86);                gpiod_direction_input(gpiod);        }        udelay(1);}void ds18b20_write_byte(struct gpio_desc *gpiod, char byte){        int i;        for (i = 0; i < 8; i++)                ds18b20_write_bit(gpiod, (byte >> i) & 0x1);}u8 ds18b20_read_bit(struct gpio_desc *gpiod){        char bit;        gpiod_direction_output(gpiod, 0);        udelay(2); // 1 us ~ 15 us, as smaller as possible        gpiod_direction_input(gpiod);        udelay(12); // wait for 12 us then read value        bit = gpiod_get_value(gpiod);        udelay(50); // >= 45us        return bit;}u8 ds18b20_read_byte(struct gpio_desc *gpiod){        int i;        char value = 0;        for (i = 0; i < 8; i++)                value |= ds18b20_read_bit(gpiod) << i;        return value;}int ds18b20_open(struct inode *inode, struct file *file){        struct ds18b20 *drv_data = container_of(inode->i_cdev, struct ds18b20, cdev);        //struct gpio_desc *gpiod = drv_data->gpiod;        file->private_data = drv_data;        // Precision set to 12        drv_data->precision = 12;        pr_info("%s success, default precision is %d\n", __func__, drv_data->precision);        return 0;}int ds18b20_release(struct inode *inode, struct file *file){        pr_info("%s\n", __func__);        return 0;}ssize_t ds18b20_read(struct file *file, char __user *buf, size_t size, loff_t *offset){        char ls_byte, ms_byte;        struct ds18b20 *drv_data = file->private_data;        struct gpio_desc *gpiod = drv_data->gpiod;        int16_t raw_temp;        size_t count = min(sizeof(int16_t), size);        int ret;        // Reset        ret = ds18b20_reset(gpiod);        if (ret < 0) {                pr_err("%s: reset fail\n", __func__);                return ret;        }        // Only one DS18B20, send Skip ROM        ds18b20_write_byte(gpiod, ROM_CMD_SKIP_ROM);        // Function Command, send Convert T        ds18b20_write_byte(gpiod, FUNC_CMD_CONVERT_T);        switch (drv_data->precision) {        case 12:                msleep(750);                break;        case 11:                msleep(375);                break;        case 10:                msleep(188);                break;        case 9:                msleep(94);                break;        default:                pr_err("unsupported precision\n");                return -EFAULT;        }        // Reset        ret = ds18b20_reset(gpiod);        if (ret < 0) {                pr_err("%s: reset fail\n", __func__);                return ret;        }        // Only one DS18B20, send Skip ROM        ds18b20_write_byte(gpiod, ROM_CMD_SKIP_ROM);        // Function Command, send Read Scratchpad        ds18b20_write_byte(gpiod, FUNC_CMD_READ_SCRATCHPAD);        // read val        ls_byte = ds18b20_read_byte(gpiod);        ms_byte = ds18b20_read_byte(gpiod);        raw_temp = (int16_t)(((u16)ms_byte << 8) | ls_byte);        pr_info("raw_temp is %d\n", raw_temp);        if (copy_to_user(buf, &raw_temp, count) != 0)                return -EFAULT;        // TODO CRC        return count;}#define DS_CMD_START _IO('D', 0)#define DS_CMD_SET_PREC _IOW('D', 1, int)#define DS_CMD_GET_TEMP _IOR('D', 2, int)long ds18b20_unlocked_ioctl(struct file *file, unsigned int cmd, unsigned long arg){        char ls_byte, ms_byte;        struct ds18b20 *drv_data = file->private_data;        struct gpio_desc *gpiod = drv_data->gpiod;        int16_t raw_temp;        int ret;        switch (cmd) {        case DS_CMD_START:                // Reset                ret = ds18b20_reset(gpiod);                if (ret < 0) {                        pr_err("%s: ds18b20 reset failed\n", __func__);                        return ret;                }                // Only one DS18B20, send Skip ROM                ds18b20_write_byte(gpiod, ROM_CMD_SKIP_ROM);                // Function Command, send Convert T                ds18b20_write_byte(gpiod, FUNC_CMD_CONVERT_T);                switch (drv_data->precision) {                case 12:                        msleep(750);                        break;                case 11:                        msleep(375);                        break;                case 10:                        msleep(188);                        break;                case 9:                        msleep(94);                        break;                default:                        pr_err("unsupported precision\n");                        return -EFAULT;                }                break;        case DS_CMD_SET_PREC:                break;        case DS_CMD_GET_TEMP:                // Reset                ret = ds18b20_reset(gpiod);                if (ret < 0) {                        pr_err("%s: reset fail\n", __func__);                        return ret;                }                // Only one DS18B20, send Skip ROM                ds18b20_write_byte(gpiod, ROM_CMD_SKIP_ROM);                // Function Command, send Read Scratchpad                ds18b20_write_byte(gpiod, FUNC_CMD_READ_SCRATCHPAD);                // read val                ls_byte = ds18b20_read_byte(gpiod);                ms_byte = ds18b20_read_byte(gpiod);                raw_temp = (int16_t)(((u16)ms_byte << 8) | ls_byte);                pr_info("raw_temp is %d\n", raw_temp);                // if (copy_to_user((void *)arg, &raw_temp, sizeof(int16_t)) != 0)                //         return -EFAULT;                if(put_user(raw_temp, (int16_t __user *)arg))                        return -EFAULT;                break;        default:                pr_err("unknown ioctl cmd\n");                return -EFAULT;        }        return 0;}int ds18b20_fasync(int fd, struct file *file, int on){        struct ds18b20 *drv_data = file->private_data;        return fasync_helper(fd, file, on, &drv_data->fa);}struct file_operations ds18b20_fops = {        .owner = THIS_MODULE,        .open = ds18b20_open,        .release = ds18b20_release,        .read = ds18b20_read,        .llseek = no_llseek,        .unlocked_ioctl = ds18b20_unlocked_ioctl,};static int ds18b20_probe(struct platform_device *pdev){        int ret;        //struct device *dev = &pdev->dev;        struct ds18b20 *ds18b20;        ds18b20 = kzalloc(sizeof(*ds18b20), GFP_KERNEL);        if (!ds18b20) {                ret = -ENOMEM;                goto err_kzalloc;        }        ret = alloc_chrdev_region(&ds18b20->dev_num, 0, 1, "ds18b20");        if (ret < 0)                goto err_chrdev_region;        cdev_init(&ds18b20->cdev, &ds18b20_fops);        ds18b20->cdev.owner = THIS_MODULE;        ret = cdev_add(&ds18b20->cdev, ds18b20->dev_num, 1);        if (ret < 0)                goto err_cdev;        ds18b20->class = class_create(THIS_MODULE, "one-wire");        if (IS_ERR(ds18b20->class)) {                ret = PTR_ERR(ds18b20->class);                goto err_class;        }        // /sys/class/one-wire/ds18b20        ds18b20->dev = device_create(ds18b20->class, NULL, ds18b20->dev_num, NULL, "ds18b20");        if (IS_ERR(ds18b20->dev)) {                ret = PTR_ERR(ds18b20->dev);                goto err_device_create;        }        ds18b20->gpiod = gpiod_get_optional(&pdev->dev, "ds18b20", GPIOD_OUT_HIGH);        if (IS_ERR_OR_NULL(ds18b20->gpiod)) {                if (ds18b20->gpiod == NULL)                        ret = -ENODEV;                else if (IS_ERR(ds18b20->gpiod))                        ret = PTR_ERR(ds18b20->gpiod);                goto err_get_gpio;        }        platform_set_drvdata(pdev, ds18b20);        return 0;err_get_gpio:        device_destroy(ds18b20->class, ds18b20->dev_num);err_device_create:        class_destroy(ds18b20->class);err_class:        cdev_del(&ds18b20->cdev);err_cdev:        unregister_chrdev_region(ds18b20->dev_num, 1);err_chrdev_region:        kfree(ds18b20);err_kzalloc:        return ret;}static int ds18b20_remove(struct platform_device *pdev){        struct ds18b20 *ds18b20 = platform_get_drvdata(pdev);        gpiod_put(ds18b20->gpiod);        device_destroy(ds18b20->class, ds18b20->dev_num);        cdev_del(&ds18b20->cdev);        class_destroy(ds18b20->class);        unregister_chrdev_region(ds18b20->dev_num, 1);        kfree(ds18b20);        return 0;}const struct of_device_id match_table[] = {        { .compatible = "even629,ds18b20" },};struct platform_driver ds18b20_pdrv = {        .driver = {                .name = "ds18b20",                .owner = THIS_MODULE,                .of_match_table = match_table,                        },        .probe = ds18b20_probe,        .remove = ds18b20_remove,};module_platform_driver(ds18b20_pdrv);MODULE_LICENSE("GPL");MODULE_AUTHOR("even629<asqwgo@outlook.com>");MODULE_DESCRIPTION("This is a test sample for ds18b20");
Loading comments…