Hi, I have been trying to get this code to run but I cant seem to find a solution that I can understand. My current code is for a project related to a Dam where we have a water flow sensor that needs interrupts to determine how much water has flown through the sensor, but while trying to send that information to the LCD the code freezes. Hope that someone can help me understand better why this happens and if there is any solution. If you have any questions with the code dont mind letting me know.
#include <Servo.h>
#include <LiquidCrystal_I2C.h>
const int Trigger = 3; //Pin digital 2 para el Trigger del sensor
const int Echo = 7; //Pin digital 3 para el Echo del sensor
const int pin_sensor_amarillo = 4; //Pin digital 4 para el Flotador con cables amarillos
const int pin_sensor_negro = 5; //Pin digital 5 para el Flotador con cables negros
const int pin_sensor_flujo = 3; //Pin digital 6 para el sensor de flujo
const int servomotor = 9;
const int pin_alarma_ultra = 11; //Pin digital 11 para detectar la activacion de una condicion en el Ultrasonico
const int pin_alarma_amarillo = 12; //Pin 12 de arduino para representar una alerta del sensor de flujo amarillo
const int pin_alarma_negro = 13; //Pin 13 de arduino para representar una alerta del sensor de flujo negro
int valor1 = 0;
int valor2 = 0;
Servo myservo; //creamos un objeto servo
LiquidCrystal_I2C lcd(0x27,16,2);
volatile int NbTopsFan;
int Calc;
int hallsensor = 2;
void rpm ()
{
NbTopsFan++;
}
void setup() {
pinMode(hallsensor, INPUT);
myservo.attach(9);
Serial.begin(9600);
attachInterrupt(0, rpm, RISING);
pinMode(Trigger, OUTPUT);
pinMode(Echo, INPUT);
digitalWrite(Trigger, LOW);
lcd.init();
lcd.backlight();
}
void loop()
{
NbTopsFan = 0;
sei();
delay (1000);
cli();
Calc = (NbTopsFan * 60 / 5.5);
Serial.print (Calc, DEC);
Serial.print (" Litros/min\r\n");
digitalWrite(Trigger, HIGH);
delayMicroseconds(10);
digitalWrite(Trigger, LOW);
long t;
long d;
t = pulseIn(Echo, HIGH);
d = t/59;
if(d <= 12)
{
digitalWrite(pin_alarma_ultra, HIGH);
}
else
{
digitalWrite(pin_alarma_ultra, LOW);
}
Serial.print (Calc,3);
Serial.print (" L/m");
Serial.println();
Serial.print("Distancia: ");
Serial.print(d);
Serial.print("cm");
Serial.println();
lcd.setCursor(0, 0);
lcd.print("Distancia: ");
lcd.print(d);
lcd.print(" cm");
lcd.setCursor(0, 1);
lcd.print("Flujo:");
lcd.print(Calc);
lcd.print(" L/min");
if(d>=15)
{
myservo.write(108);
}
if(d<=9)
{
myservo.write(15);
}
}
and several solutions are proposed, one using "blink without delay" code to read the counts, and one maintaining the delay( ) based counting but managing the sei() and cli() functions to allow communications to work.
So just to make sure that I am understanding it correctly, I need to enable the interrupts for a second in my loop so that the I2C can do its job ? Or where exactly should I enable the interrupts ?
Apologies for the late reply, but my internet was down.
If you follow the referenced thread, you would see that I would attach interrupts in setup and only disable them very briefly to transfer the isr data to a loop variable and then reenable after the transfer.
Sorry I don't understand that statement. I2C is not interrupt driven by its very nature, given that the I2C data line is an input and is not normally connected to an interrupt for the specific reason it can be interrupted by another process without data loss.
As far as interrupts go, you do of course only disable them long enough to process a small routine or to set a flag, depending on your requirements, then re-enable them immediately after.
gfvalvo is absolutely 100% correct. And what Phillips says is totally irrelevent. Phillips defined the electrical interface protocol, but NOT the hardware implementation of the interface. It CAN be implemented without interrupts, but most/all Arduinos have HARDWARE I2C interfaces and are absolutely interrupt-driven. If you can't accept that, then you're not going to solve your problem.
Try reading the source code. For your convenience, here's the ISR for the TWI vector from twi.c:
ISR(TWI_vect)
{
switch(TW_STATUS){
// All Master
case TW_START: // sent start condition
case TW_REP_START: // sent repeated start condition
// copy device address and r/w bit to output register and ack
TWDR = twi_slarw;
twi_reply(1);
break;
// Master Transmitter
case TW_MT_SLA_ACK: // slave receiver acked address
case TW_MT_DATA_ACK: // slave receiver acked data
// if there is data to send, send it, otherwise stop
if(twi_masterBufferIndex < twi_masterBufferLength){
// copy data to output register and ack
TWDR = twi_masterBuffer[twi_masterBufferIndex++];
twi_reply(1);
}else{
if (twi_sendStop){
twi_stop();
} else {
twi_inRepStart = true; // we're gonna send the START
// don't enable the interrupt. We'll generate the start, but we
// avoid handling the interrupt until we're in the next transaction,
// at the point where we would normally issue the start.
TWCR = _BV(TWINT) | _BV(TWSTA)| _BV(TWEN) ;
twi_state = TWI_READY;
}
}
break;
case TW_MT_SLA_NACK: // address sent, nack received
twi_error = TW_MT_SLA_NACK;
twi_stop();
break;
case TW_MT_DATA_NACK: // data sent, nack received
twi_error = TW_MT_DATA_NACK;
twi_stop();
break;
case TW_MT_ARB_LOST: // lost bus arbitration
twi_error = TW_MT_ARB_LOST;
twi_releaseBus();
break;
// Master Receiver
case TW_MR_DATA_ACK: // data received, ack sent
// put byte into buffer
twi_masterBuffer[twi_masterBufferIndex++] = TWDR;
__attribute__ ((fallthrough));
case TW_MR_SLA_ACK: // address sent, ack received
// ack if more bytes are expected, otherwise nack
if(twi_masterBufferIndex < twi_masterBufferLength){
twi_reply(1);
}else{
twi_reply(0);
}
break;
case TW_MR_DATA_NACK: // data received, nack sent
// put final byte into buffer
twi_masterBuffer[twi_masterBufferIndex++] = TWDR;
if (twi_sendStop){
twi_stop();
} else {
twi_inRepStart = true; // we're gonna send the START
// don't enable the interrupt. We'll generate the start, but we
// avoid handling the interrupt until we're in the next transaction,
// at the point where we would normally issue the start.
TWCR = _BV(TWINT) | _BV(TWSTA)| _BV(TWEN) ;
twi_state = TWI_READY;
}
break;
case TW_MR_SLA_NACK: // address sent, nack received
twi_stop();
break;
// TW_MR_ARB_LOST handled by TW_MT_ARB_LOST case
// Slave Receiver
case TW_SR_SLA_ACK: // addressed, returned ack
case TW_SR_GCALL_ACK: // addressed generally, returned ack
case TW_SR_ARB_LOST_SLA_ACK: // lost arbitration, returned ack
case TW_SR_ARB_LOST_GCALL_ACK: // lost arbitration, returned ack
// enter slave receiver mode
twi_state = TWI_SRX;
// indicate that rx buffer can be overwritten and ack
twi_rxBufferIndex = 0;
twi_reply(1);
break;
case TW_SR_DATA_ACK: // data received, returned ack
case TW_SR_GCALL_DATA_ACK: // data received generally, returned ack
// if there is still room in the rx buffer
if(twi_rxBufferIndex < TWI_BUFFER_LENGTH){
// put byte in buffer and ack
twi_rxBuffer[twi_rxBufferIndex++] = TWDR;
twi_reply(1);
}else{
// otherwise nack
twi_reply(0);
}
break;
case TW_SR_STOP: // stop or repeated start condition received
// ack future responses and leave slave receiver state
twi_releaseBus();
// put a null char after data if there's room
if(twi_rxBufferIndex < TWI_BUFFER_LENGTH){
twi_rxBuffer[twi_rxBufferIndex] = '\0';
}
// callback to user defined callback
twi_onSlaveReceive(twi_rxBuffer, twi_rxBufferIndex);
// since we submit rx buffer to "wire" library, we can reset it
twi_rxBufferIndex = 0;
break;
case TW_SR_DATA_NACK: // data received, returned nack
case TW_SR_GCALL_DATA_NACK: // data received generally, returned nack
// nack back at master
twi_reply(0);
break;
// Slave Transmitter
case TW_ST_SLA_ACK: // addressed, returned ack
case TW_ST_ARB_LOST_SLA_ACK: // arbitration lost, returned ack
// enter slave transmitter mode
twi_state = TWI_STX;
// ready the tx buffer index for iteration
twi_txBufferIndex = 0;
// set tx buffer length to be zero, to verify if user changes it
twi_txBufferLength = 0;
// request for txBuffer to be filled and length to be set
// note: user must call twi_transmit(bytes, length) to do this
twi_onSlaveTransmit();
// if they didn't change buffer & length, initialize it
if(0 == twi_txBufferLength){
twi_txBufferLength = 1;
twi_txBuffer[0] = 0x00;
}
__attribute__ ((fallthrough));
// transmit first byte from buffer, fall
case TW_ST_DATA_ACK: // byte sent, ack returned
// copy data to output register
TWDR = twi_txBuffer[twi_txBufferIndex++];
// if there is more to send, ack, otherwise nack
if(twi_txBufferIndex < twi_txBufferLength){
twi_reply(1);
}else{
twi_reply(0);
}
break;
case TW_ST_DATA_NACK: // received nack, we are done
case TW_ST_LAST_DATA: // received ack, but we are done already!
// ack future responses
twi_reply(1);
// leave slave receiver state
twi_state = TWI_READY;
break;
// All
case TW_NO_INFO: // no state information
break;
case TW_BUS_ERROR: // bus error, illegal stop/start
twi_error = TW_BUS_ERROR;
twi_stop();
break;
}
}
The AVR TWI is byte-oriented and interrupt based. Interrupts are issued after all bus events, like reception of a byte or transmission of a START condition.
The data sheet for the processor you are using will also contain more information about the implementation of the twi bus.
Not I2C then, so its just a question of the naming used in this thread.
As you have pointed out this is an AVR TWI which raises sets (0)interrupt flag, which has to be cleared(1) after receiving data to allow the TWI to operate again.
• Bit 7 – TWINT: TWI Interrupt Flag
This bit is set by hardware when the TWI has finished its current job and expects application software response. It is not automatically cleared by hardware when executing the interrupt routine. So, you must clear TWINT Flag by writing a logic one to it.
Microchip states the following:
The Two-Wire Serial Interface (TWI) is compatible with Philips I2C protocol.
Loosely speaking, except I2C does not have interrupt flags.
There speaks ignorance. Phillips invented the I2C specification in agreement with other companies which are hardware based (Texas), and as far as I am aware many hardware devices did not use interrupts either for I2C.
Of course it is. As @RayLivingston pointed out, the Phillips spec has absolutely nothing to do with implementation details. I stand by my original statement. You are simply wrong.
I've been working with I2C since the 1970's when it was first introduced by Phillips. In fact, I even worked FOR Phillips in the '80s. I have designed dozens of products using I2C, and I have personally designed about a dozen chips with built-in I2C interfaces. What, exactly, is your experience that makes you so confident of your expertise on this topic?