For a sensor I'm interfacing with, I am required to consistently send bytes to the sensor as fast as I2C can handle. This stops me from using Wire.endTransmission, since the overhead from the addressing would be too great. This is why I want to create a new function in the I2C library that allows for writing to the sensor, without a start (repeated or normal), address and stop.
I am having some issues with the required changes to the TWI library. I want to add a function similar to twi_writeTo, but without the aforementioned overhead. However, changing the ISR is proving difficult since for some of the variables used there, like the state names and such, I can't find where they are defined or changed. Some of the TWCR bits are not completely clear to me as well.
Does anyone know how this could be achieved, or if someone has created this functionality before?
What's wrong with using the existing Wire.write(byte) function? It appears to do exactly what you want to do: write data to the sensor as fast as I2C can handle (at the speed you select, the default of which is far lower than the maximum the I2C bus is designed to do).
Unless your sensor does not follow the I2C standard, you will have to first do a Wire.beginTransmission(address) call so the sensor knows you're trying to write data to it.
Wire.write does not actually write any data, it merely sends data to the buffer (which by default is only 32 bytes). The endTransmission function has to be called in order to write the buffered data to the peripheral.
Mmm, interesting, I never knew of that limitation. I also wonder why that is, I thought it's perfectly fine for the master to pause transmission (don't send clock pulses) for a short while.
Looking at the Wire implementation you can send unlimited data if in slave mode using the twi_transmit(data, quantity) function. You'll have to look at the detailed implementation of those functions and the ATmega datasheet and the I2C specifications to understand how this could work.
By the way, what sensor are you working with that needs to receive data continuously? Even more so, why would you want to use the I2C bus for that, which is meant to be shared between devices?
I don't know if I am able to share any specifics about the sensor, but a very fast continuous sending of data is required for proper operation. Using I2C is something that is out of my control, it is just what the sensor uses.
Yes, I tried that, but I got lost trying to understand the ISR, since it's not clear to me how exactly it operates. I just got stuck in loops everytime.
Is the sensor a commercially available product, or something proprietary?
Seems really odd that it needs continuous data sent to it, how do you even know when to get data back from it, much less actually have time to stop the data send and do a data read? Or is this just a case where the sensor needs a clock signal, and you are providing that as a by-product of the I2C communications?
It is a commercially available sensor that I am using in proprietary ways together with the company that designed it. All the I2C bus is used for, is sending this continuous signal to the sensor, reading data from the sensor will happen in a different way.
TWI Bus is a byte oriented and interrupt driven protocol. Whenever the Slave receives its address or a data byte from the Master, it is immediately interrupted and goes to the ISR() to read the arrived address/data from TWDR Register.
Here is an example sketch (Register Level Codes) tested on UNO-NANO. In this example, the Master makes a Roll Call to the Slave at its address (0010011); the Slave receives the address and shows on its Serial Monitor. The attached file may worth reading. I2C Master Sketch (UNO):
void setup()
{
Serial.begin(9600);
//-----TWI Bus and 100 kBit Rate)----------------
bitSet(TWCR, TWEN); //LH → TWEN-bit of TWCR
bitSet(TWSR, TWPS1); //TWSR1 = 1 ; speed selection
bitClear(TWSR, TWPS0); //TWSR0 = 0
TWBR = 36; //100 kbit/s TWI Bus speed
//--Master brings START condition on the TWI Bus:
TWCR = 0b10100100; //TWI bus formation; TWINT-bit is cleared; START condition is asserted
//TWCR = TWINT TWEA TWSTA TWSTO TWWC TWEN X TWIE
//Execution order: TWEN, TWINT, TWSTA, ...
while (bitRead(TWCR, TWINT) != HIGH) //checking if process is done by looking LH for TWINT-bit
{
; //wait until TWINT-bit becomes LH
}
Serial.println((TWSR & 0b11111000), HEX); //shows: 08/correct
//-----Roll calling of Slave at Slave Address: 0010011
TWDR = 0b00100110; //slaveAddres + Write-bit
TWCR = 0b10000100; //TWI bus remains enabled; TWINT-bit is cleared; START bit is OFF
//TWCR = TWINT TWEA TWSTA TWSTO TWWC TWEN X TWIE
//Execution order: TWEN, TWINT, TWSTA, ...
while(bitRead(TWCR, TWINT) != HIGH) //checking if process is completed
{
; //wait until the process is completed
}
Serial.print((TWSR & 0b11111000), HEX); //shows: 18; Slave recognized address
}
void loop()
{
}
I2C Slave Sketch (NANO):
byte recvData;
bool flag = false;;
void setup()
{
Serial.begin(9600);
//-----TWI Bus and 100 kBit Rate)----------------
bitSet(TWCR, TWEN); //LH → TWEN-bit of TWCR
bitSet(TWCR, TWEA); //Enable ACK Bit; see data sheets
// bitSet(TWSR, TWPS1); //TWSR1 = 1 ; speed selection
// bitClear(TWSR, TWPS0); //TWSR0 = 0
// TWBR = 36; //100 kbit/s TWI Bus speed
//--setting 7-bit Slave address as: 0010011--
TWAR = 0b00100110;
//--- Interrupt logic enabled
bitSet(TWCR, TWIE); //Local interrupt enable bit is active
bitSet(SREG, 7); //Global interrupt enable bit is active
}
void loop()
{
if (flag == true)
{
Serial.println(recvData, BIN);
flag = false;
}
}
ISR(TWI_vect)
{
flag = true;
recvData = TWDR;
bitSet(TWCR, TWINT); //clear TWINT flag by placing HIGH at this (TWINT) bit
}
Can you give a START condition and then endlessly send data ? A data byte is only 9 clock pulses and 8 databits.
At 100kHz, the overhead is only little. Have you tried 400kHz and higher ? The standard I2C bus is maximum 400kHz, but some chips can go up to 1MHz. If you use the Wire.setClock() on a Arduino Uno, then you have to verify the signals with a Logic Analyzer to see if it is set to that frequency. I think that 50-400kHz do work.
If the "sensor" has the I2C interface implemented in hardware and is not a processor and it does not use clock pulse stretching, then the SCL signal can be a strong high and low output, it does not need to be a open-drain output.
Which Arduino board do you use ?
I'm currently working with an Arduino Uno with an ATmega328P, but I already received an Uno R4 Minima that I plan to use for the 1MHz I2C (I am still having issues with getting the regular I2C to work on this board). I don't know about the I2C implementation on the sensor. About the overhead, I need to send some other configuration bytes every time as well, and this increases the overhead a lot. These would not be neccesary with the continuous writing I'm looking for.