2
\$\begingroup\$

I have the following situation: I was given a AVR32UC3 (pretty neat thing) and a measurement device. These two communicate via SPI, which works as expected (after fiddling with SPI params and so on).

The program structure is as follows:

  1. Init the device via SPI
  2. Start the measurement via SPI
  3. Wait for the measurement to be done (this is signaled via a GPIO --> Interrupt)
  4. Read the measurement data via SPI
  5. Go to 2

At this very moment, I can roughly guess how long the measurement will take (~3ms). I currently ignore the interrupt, do busy waiting instead of 3, then read the data.

Now for my question: I would like to get rid of that "busy" waiting. Of course, if another interrupt arrives (from some other peripheral) the code will execute that interrupt anyway but code in my main loop will not be processed during the busy waiting. How would I do this "clean" using the interrupt itself.

The idea would be:

  • Setup interrupt source + conditions
  • Start the measurement
  • Interrupt occurs
  • Pick up where I left

How does the source code (pseudo-code) for something like this look? How is this done PROPERLY?

\$\endgroup\$
  • \$\begingroup\$ See related: stackoverflow.com/questions/13583419/… \$\endgroup\$ – embedded.kyle Dec 7 '12 at 15:31
  • \$\begingroup\$ You did not link to the datasheet, but am I correct that your micro can trigger the interrupt on completion of an SPI transfer? \$\endgroup\$ – Kortuk Dec 7 '12 at 15:45
  • \$\begingroup\$ @embedded.kyle: Yes, this is somewhat similar to the response from Rev1.0 below. I added a comment, maybe you can look into it as well? \$\endgroup\$ – Tom L. Dec 7 '12 at 16:48
  • \$\begingroup\$ @Kortuk: Yes, it definitely is possible. There is also the possibility to program a DMA (it's called a peripheral DMA) to send out all characters via SPI without any interruption and to be notified afterwards. \$\endgroup\$ – Tom L. Dec 7 '12 at 16:49
  • \$\begingroup\$ @TomL. Ohhh, that sounds like a really fun feature. I have never had that option. \$\endgroup\$ – Kortuk Dec 7 '12 at 18:22
7
\$\begingroup\$

You could implement a small state machine like this:

// global, volatile (accessed from ISR) variable 
// (should also be 1-byte to prevent multibyte access issues on 8-bit system)
volatile CurrentState;

main() {
    Spi_Init();
    CurrentState = StartMeasurement;

    // your main loop
    while(true) {
        // other main processing here            

        // spi processing
        switch (CurrentState) {
           case StartMeasurement:
               Spi_StartMeasurement();
               CurrentState = WaitForResult;
               break;
           case WaitForResult:
               // nothing todo -> no blocking main -> this switch is just for clarification and can even be removed
               break;
           case ResultReady:
               Spi_GetResult();
               CurrentState = StartMeasurement;
               break;
           default:
               // CurrentState was set to invalid value -> error handling
               break;
        }
    }
}

ISR_ResultReady() {
    if (CurrentState == WaitForResult) {   // this should always be true but its cleaner to check it
        // signal main that result is ready
        CurrentState = ResultReady;
    }
}

I hope you get the idea. There are cleaner, more abstract ways to do this, but for small applications it also overcomplicates things.

\$\endgroup\$
  • \$\begingroup\$ ... the OP doesn't want to "spin wait" in his main loop (i.e. the WaitForResult state), but I suppose he can put the "do other work here" in that case of the switch ... \$\endgroup\$ – vicatcu Dec 7 '12 at 16:04
  • \$\begingroup\$ @Rev1.0: That's a pretty good idea, I really like it except for one thing: I cannot separate the driver from my main loop. I would like to separate them as much as possible so I could simply perform the measurement and then be informed (kind of an event) that the processing has taken place (i guess this would be some kind of callback function?). Any ideas on that? The reason is that I would like to reuse the components whenever possible and as soon as I need to write specific code in my main I lose some flexibility in porting. \$\endgroup\$ – Tom L. Dec 7 '12 at 16:43
  • 1
    \$\begingroup\$ @TomL. I'm not sure I understand you. Do you mean you want to have the switch statement from Rev1.0's example operate in the background? If so, why not move it to a Timer ISR? Though personally, I would rather have this kind of processing take place in the main loop as Rev1.0 has it and keep the interrupts short by just setting flags. \$\endgroup\$ – embedded.kyle Dec 7 '12 at 18:11
  • \$\begingroup\$ @vicatcu: The "do other work here" would just be outside of the switch case. Or what do you mean? I edited the answer to make it clearer. \$\endgroup\$ – Rev1.0 Dec 8 '12 at 13:41
  • \$\begingroup\$ @Tom: What you want is actually my preferred way of doing it. I would put the switch-case processing in a separate file. Say in a function MyDriver_Processing(), which is continuosly called in the main loop. You can add something like MyDriver_Init() that performs the initial setup and will be called once before the main loop. Another function MyDriver_RequestValue(*Callback) accepts a callback function as parameter. Use this to tell the driver to perform one readout (i.e. run the switch case processing once) and it will notify the main program when it is finished. \$\endgroup\$ – Rev1.0 Dec 8 '12 at 13:58
1
\$\begingroup\$

I really think that using an RTOS can benefit you in the long term. FreeRTOS is very good and has already been ported to the UC3.

It will make it easier for you to handle all of these things if you keep the interrupt short and then process the data in a task.

\$\endgroup\$
  • \$\begingroup\$ Actually that doesn't really answer my question, it just puts it on another level. How can I separate the driver from the actual project as much as possible is the question behind this issue. I've been working with FreeRTOS before but for my application it's not what I am looking for. \$\endgroup\$ – Tom L. Dec 8 '12 at 15:10

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.