How to store data from an interrupt in C

  • Thread starter haxor489
  • Start date
  • Tags
    Data
In summary, the conversation discusses a dilemma in sharing a queue or array between an interrupt and main program in C programming. The main issue is accessing the latest empty position in the array, with the interrupt being declared and defined in another .c file. One approach considered is reserving two bytes at the beginning of the allocated memory to act as an iterator and tail pointer, but the conversation also explores potential race conditions and suggests using global variables or implementing a pre-emptive multi-tasking executive.
  • #1
haxor489
25
0
It's been a while since I've done some C programming and I can't remember how I should go about implementing this. I have a chunk of memory allocated for storing an array of data. The allocation takes place while the main program is running. The data is being stored to the already allocated memory space in an interrupt (this code is running on a microcontroller) . My issues is accessing the memory location of the latest empty position in the array, to add the next sequence of data. The interrupt is declared and defined in another .c file.

My , probably overly complicated, idea was to reserve two bytes at the beginning of the allocated memory space; one to act as an iterator for the array and the other as a tail pointer. I'd use the tail pointer in the interrupt to place the received data into the array and increment the pointer for the next interrupt.

I feel like there's probably an easier way to go about this. Thoughts or suggestions?
 
Technology news on Phys.org
  • #2
haxor489 said:
It's been a while since I've done some C programming and I can't remember how I should go about implementing this. I have a chunk of memory allocated for storing an array of data. The allocation takes place while the main program is running. The data is being stored to the already allocated memory space in an interrupt (this code is running on a microcontroller) . My issues is accessing the memory location of the latest empty position in the array, to add the next sequence of data. The interrupt is declared and defined in another .c file.

My , probably overly complicated, idea was to reserve two bytes at the beginning of the allocated memory space; one to act as an iterator for the array and the other as a tail pointer. I'd use the tail pointer in the interrupt to place the received data into the array and increment the pointer for the next interrupt.

I feel like there's probably an easier way to go about this. Thoughts or suggestions?

Hi haxor489! :smile:

I think you need to provide a little more information.
As it is, you really need to allocate your data before your microcontroller starts doing anything.

Anyway, as a rule, if you pick up data asynchronously, you will need to transfer it to a queue before processing it.
 
  • #3
Hi I Like Serena! :)

I'm not really sure what else to add to that but I'll give it a shot.

"Anyway, as a rule, if you pick up data asynchronously, you will need to transfer it to a queue before processing it."

That's exactly my intention but the queue or array in which to share between my interrupt and main program is my dilemma.

I'm collecting data asynchronously over the SCI module. Every time the SCIRXBUF fills up it sets a flag which triggers an interrupt. This interrupt is located/defined in the PIE vector table. I'm coding the interrupt and since it's not part of my main program I'm not sure how to share a static variable between the two so that they can both reference the same location and pointer. Is there any way to do this in C without have to have fixed values to memory addresses for such a thing?

I hope I'm explaining this clearly. If I'm not making sense then I must be thinking of implementing this in a way that isn't conventional. Please explain in general how you would share pointers to the queue or array between your interrupt and main program.
 
  • #4
Hey haxor489.

Does the OS/Interrupt code have a way of re-allocating an array in the same way that you can re-allocate an array in C through realloc()?
 
  • #5
You will need some global variables in order to do this. For the queueing of data, you could use a linked list or a circular buffer. Since you mentioned an iterator, sounds like you're considering using a circular buffer. If the interrrupt routine is queing data to the main level code, the interrupt routine advances the head pointer / index each time it queues an entry, and the main level code advances the tail pointer / index each time it extracts an entry.

Another consideration is if you're considering implementing a simple pre-emptive multi-tasking executive, where a task can "pend" on an empty queue, then switched to ready and or runnable when the interrupt (or another task) appends an entry onto the empty queue.
 
  • #6
I wonder if your plan might result in a race condition on the pointers into the array, if both a program and an interrupt service routine are both using a couple of address that point into the array. Is only one WRITING the pointers, and the other READING the pointers? Even then, is there a chance that while one is in the process of writing a pointer, the other might come along and try to read the pointer while it's in some intermediate state not completely changed yet?
 
  • #7
harborsparrow said:
I wonder if your plan might result in a race condition on the pointers into the array, if both a program and an interrupt service routine are both using a couple of address that point into the array. Is only one WRITING the pointers, and the other READING the pointers? Even then, is there a chance that while one is in the process of writing a pointer, the other might come along and try to read the pointer while it's in some intermediate state not completely changed yet?
Only the interrupt routine updates the head pointer and only the man level code updates the tail pointer (except for initialization). On most processors, reading or writing an index or a pointer is done in a single atomic (non-breakable) operation, since these are of native size and should be on native boundaries. There could be an isssue if a pointer was forced to be on a non-native boundary, such as a packed structure.

A race condition can occur if something special needs to be done when a queue goes from empty to non-empty. In the OP's case, the interrtupt routine is the one adding data to the queue, so it's only an issue on a multi-threaded or multi-tasking operating system where the main level code can "pend" on a queue to wait for the queue to go non-empty. There would be operating system functions that handle this (including disabling interrupts as needed).

