I use twi_attachSlaveRxEvent() to call a custom function (customSlaveRxEvent()) that will take the data and store it at a global uint8_t* twi_buffer.
Then I have another function when called will take out data from global twi_buffer, i.e. get_from_buffer().
Is it possible when I'm calling get_from_buffer(), thus manipulating the global uint8_t* twi_buffer and removing data from it, the twi interrupt fires up (calling my customSlaveRxEvent()) and messes with the contents of twi_buffer ? If that can happen I should be able to change customSlaveRxEvent() function to use cli() and sei() in order not to be interrupted guaranteeing data consistency at global uint8_t* twi_buffer.. my question about this part is what happens if a twi interrupt fires when I have interrupts disabled, will the incoming twi data be missed ?
Is it possible when I'm calling get_from_buffer(), thus manipulating the global uint8_t* twi_buffer and removing data from it, the twi interrupt fires up (calling my customSlaveRxEvent()) and messes with the contents of twi_buffer ?
Yes, it is. Although "messes with" is probably not the best way to describe it.
If that can happen I should be able to change customSlaveRxEvent() function to use cli() and sei() in order not to be interrupted guaranteeing data consistency at global uint8_t* twi_buffer..
No need to. While the interrupt handler, customSlaveRxEvent() is running, interrupts are already disabled.
You need to prevent interrupts while reading the data, in get_from_buffer(), not while writing the data, in customSlaveRxEvent().
A circular buffer, of appropriate size (i.e. less than 255 bytes), though, would allow you to read the data without worrying about the interrupt handler writing more data, and then atomically move the pointer to the end of the read area would be better, though.
You're right I confused myself.. what I really wanted to say:
If that can happen I should be able to change get_from_buffer() function to use cli() and sei() in order not to be interrupted guaranteeing data consistency at global uint8_t* twi_buffer..
Meaning that when I'm fetching data from twi_buffer I don't want for twi interrupt to put there more data.
The last question still remains, what happens when a twi packet arrive and I have interrupts disabled (thus being inside the get_from_buffer() function) ?
The last question still remains, what happens when a twi packet arrive and I have interrupts disabled (thus being inside the get_from_buffer() function) ?
You are at home. The phone rings, often. That is an interrupt. You have to stop doing what you are doing and answer the phone.
The baby starts crying, so you get ready to feed the baby. In order to be able to concentrate on that, you turn off the ringer on the phone (disable interrupts).
When you are done feeding the baby, you turn the phone back on (re-enable interrupts).
What happens to the calls that came in while you were feeding the baby? They don't all arrive again after you enable interrupts. The first one that arrives after you disable interrupts is waiting to be processed. The rest are discarded.
You should, as I suggested earlier, be able to use a circular buffer to hold the data being written and read. The ISR can interrupt your reading to add more data (if there is room). When the interrupt ends, you resume reading, from a different place on the ring. As you read stuff, you move the pointer to the unread data along. If the buffer size is not greater than the size of a byte (i.e. less than or equal 255 bytes), moving the read pointer is an atomic (uninterruptable) operation.
unsigned long millis()
{
unsigned long m;
uint8_t oldSREG = SREG;
// disable interrupts while we read timer0_millis or we might get an
// inconsistent value (e.g. in the middle of a write to timer0_millis)
cli();
m = timer0_millis;
SREG = oldSREG;
return m;
}
Using millis() a lot could lead to twi packet loss for instance ?
try 2 variables - 1 for writes to the buffer and 1 for reads from the buffer. That way only the write routine manages the Head variable and the read routine manages the Tail variable.
Both Head and Tail start at the same location, as the recieve routine adds data it moves Head, as you remove data you change Tail.
It is possible for the Head to wrap around and pass the Tail, but that shouldn't be a problem if you are coding to keep the buffer emptied.
That uses a circular buffer with int pointers (I wanted to hold more than 255 bytes). That turns interrupts off in two critical spots (testing the tail pointer, and adjusting the tail pointer).
The last question still remains, what happens when a twi packet arrive and I have interrupts disabled (thus being inside the get_from_buffer() function) ?
The last interrupt is remembered, as PaulS said. You would have to turn them off for quite a while for that to be a problem. Don't do that.
I wouldn't have them off all the time during a get_from_buffer function. Only at the critical moment when you are testing or fiddling with the tail pointer. The time taken to increment an int is under a microsecond, I would guess, so that is probably OK.
Isn't it the first that is remembered. Additional interrupts of the same type are discarded, since there is already one in the queue, as I understand it.
Er, well, any interrupt event will set the appropriate flag inside the processor. This flag is cleared by servicing the interrupt, or by manual code.
I suppose with a boolean flag it is moot whether the first or last one was remembered, the fact is that there is only one flag.
However I agree that in the case of something like a UART buffer, it will be the first incoming byte that will fill the buffer (ie. in this case the TWI byte). And then set the flag. In the case of the serial UART I think it has two or three bytes of buffer, so it could hold a couple of incoming bytes.
So I am inclined to agree with PaulS, you could say more accurately that the first one is remembered.
Would this be acceptable ? i.e. code fast enough. This code is part of the function that fetches and removes data from rx_buffer.
Actually there are two buffers, one with payload data (rx_buffer) and another one with the size of the payload for accounting (rx_info).
rx_buffer and rx_info are declared as volatile thus the type cast on memmove().
BTW, when memmove() into a pointer with more allocated memory than needed do I need to memset() the remaining bytes or memmove() will set them automatically to 0x00 ?
First, inside a class you don't normally have to use this. It is implied. So instead of:
this->rx_buffer_used -= t_bytes;
You should be able to write:
rx_buffer_used -= t_bytes;
Next, why do memmove? I don't see this working with a circular buffer.
BTW, when memmove() into a pointer with more allocated memory than needed do I need to memset() the remaining bytes or memmove() will set them automatically to 0x00 ?
No, how can it? You only pass down two pointers and a length. It has no way of knowing what is beyond the length position of either pointer.
I already knew that, anyway thanks for the tip. I like to use "this" because increases code readability, I never told you it was a snippet from a class but you automatically recognized it.
Before shifting all my code's logic, I would like to know if that part of the code is "fast enough" to be inside a no interrupt area; if that's the case then I'm OK with what I've got.
I don't know, since I don't know how many bytes are being moved at once. I just don't understand why you need the memmove.
With circular buffers it makes sense to me to pull out a byte at a time, like I did here:
void loop (void)
{
// insertion and removal point the same, nothing there
noInterrupts (); // atomic test of a 16-bit variable
if (outpoint == inpoint)
{
interrupts ();
return;
}
interrupts ();
// display anything found in the circular buffer
Serial.print (buf [outpoint]);
noInterrupts ();
if (++outpoint >= sizeof buf)
outpoint = 0; // wrap around
interrupts ();
} // end of loop
And even then you don't need the noInterrupts/interrupts if you have a 255 byte buffer, because updating one byte is an atomic operation. I only did that because I wanted to have 1000 bytes of debugging being pumped out at high speed.