Read The Serial Number
The DS1990R and similar are very simple 1-wire devices and they only support a few commands and if it is the only device on the bus then only one of these is useful. After the initilization pulse and the device has sent its presence pulse back it waits for a command byte to be transmitted from the master.
In most cases the only command you need to send is Read ROM which is either 0x33 or 0x0F. In respond the slave then sends the unique serial number stored in its memory.
Normally you would have to send the Skip ROM command or Match ROM command to select the slave device but in this case it isn't necessary because the iButton is usually the only device on the bus.
To send the Read Rom command you use the writeByte function introduced in the previous chapter:
writeByte(RPI_GPIO_P1_07, 0x33);
Now you can read the data that the slave is read to send. The slave sends eight bytes. The first byte is the family code which identifies the type of device for a simple iButton this is a 0x01. Next it sends the six bytes of the serial number and finally a CRC check byte. Often you can ignore the check byte because the transmission is reliable and getting it wrong isn't important. In this case you can't because the connection with the iButton is subject to a lot of noise.
Reading the eight bytes is easy:
uint8_t code[8]; int i; for (i = 0; i < 8; i++) { code[i] = readByte(RPI_GPIO_P1_07); }
Now we have all of the data we need but it does need to be checked for transmission errors.
Computing The CRC
A CRC Cyclic Redundancy Checksum is often regarded as a tough problem. However once you know the basic idea you should be able implement any CRC calculation - perhaps not in the most efficient way but it will work. For low data rate applications high efficiency isn't needed and you can make use of a direct implementation.
The data sheet specified the CRC used as a shift register:
The first thing to notice is the polynomial that defines the CRC. This is generator polynomial and in this case it is:
X8 + X5 + X4 + 1.
The first question to answer is what is the connection between binary values and polynomials?
The answer is that you can treat a binary number as the coefficients of a polynomial. For example 101 is
1*X2+0*X+1
Each bit position corresponds to a power of X. Using this notation creates a very simple relationship between multiplying by X and a left shift. For example
1*X2+0*X+1 * X = 1*X3+0*X2+1X+0
which corresponds to 101 <<1 == 1010.
You can see that this extends to multiplying one polynomial by another and even polynomial division - all accomplished by shifting and exclusive or.
The CRC is the remainder when you divide the polynomial that represents the data by the generator polynomial. The computation of the remainder is what the shift register specified on the data sheet does. The fact that the division can be implemented so simply in hardware is what makes this sort of CRC computation so common. All the hardware has to do is zero the shift register and feed the data into it. When all the data has been shifted in what is left in the shift register is the CRC i.e. the remainder.
To check the data you have received all you have to do is run it thought the shift register again and compare the computed CRC with the one received. A better trick is to also run the received CRC though the shift register when if there have been no errors will result in zero.
You can look into the theory of CRCs, bit sequences and polynomials further - and it is interesting and practically useful - but we now know everything we need to if we want to implement the CRC used by the iButton. You can delve further but all we have to do is implement the shift register in software.
From the diagram what we have to do is take each bit of the input data and exclusive or it with the least significant bit of the current shift register. If the input bit is zero then the exclusive ors in the shift register don't have any effect and the crc just has to be moved one bit to the right. If the input bit is 1 then you have to exclusive or bits at positions 3 and 4 with 1 and we have to put a 1 in at position 7 to simulate shifting a 1 into the register. i.e. exclusive or the shift register with 10001100 i.e.
So the algorithm for a single byte is:
for (j = 0; j < 8; j++) { temp = (crc ^ databyte) & 0x01; crc >>= 1; if (temp) crc ^= 0x8C; databyte>>= 1; } }
First we exclusive or the data with the current CRC and extract the low order bit into temp. Then we right shift the CRC by 1 place. If the low order result, stored in temp was a 1 you have to exclusive or the CRC with 0x8C to simulate the exclusive ors in the shift register and shift in a 1 at the most significant bit. Then shift the data one place right and repeat for the next data bit.
With this worked out we can now write a function that computes the CRC for the entire 8 byte data:
uint8_t crc8(uint8_t *data, uint8_t len) { uint8_t i; uint8_t j; uint8_t temp; uint8_t databyte; uint8_t crc = 0; for (i = 0; i < len; i++) { databyte = data[i]; for (j = 0; j < 8; j++) { temp = (crc ^ databyte) & 0x01; crc >>= 1; if (temp) crc ^= 0x8C; databyte >>= 1; } } return crc; }
With this in place we can now check the CRC and print the results:
uint8_t crc = crc8(code, 8); printf("CRC %hhX ", crc); for (i = 0; i < 8; i++) { printf("%hhX ", code[i]); } printf("\n\r"); fflush(stdout); } bcm2835_delayMicroseconds(100000);
A Complete iButton Function
Putting the functions together we can create a single function that returns a 0 if there is no iButton or if the CRC is incorrect and 1 if an iButton has been read and the CRC is ok.
int readiButton(uint8_t pin, uint8_t *data) { int b = presence(pin); if (b != 0) return 0; writeByte(pin, 0x33); int i; for (i = 0; i < 8; i++) { data[i] = readByte(pin); } uint8_t crc = crc8(data, 8); if (crc == 0) return 1; return 0; }
Notice that if it returns a 1 then the serial number is in the second parameter which has to be passed an 8 byte array.
For example:
int i; uint8_t code[8]; for (;;) { int p = readiButton(RPI_GPIO_P1_07, code); if (p == 1) { for (i = 0; i < 8; i++) { printf("%hhX ", code[i]); } printf("\n\r"); fflush(stdout); } bcm2835_delayMicroseconds(100000); };