The other race situation occurs when it's the main program adding data to a queue, starting an I/O, and then allowing the interrupt routine to retrieve data from a queue and continue doing I/O's as long as the queue is non-empty. To handle this case, the enqueue function needs to disable interrupts as needed and return an indicator that the queue was previously empty, such as a status, previous count (== 0), current count (==1). If the queue was previously empty, then the main program starts the initial I/O.

I wrote an example multi-threaded windows dos console program that copies a file, using the current thread to read, and a spawned thread to write. In this example, I have functions that handle a fifo queue in the form of a link list, that utilizes mutexes and semaphores. The dequeue function (GetNextElement()) uses windows WaitForMultipleObjects(), which eliminates any issue related to thread priority.

http://rcgldr.net/misc/mtcopy.zip
 
  • #8
rcgldr said:
On most processors, reading or writing an index or a pointer is done in a single atomic (non-breakable) operation, since these are of native size and should be on native boundaries. There could be an isssue if a pointer was forced to be on a non-native boundary, such as a packed structure.
http://rcgldr.net/misc/mtcopy.zip

I'm not confident of the above statement. There are cases where a read or write involves multiple bus cycles (if, say, the pointer is wider than one byte), and a thread switch can occur in the middle of the operation; this can produce incorrect values. There may also be cache coherency issues, where a write from one thread updates its processor's cache, but does not update global memory; a read from a different thread reads global memory, and doesn't see the updated value in the other processor's cache. Also, a compiler can shuffle the order of reads and writes under the assumption that the values are not accessed from another thread, resulting in chaos.

I believe there is an atomic data type in C++ that will guarantee against these, but I wasn't sure if those are being used in this case, or not.

Could the poster please clarify what hardware platform and software tools are being used, so that we need not debate more than necessary? Maybe I'm worrying unnecessarily, but it never hurts to think through these scenarios. I've seen myself and others bitten more than once by overconfidence, and the problems are very difficult to diagnose after the fact, as they will manifest intermittently and maybe seldom.
 
  • #9
rcgldr said:
On most processors, reading or writing an index or a pointer is done in a single atomic (non-breakable) operation, since these are of native size and should be on native boundaries.

harborsparrow said:
I'm not confident of the above statement. There are cases where a read or write involves multiple bus cycles (if, say, the pointer is wider than one byte).
Most processors will treat any read or write as an atomic operation, even if it involves multiple memory or cache line operations (mis-aligned integer or pointer). An interrupt is not going present a problem here. The ram copy won't matter unless it's a multi-processor environment or a bus mastering controller is modifying data being used by a program for variables (not a good idea).

Consider the issue of trying to save a thread's context if a context switch to a higher priority thread or interrupt could occur in the middle of a stack read / write operation. Interrupt handling, multi-threading, and multi-processing all rely on the fact that any current instruction will complete before switching context. Repeated instructions, such as rep movsb, will be paused mid-loop, but not mid-instruction.

harborsparrow said:
I believe there is an atomic data type in C++ that will guarantee against these, but I wasn't sure if those are being used in this case, or not.
Global variables shared between threads and/or interrupts need to be declared as volatile, to prevent the compiler from optimizing the operations to use registers instead of memory image, but I'm not aware of an atomic type for C++. Atomic operations usually involve multiple instructions and some method used like disabling interrupts and/or context switching to make sure such operations are atomic. I'm not aware of any built-in atomic operators for C++, unless it's in some processor specific library.
 
Last edited:

1. What is an interrupt in C?

An interrupt in C is a signal that temporarily stops the execution of a program in order to handle a specific event or request. Interrupts are commonly used in embedded systems and real-time applications to respond to external events.

2. How can I store data from an interrupt in C?

To store data from an interrupt in C, you can use global variables or pointers. Global variables can be accessed by the interrupt handler and the main program, while pointers can be used to pass data between the interrupt handler and the main program.

3. Is it necessary to disable interrupts when storing data?

Yes, it is necessary to disable interrupts when storing data in order to avoid data corruption. This is because interrupts can occur at any time and interrupt the execution of the program, potentially causing conflicts when trying to access or modify data.

4. How do I enable interrupts after storing data?

To enable interrupts after storing data, you can use the appropriate function or instruction for your specific microcontroller or system. This will vary depending on the platform you are using, so be sure to consult the documentation or resources for your specific setup.

5. Are there any best practices for storing data from an interrupt in C?

Yes, there are a few best practices you can follow when storing data from an interrupt in C. These include minimizing the use of global variables, using proper synchronization techniques, and avoiding excessive or unnecessary data transfers between the interrupt handler and the main program.

Similar threads

  • Programming and Computer Science
Replies
17
Views
1K
  • Programming and Computer Science
Replies
6
Views
3K
  • Programming and Computer Science
4
Replies
118
Views
6K
  • Programming and Computer Science
Replies
11
Views
995
  • Programming and Computer Science
Replies
4
Views
3K
  • Programming and Computer Science
Replies
12
Views
2K
  • Programming and Computer Science
Replies
5
Views
1K
  • Programming and Computer Science
Replies
29
Views
11K
  • Programming and Computer Science
Replies
1
Views
944
  • Programming and Computer Science
Replies
6
Views
2K
Back
Top