Article Index

oneWireScan

The complete function is:

int oneWireScan(uint8_t pin, uint64_t serial[]) {
    static int bitcount = 0;
    static int deviceCount = 0;

    if (bitcount > 63) {
        bitcount = 0;
        deviceCount++;
        return deviceCount;

    }

    if (bitcount == 0) {
        if (presence(pin) == 1) {
            bitcount = 0;
            return deviceCount;
        }
        deviceCount = 0;
        serial[deviceCount] = 0;
        writeByte(pin, 0xF0);
    };

    int b1 = readBit(pin);
    int b2 = readBit(pin);
   
    if (b1 == 0 && b2 == 1) {
        serial[deviceCount] >>= 1;
        writeBit(pin, 0);
        bitcount++;
        oneWireScan(pin, serial);
    };

    if (b1 == 1 && b2 == 0) {
        serial[deviceCount] >>= 1;
        serial[deviceCount] |= 0x8000000000000000LL;
        writeBit(pin, 1);
        bitcount++;
        oneWireScan(pin, serial);

    };

    if (b1 == 1 && b2 == 1) {
        bitcount = 0;
        return deviceCount;
    };

    if (b1 == 0 && b2 == 0) {
        serial[deviceCount] >>= 1;
        writeBit(pin, 0);
        int bitposition = bitcount;
        bitcount++;
        oneWireScan(pin, serial);

        bitcount = bitposition;
        if (presence(pin) == 1){
            bitposition=0;
            return 0;
        }
        
        writeByte(pin, 0xF0);
        uint64_t temp = serial[deviceCount - 1] | (0x1LL << (bitcount));
        int i;
        uint64_t bit;
        for (i = 0; i < bitcount+1; i++) {
            bit = temp & 0x01LL;
            temp >>= 1;
            b1 = readBit(pin);
            b2 = readBit(pin);
            writeBit(pin, bit);
            serial[deviceCount] >>= 1;
            serial[deviceCount] |= (bit << 63);
        }
        bitcount++;
        oneWireScan(pin, serial);
    };

    return deviceCount;
}

 

Scanning The Bus

Scanning the 1-wire bus is a strange thing to do. The reason is that in most cases the devices on the bus don't change. You set up a set of slaves and unless one or more of them fail the serial numbers don't change. 

So why bother scanning the bus?

As long as you program in the serial numbers to your code it will all work. It is not uncommon to find people using 1-wire bus scanners to discover what is connected and then transcribing the serial numbers to the program - sometime even if there is only one device on the bus!

However discovering the devices on the bus dynamically does allow you to write programs that are robust against failures and auto configuring. 

So instead of scanning the bus and hardcoding the serial numbers why not scan the bus each time there is a restart, or even sooner?

Now we come to the problems of scanning the bus. 

The 1-wire bus isn't very robust. Its use of pullup resistors at such high speeds means that it doesn't take much capacitance to make the pulses look more like a capacitor charging up. Getting the wiring right for a 1-wire bus with multiple devices isn't easy. What is more scanning the bus is the only time, apart from the presence pulse, when multiple devices control the bus. A set up that works perfectly well with a single device often has problems when there is more than one device. In fact a standard 1-wire bus debugging technique is to remove all but one device and see if what wasn't working suddenly works.

You can get around some of the problems by lowering the value of the pull up resistor but this does increase the load on the driving GPIO lines and the slave devices. You can reduce the pull up to 2K or even less to account for the reduced working voltage of 3.3V. The 1-wire bus works best as 5V when the lines are long but it is usually not worth the trouble to add a 3.3V to 5V driver. 

If you want to drive a very long line or just need the highest performance you can get then there is a technique which, while it might not be worth using in many situations is worth knowing about. It is called by a number of names but "controllable slew rate" is close enough. The idea is that when the master releases the bus then we have to wait for it to pull up via the resistor.

 

The transistor associated with SPU is related to the strong pull up needed for  parasitic power power mode and you can ignore it. 

The idea is that we can put a transistor in parallel with the pull up resistor and use it to pull the line up faster when appropriate. For example in when the master pulls the line low for a presence pulse it can trigger the pull up transistor when the pulse ends to get the line back up faster than just via the resistor. The fast pull up transistor is switched off just before the slaves start to pull the line low. You can use the fast pull up transistor in the same way when the master writes a zero  or reads or writes a one. Of course you don't need to use it when the master reads a zero because the slave holds the line low for the whole time slot and there is no pull-up. It is claimed that lines as long as 500m can work in this mode. Notice that you now need an additional GPIO line to drive the fast pull up transistor. If you want to know more then see the Maxim design notes listed at the end of the chapter:

In practice you have to face up to the fact that you will get errors when working with the 1-wire bus and especially so with multiple devices. The problem is particularly bad if you are testing a circuit using a protoboard. Things often work better when you move to properly soldered connections and cables - but you will still get errors. However even with the best wiring possible you are still going to get errors due to the inevitable occasional interrupt that Linux will force on your program. 

The only reasonable solution is to add error checking. You can error check the serial numbers using the standard CRC function.

Error Checking

Error checking a bus scan is fairly easy. The only problem is exactly how respond when you detect an error.

When the oneWireScan function returns we have an array with serial numbers ready to check. Each serial number is a 64 bit integer but the CRC checking function accepts an 8 byte array. At this point you might be starting to think about repacking the bits into an array. The solution is much simpler. 

The layout in memory for an 8 byte array and a 64 bit integer is the same so all we have to do is cast the array to a suitable pointer type and pass the 64 bit integer as if it was an 8 byte array. 

That is:

    uint64_t serial[15];
    uint8_t *code;
    int crc;
    int d;
    d = oneWireScan(RPI_GPIO_P1_11, serial);
    crc = 0;
    for (i = 0; i < d; i++) {
        code = (uint8_t*) & serial[i ];
        crc += crc8(code, 8);
    }

When you call oneWireScan you have to provide it with an array of 64 bit integers it can use to store serial numbers. It returns d the number of devices it has found and you need to check for CRC errors in each one. As there is no CRC error if they are all zero we can check for any CRC errors by summing the crc value for each serial number. The important line to explain is:

code = (uint8_t*) & serial[i ];

This takes the address of the ith element of serial and casts it to a pointer to uint8_t i.e. unsigned bytes. The function can use this as if it was an array of unsigned bytes and everything works without having to do any bit manipulation at all. Such is the power, and danger, of programming in C. Everything works as long as the basic assumption that a 64 bit unsigned integer and an 8-byte array are stored in memory in the same way - they usually are but there is no guarantee.

This short piece of code allows you to scan the bus and detect if there is an error. If there is then you can scan it again. Generally speaking you will mostly get a scans without error but you will get errors about one in ten scans for most configurations with small numbers of devices - up to 8 say. Notice that a scan transfers a lot of data and how long it takes depends on how many devices are connected to the bus.