write to interrupted variable

14 Nov 2010

hello everyone,

I'm trying to implement a circular buffer, but my problem is really with interrupts in general.

How can I write to a variable that is then read by an interrupt-handling function?

I've attached the serial interrupt to a buffering function, and it autamatically reads in whatever is on the line as soon as it's received.  What I want to do is change the point at which the buffer circles around by assigning to it with other code in the class (which contains the handler).

The code I have now simply doesn't perform the assignment, the handler recirculates the buffer at the value that was assigned before the handler was attached.

My code:

    private:
        void FillSerialBuffer();

    public:
        MS3DMGX2(PinName tx, PinName rx);

        volatile unsigned char Buffer[87];
        volatile unsigned char BufferEnd;
        volatile unsigned char BufferLength;

        void Mode(unsigned char Selection);

MS3DMGX2::MS3DMGX2(PinName tx, PinName rx) : DataLines(tx, rx), LED(LED1),
    BufferEnd(0), BufferLength(31)
{
    DataLines.baud(115200);
    DataLines.format(8, Serial::None, 1);
    DataLines.attach(this, &MS3DMGX2::FillSerialBuffer, Serial::RxIrq);        
}

void MS3DMGX2::Mode()
{
    BufferLegnth = 86;
}

void MS3DMGX2::FillSerialBuffer()
{
    while (DataLines.readable())
    {
        Buffer[BufferEnd] = DataLines.getc();
        BufferEnd++;
        BufferEnd %= BufferLength;
    }
}

14 Nov 2010

To run a circular buffer you need two offset pointers like BufferRead and BufferWrite to use in place of your BufferEnd.

The data in the buffer does not move you have to keep moving the offset pointers Buffer[BufferWrite++] = getc(); and x = Buffer[BufferRead++];

You also have to check that the offset pointers do not run outside the buffer. That is easy if the buffer length is a simple binary number like 64 or 128. This allows you to 'And' each pointer with BufferLength after each increment.

The final task is to make sure that the 'Read' pointer never overtakes the 'Write' pointer. If you do a set of Writes followed by a set of Reads to get all the data, and the buffer length is big enough to always hold the set of 'Writes', then you can just zero both offset pointers after the 'Read' set. If reading and writing is interlaced then this part of the code gets to be a little tricky.

Good luck John

14 Nov 2010

See SerialBuffered which does all this.

14 Nov 2010

Sorry, I don't think my question was clear.  Please let me try again:

"How can I write to a variable that is being read by an interrupt-handling function?"

I'm doing the recircle with a mod-assign, and I want to make the buffer length variable.  What I'm trying to do is assign values to the buffer length after the interrupt is enabled.  I have a BufferStart too, and (oddly, I know) I'm not concerned about the start overtaking the end.  I took out all of the impertinent code for clarity.  I could use SerialBuffered if it had a way to tell me how may bytes are available in the buffer.

--->all I need is the ability to assign to BufferEnd and BufferLength from outside the interrupt handler.

Thank you for the help John and Andy

14 Nov 2010

iirc the current implementation of SerialBuffered will flush the buffer if you change the buffer length dynamically. Seesm you don't want to do this.

Lengthing the buffer shouldn't be a big deal but shortening it will require extra logic to ensure the current contains still fit inside it's deffinition.

Basically, you should disable the Uart interrupts, change the values with whatever method you want then re-enable the interrupts. The UART FIFOs are 16bytes long. While the IRQ are switched off temp then incoming chars will go into the rx fifo. So as long as you can reassign the buffers within 16char rx time, you'll be ok. Re-enabling interrupts will just kick in and flush the fifo to teh buffer. Just remember NOT to read the IIR register as this will clear any pending interrupts which may have occured during your reassignment.

14 Nov 2010

btw, I am not sure what your application is trying to do and why you need dynamic buffer sizes, but if you can make your buffer reassigments happen when the buffer is empty then it'll simplfy your logic a lot.

From http://mbed.org/users/AjK/libraries/SerialBuffered/lfp8fd/docs/sb__getc_8cpp_source.html you can see the folowing logic returns true when the buffer is empty:-

_rx_buffer_out[uart_number] == _rx_buffer_in[uart_number] && !_rx_buffer_full[uart_number]

However, notice that int c = xxx.getc(false); will return -1 if the buffer is empty, that's an easier way to detect the RX buffer empty.

 

 

14 Nov 2010

Wow, thanks, this seems to be exactly what I'm looking for.  I'll post the results after I have a chance to try an implementation.

but, to be clear, you are essentially saying:

disabling the interrupt will allow me to change the values of variables that are read in the handler, and re-enabling the interrupt afterwards will cause the handler to read these new values.  Right?

Then I can write:

void MS3DMGX2::Mode()
{
    DataLines.attach(NULL, Serial::RxIrq);
    BufferLegnth = 86;
    DataLines.attach(this, &MS3DMGX2::FillSerialBuffer, Serial::RxIrq);
}

and the handler will now circle at 86 instead of 31.

Thank you very much, if this works it will save a lot of pain.

21 Nov 2010 . Edited: 21 Nov 2010

Thomas, you may find this of use:-

MODSERIAL and the Cookbook page

27 Nov 2010

Problem resolved.

The volatile keyword actually did reserve the issue, my debugging skills were to blame (stay away from printf...)

Thanks a bunch.