I am quite new to using I2C with arduino and I am trying to interface schurter CDS1 Touch Display Switch with arduino nano. When I am trying to read the register 0xFC containing device serial number, the value returned for the first byte in FC. Similarly, when I read the 'Who am I' register 0x00, the value returned is 0. The read command is simply returning the value written earlier. What am I doing wrong?
Here's the code:
#include <Wire.h>
byte error=1, CDS1_found, lowByte=0, byte0=0;
int address = 0x3F;
int numbytes =0;
void setup()
{
delay(9000); //Initial delay to set up CDS1 switch.
Wire.begin();
Serial.begin(9600);
TWBR = 72;
pinMode(13, OUTPUT);
Serial.println("Scanning I2C bus...");
Wire.beginTransmission(address);
digitalWrite(13, HIGH);
error = Wire.endTransmission();
Serial.println(error,HEX);
if (error == 0)
{
Serial.println("I2C device found at 0x3F");
Serial.println("Send read request to read who am I register...");
Wire.beginTransmission(address);
Wire.write(0xFC); //Serial number
error = Wire.endTransmission();
Serial.println(error,HEX);
numbytes = Wire.requestFrom(address, 1);
Serial.println(numbytes,HEX);
while(Wire.available()<1);
byte0 = Wire.read();
Serial.println(byte0, HEX);
delay(300);
digitalWrite(13, LOW);
}
}
void loop()
{
}
And here's the result from serial monitor:
Scanning I2C bus...
0
I2C device found at 0x3F
Send read request to read who am I register...
0
1
FC
Please can someone point out what I am doing wrong and help me solve this issue? Thanks in advance.
A Arduino Nano is a 5V board, the sensor is a 3.3V sensor.
It will probably work, but you might need a I2C level shifter.
Do you use pullup resistors at SDA and SCL ? What is the value ? Are the pullup resistors to 3.3V ?
Please remove this line:
TWBR = 72 ;
If you need to change the clock speed, use Wire.setClock(). That is for later.
Instead of just writing the register address, also the number of bytes to read should be send.
I think you also have to write and read the CRC byte.
This sentence is confusing: "Between the read command from the master and the start of clocking out the values from the slave a delay of at least 10ms must be inserted"
The pictures show what they mean, it is a delay of 10 ms between writing the register address and reading data.
To be able to communicate with it, you need to calculate the CRC.
The ID at register address 0 is the most simple data. Start with that.
byte length = 1; // data length
byte register_address = 0x00; // ID register
byte crc = ? ? ? ?
Wire.beginTransmission( address);
Wire.write( length);
Wire.write( register_address);
Wire.write( crc);
Wire.endTransmission();
delay( 10); // required minimal delay
Wire.requestFrom( address, 2); // one databyte plus crc
int id = Wire.read();
int crc = Wire.read();
Serial.println( id, HEX); // hoping for 0x53
The CRC is a CRC-8-CCITT with polynomial of 0x07. I can not find a good example. I hope someone else can give a working function.
I suspect that the CRC8 you are using is not the correct algorithm. The polynomial required is 0x07. There are many variants, and this one, commonly used with Arduino, uses 0x8C as the polynomial. You could try replacing the 0x8C below with 0x07.
The only way to tell for sure is to get some examples of valid messages, with the correct CRC.
//CRC-8 - based on the CRC8 formulas by Dallas/Maxim
//code released under the therms of the GNU GPL 3.0 license
byte CRC8(const byte *data, byte len) {
byte crc = 0x00;
while (len--) {
byte extract = *data++;
for (byte tempI = 8; tempI; tempI--) {
byte sum = (crc ^ extract) & 0x01;
crc >>= 1;
if (sum) {
crc ^= 0x8C;
}
extract >>= 1;
}
}
return crc;
}
How did you come up with the contents of crc_array in the following?
Study these instructions from the user guide carefully. I don't understand them completely, so you may have to experiment.
I think you would have an easier time using "RS232" communications.
I2C write cycle:The machine control unit sends the start condition and afterwards the write command consisting of the I2C slave address of the CDS1 and the R/W_n bit set for write, followed by a data byte which defines the number of bytes to be written. Then the address of the first instruction register needs to be sent followed by the data bytes. Each single byte will be acknowledged by an acknowledge bit. In addition, the register address is automatically incremented after each data byte and its acknowledge bit. Once the last data byte was sent, the machine control unit has to send a CRC-8 check sum to the CSD1 which was calculated about the whole package. The CDS1 compares the preserved check sum to the calculated one. If a CRC error is detected, the CDS1 generates an interrupt. When the write sequence is finished, the machine control unit sends the I2C stop condition.
I2C
As far as I know, the I2C address (with Read/Write bit) is not a part of the data for a CRC.
Is that specified in the datasheet somewhere ? A CRC is always used with data bytes.
In the light of your original post and Post#4, the following program is prepared. You may try it:
I2C write cycle:The machine control unit sends the start condition and afterwards the write command consisting of the I2C slave address of the CDS1 and the R/W_n bit set for write, followed by a data byte which defines the number of bytes to be written. Then the address of the first instruction register needs to be sent followed by the data bytes. Each single byte will be acknowledged by an acknowledge bit. In addition, the register address is automatically incremented after each data byte and its acknowledge bit. Once the last data byte was sent, the machine control unit has to send a CRC-8 check sum to the CSD1 which was calculated about the whole package. The CDS1 compares the preserved check sum to the calculated one. If a CRC error is detected, the CDS1 generates an interrupt. When the write sequence is finished, the machine control unit sends the I2C stop condition.
#include <Wire.h>
#define slaveAddress 0x3F
#define bytesToSend 0x02 //number of data bytes for two register
#define addressOfFirstReg 0xFC
#define firtDataByte 0x53
#define secondDataByte 0x17
byte data [5] = {slaveAddress, bytesToSend, addressOfFirstReg, firtDataByte, secondDataByte};
byte crc;
void setup()
{
// delay(9000); //Initial delay to set up CDS1 switch.
Wire.begin();
Serial.begin(9600);
Wire.beginTransmission(slaveAddress); //0x3F
Wire.write(bytesToSend); //0x02
Wire.write(addressOfFirstReg); //0xFC
Wire.write(firtDataByte); //0x53 for the First Register
Wire.write(secondDataByte); //0x17 for the next register
crc = CRC8(data, 5);
Wire.write(crc); //for above 5-byte; polynomial must match with slave's polynomial
Wire.endTransmission();
// Serial.print(crc, HEX); //prints: E6
//------------------------------------------------------------------
Wire.beginTransmission(slaveAddress); //0x3F
Wire.write(addressOfFirstReg); //0xFC pointing to firtsRegister of Slave
Wire.endTransmission();
Wire.requestFrom(slaveAddress, 2); //2-byte must come from slave register
byte x = Wire.read();
Serial.println(x, HEX); // Serial Monitor should show: 53.
byte y = Wire.read();
Serial.println(y, HEX); // Serial Monitor should show: 17
//------------------------------------------------------------------
}
void loop()
{
}
//due to @jremington
//CRC-8 - based on the CRC8 formulas by Dallas/Maxim
//code released under the therms of the GNU GPL 3.0 license
byte CRC8(const byte *data, byte len)
{
byte crc = 0x00;
while (len--)
{
byte extract = *data++;
for (byte tempI = 8; tempI; tempI--)
{
byte sum = (crc ^ extract) & 0x01;
crc >>= 1;
if (sum)
{
crc ^= 0x8C;
}
extract >>= 1;
}
}
return crc;
}
The datasheet (User Manual) has not enough information. You should contact Schurter.
This is Schurter's page about the CDS1 : https://www.schurter.com/en/Landing-Page/Products-and-Technologies/CDS1.
I have just sent them a mail with a link to this topic and I ask them for an example for I2C.
jremington:
Much simpler data transmission and reception.
But, at the cost of reliability? To make async serial protocol more reliable, we need to create the handshakings artificially by software instructions. The error checking is a simile parity bit.
The I2C has built-in handshaking in the protocol itself. It thus provides far greater reliability than the RS232 protocol. The error checking can be done using much better CRC scheme.
@GolamMostafa: I tried your code now and this is the result:
FF
FF
I am really beginning to think whether I should try SPI or RS232 for a change because I tried I2C comms with MPU6050 which is another I2C device and it works fine !
Could there be a problem with I2C in this display?
The CRC checksum is not right yet, in my opinion. If I had such a display myself, then I would write a sketch that tests it all, with all checksums and all possibilities, until I got the right answer. With the right answer, perhaps I could reverse-engineer the CRC function. There are only 256 possible checksum values and only a small number of possible ways to calculate it.
There was a firmware update for I2C. That update is for the touch function. Even without the update, the I2C should work.
Since I2C data is transferred most significant bit first, it is possible that the CRC calculation is done MSB first as well. The code in msg #4 calculates the CRC starting at the low order bit.
The code below starts with the most significant bit. Give it a try.
The code in the first post of that thread appears to be exactly what you need. It communicates with the CDS1 display using RS232, evidently using the correct checksum calculation. The author claims that the display works perfectly but there are problems with relays attached to the system.
While there is obviously a lot of stuff you don't need, the framework is there for you to build your own application. Note that the code reads the CDS1 interrupt output.
Here is the checksum calculation:
// CRC table
byte crcTable[256];
...
// Initialise CRC Table
CRC(0x07);
...
// *** CRC Calculation ***
byte AppendChecksum(byte command[], byte size)
{
byte length = size - 1;
byte CRC = CalculateCRC8(command, length);
return CRC;
}
// Create CRC Table with given polynom
void CRC( byte polynome)
{
int temp;
int i;
int j;
for (i = 0; i < 256; ++i)
{
temp = i;
for (j = 0; j < 8; ++j)
{
if ((temp & 0x80) != 0)
{
temp = (temp << 1) ^ polynome;
}
else
{
temp <<= 1;
}
}
crcTable[i] = (byte)temp;
}
}
byte CalculateCRC8(byte data[], byte size)
{
byte crc = 0;
byte i = 0;
if (size <= 0)
{
return 0;
}
for (i = 0; i < size; i++)
{
crc = crcTable[crc ^ data[i]];
}
return crc;
}