How to store data from an interrupt in C

  • Thread starter Thread starter haxor489
  • Start date Start date
  • Tags Tags
    Data
Click For Summary
SUMMARY

This discussion focuses on the implementation of data storage from an interrupt in C, specifically for microcontroller applications. The user seeks advice on managing memory allocation and accessing the latest empty position in an array during interrupts. Key suggestions include using a circular buffer for efficient data queuing and ensuring that shared variables between the main program and interrupt are handled correctly to avoid race conditions. The importance of atomic operations and proper variable declaration, such as using 'volatile', is emphasized to maintain data integrity.

PREREQUISITES
  • C programming knowledge, particularly with interrupts
  • Understanding of memory allocation in embedded systems
  • Familiarity with circular buffers and data queuing mechanisms
  • Knowledge of atomic operations and the 'volatile' keyword in C
NEXT STEPS
  • Research how to implement circular buffers in C for interrupt-driven data handling
  • Learn about using 'volatile' for shared variables in embedded C programming
  • Explore atomic operations in C and their importance in multi-threaded environments
  • Investigate queue management techniques for asynchronous data processing in microcontrollers
USEFUL FOR

Embedded systems developers, C programmers working with interrupts, and anyone involved in real-time data processing on microcontrollers will benefit from this discussion.

haxor489
Messages
25
Reaction score
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
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.
 
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.
 
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()?
 
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.
 
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?
 
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
 
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.
 
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:

Similar threads

  • · Replies 17 ·
Replies
17
Views
3K
Replies
6
Views
4K
  • · Replies 118 ·
4
Replies
118
Views
10K
  • · Replies 12 ·
Replies
12
Views
3K
  • · Replies 4 ·
Replies
4
Views
3K
  • · Replies 12 ·
Replies
12
Views
2K
  • · Replies 5 ·
Replies
5
Views
2K
  • · Replies 29 ·
Replies
29
Views
12K
  • · Replies 16 ·
Replies
16
Views
5K
  • · Replies 10 ·
Replies
10
Views
7K