Raspberry Pi IoT In C - 1‑Wire Bus Basics
Written by Harry Fairhead   
Monday, 03 October 2022
Article Index
Raspberry Pi IoT In C - 1‑Wire Bus Basics
Writing

Writing Bits

Our next task is to implement the sending of some data bits to the device. The 1‑wire bus has a very simple data protocol. All bits are sent using a minimum of 60µs for a read/write slot. Each slot must be separated from the next by a minimum of 1µs.

The good news is that timing is only critical within each slot. You can send the first bit in a time slot and then take your time before you send the next bit as the device will wait for you. This means you only have to worry about timing within the functions that read and write individual bits.


To send a 0 you have to hold the line low for most of the slot. To send a 1 you have to hold the line low for just between 1µs and 15µs and leave the line high for the rest of the slot. The exact timings can be seen below:

writetiming

It seems reasonable to use the typical timings given in the datasheets. So for a 0 we hold the line low for 60µs then let it go high for the remainder of the slot, 10µs. To send a 1 we hold the line for 6µs and then let it go high for the remainder of the slot, 64µs. As the only time critical operations are the actual setting of the line low and then back to high, there is no need to worry too much about the speed of operation of the entire function so we might as well combine writing 0 and 1 into a single writeBit function:

void writeBit(uint8_t pin, int b) {
    int delay1, delay2;
    if (b == 1) {
        delay1 = 6;
        delay2 = 64;
    } else {
        delay1 = 60;
        delay2 = 10;
    }
    bcm2835_gpio_fsel(pin, BCM2835_GPIO_FSEL_OUTP);
    bcm2835_gpio_write(pin, LOW);
    bcm2835_delayMicroseconds(delay1);
    bcm2835_gpio_fsel(pin, BCM2835_GPIO_FSEL_INPT);
    bcm2835_delayMicroseconds(delay2);
}

The code at the start of the function simply increases the time between slots slightly. Notice that once again we return the GPIO line to input, i.e. high impedance, rather than driving the line high at the end of the transaction. This allows the line to be pulled high ready for any response from the slave.

You can see two 1s followed by two 0s in the following logic analyzer trace:

onezero

A First Command - Writing Bytes

After discovering that there is at least one device connected to the bus, the master has to issue a ROM command. In many cases the ROM command used first will be the Search ROM command, which enumerates the 64-bit codes of all of the devices on the bus. After collecting all of these codes, the master can use Match ROM commands with a specific 64-bit code to select the device the master wants to talk to.

While it is perfectly possible to implement the Search ROM procedure, it is simpler to work with the single device by using commands which ignore the 64-bit code and address all of the devices on the bus at the same time. Of course, this only works as long as there is only one device on the bus. If there is only one device then we can use the Skip ROM command 0xCC to tell all the devices on the bus to be active.

We now need a function that can send a byte. As we have a writeBit function this is easy:

void sendskip(uint8_t pin){
 writeBit(pin 0);
 writeBit(pin 0);
 writeBit(pin 1);
 writeBit(pin 1);
 writeBit(pin 0);
 writeBit(pin 0);
 writeBit(pin 1);
 writeBit(pin 1);
}

Notice that 0xCC is 1100 1100 in binary and the 1‑wire bus sends the least significant bit first. If you try this out you should find it works but the device doesn't respond because it is waiting for another command. Again, as the time between writing bits isn't critical we can take this first implementation of the function and write something more general if slightly slower.

The writeByte function will write the low eight bits of an int to the device:

void writeByte(uint8_t pin, int byte) {
    int i;
    for (i = 0; i < 8; i++) {
        if (byte & 1) {
            writeBit(pin, 1);
        } else {
            writeBit(pin, 0);
        }
        byte = byte >> 1;
    }
}

Using this we can send a Skip ROM command using:

writeByte(RPI_BPLUS_GPIO_J8_07, 0xCC);

You can see the pattern of bits sent on a logic analyzer:

skiprom

Reading Bits

We already know how the master sends a 1 and a 0. The protocol for the slave device is exactly the same except that the master still provides the slot’s starting pulse. That is, the master starts a 60µs slot by pulling the bus down for at least 1µs. Then the slave device either holds the line down for a further 15µs minimum or it simply allows the line to float high. See below for the exact timings:

readtiming

So all we have to do to read bits is to pull the line down for more than 1µs and then sample the bus after pausing long enough for the line to be pulled up or held low. The datasheet gives 6µs for the master's pulse and a 9µs pause. In practice, a final delay of 2µs seems to work best and allows for the time to change the line's direction.

uint8_t readBit(uint8_t pin) {
    bcm2835_gpio_fsel(pin, BCM2835_GPIO_FSEL_OUTP);
    bcm2835_gpio_write(pin, LOW);
    bcm2835_delayMicroseconds(8);
    bcm2835_gpio_fsel(pin, BCM2835_GPIO_FSEL_INPT);
    bcm2835_delayMicroseconds(2);
    uint8_t b = bcm2835_gpio_lev(pin);
    bcm2835_delayMicroseconds(60);
    return b;
}

A logic analyzer shows the typical pattern of bits from the device:

readbits

By adding some commands to toggle a line after the sample is taken we can see how the timing works:

readsample

You might think that sampling so close to the rising edge of the timing pulse is a bad idea, but the timing of the sample tends to drift longer not shorter and this short timing reduces the error rate.

Finally we need a function that will read a byte. As in the case of writing a byte, there is no time criticality in the time between reading bits so we don't need to take extra special care in constructing the function:

int readByte(uint8_t pin) {
    int byte = 0;
    int i;
    for (i = 0; i < 8; i++) {
        byte = byte | readBit(pin) << i;
    };
    return byte;
}

The only difficult part is to remember that the 1‑wire bus sends the least significant bit first and so this has to be shifted into the result from the right.

we can:

  • Test to see if a device is present:

presence(uint8_t pin) 
  • Write a byte:

   void writeByte(uint8_t pin, int byte) 
  • Read a byte:

   int readByte(uint8_t pin)

These functions will be used in the next two chapters to work with real 1‑wire devices.

These are not the only functions we need to work with the 1‑wire bus. We need to be able to compute the CRC error checks that are commonly used to confirm that data has been transmitted correctly and we need to perform a ROM search to discover what devices are connected to the bus.

FrontCover800

Summary

 

  • The 1-wire bus is a propriety but widely used bus. It is simple and very capable.

  • As its name suggests it makes use of a single data wire and usually a power supply and ground.

  • It is possible to dispense with the power line and the connected device will draw power from the data line.

  • Implementing the 1-wire protocol is mostly a matter of getting the timing right.

  • There are three types of interaction: presence pulse, read and write.

  • The presence pulse simply asks any connected devices to reply and make themselves known.

  • The 1-wire protocol is easier to implement than you might think because each bit is sent as a “slot” and while timing is critical within the slot how fast slots are sent isn’t.

 

Raspberry Pi And The IoT In C Second Edition

By Harry Fairhead

FrontCover800

Buy from Amazon.

Contents

  1. Why Pi For IoT?
  2. Getting Started
  3. Getting Started With The GPIO
  4. Simple Output
  5. Some Electronics
  6. Simple Input
  7. GPIO The Linux Way
       Extract 1:The Linux GPIO Driver 
  8. Advanced Input – Events, Threads, Interrupts
       Extract 1: Events & Interrupts 
  9. Pulse Width Modulation - Servos And More
       Extract 1:Basic Pulse Width Modulation 
  10. Using The I2C Bus
  11. The DHT22 Sensor Implementing A Custom Protocol
  12. Exploring - 1‑Wire Bus Basics ***NEW!
  13. Using iButtons
  14. DS18B20 Temperature Sensor
      Extract 1: The DS18B20 Temperature Sensor 
  15. The Multidrop 1‑Wire Bus
  16. The Serial Port
      Extract 1: 1-wire Via Serial 
  17. Getting Started With The SPI Bus
  18. A to D With The SPI Bus
  19. Connecting With The Web - Sockets
  20. Memory-Mapped GPIO
  21. Almost Real-Time Linux
  22. Appendix I GPIO Sysfs Interface

 <ASIN:1871962633>

<ASIN:B08KLNT2JC>

To be informed about new articles on I Programmer, sign up for our weekly newsletter, subscribe to the RSS feed and follow us on Twitter, Facebook or Linkedin.

Banner


Rust And C++ Should Be Friends?
20/11/2024

The Rust Foundation has just released a statement on Rust and C++ interoperability and Google is ponying up $1 to see that it gets done.



IBM Updates Granite Models
28/10/2024

IBM has released new Granite models that it says provide state-of-the-art performance relative to model size. The Granite 3.0 collection includes a new, instruction-tuned, dense decoder-only LLM.


More News

espbook

 

Comments




or email your comment to: comments@i-programmer.info



Last Updated ( Wednesday, 05 October 2022 )