[En-Nut-Discussion] SPI in IRQ

Ulrich Prinz ulrich.prinz at googlemail.com
Thu Feb 16 15:25:58 CET 2012

Hi Klaus,

If I understand the datasheet of the ADC correctly it serves you with
/DRDY signal every time a sample is complete.
So if you write a GPIO IRQ handler that reads the SPI at this time,
you are done with the first part.

For reading SPI you need to throw out dummy bytes that just trigger
SCK so the ADC can write via MISO to your AT91.
To get more free CPU time, you should think about using DMA transfer for SPI.
So you set up a dummy region of RAM that hold a fixed pattern of
control words and dummy bytes.
Then you set up another region to store the read values into.
Then you setup and start DMA with completion interrupt set.

The DMA completion interrupt then shows your application that there is
a new value available.

If you need several values at a time, you can expand the regions of
dummy and reception values and setup the DMA to transfer this bigger
regions. The DMA Interrupt then only counts down the number of samples
per block and calls your application after counter has reached 0.

To be more flexible and gather some additional time:
You can set the AT91 DMA to work with separate pages. So if a DMA is
completed you can flip to a nother page where it continues to work
right away. So after you sampled one page, you have time to decode and
move the date from there, while the DMA continues to write into the
second page. After the second page is completed the DMA flips back to
the first page or even a third page.

If I remeber my last AT91 devel over a year ago, it should be possible
to implement you ADC routines with just DMA and a single DMA interrupt

In general:
You should keep a state variable in you code, that shows if your
sampling is active. If not active, you can use normal nut/os SPI
functions to setup / program your ADC. After activating your sampling,
the variable should state that no normal nut/os function should
trigger SPI.

You did not state the reason for this construction but in general I
would suppose to use the I2S interface of an AT91 or STM32 and use the
special features of this interface that automates constant time
sampling and delivery of data with very low software overhead
including multi buffer handling ( work in one while the other is
filled, then switch)

I2S is just a derivate of SPI...


Am 16. Februar 2012 12:04 schrieb Klaus Kloos <klaus.kloos at gmx.de>:
> Hello Harald
> Thanks for your fast answer.
>> On 16.02.2012 08:22, Klaus Kloos wrote:
>>> So I tried to use the NutOS functions. (*spiBus1At91.bus_alloc) and (*spiBus1At91.bus_release) are called outside the IRQ,  (*spiBus1At91.bus_transfer) is called at IRQ time.
>>> The transfer of the 3 bytes last ca. 12us using 10MHz SPI. It is working, but unreliable. Sometimes my device freezes.
>> Probably. In general, it is not allowed to call API functions in
>> interrupt context, except NutEventPostFromIrq(). As a special exception
>> it is possible to use stdio functions on a polling device, usually devDebug.
> ok, i was aware that this is not the right way. It was only a test and it gave me a 'near to working' result :-) My hope was to get bus-transfer() IRQ save somehow......
> What is necessary to make a function IRQ save?
>>> What are my possibilities? Is there a recommended way to solve such time-critical SPI-requests?
>>> Can I get the Nut/OS functions IRQ save somehow?
>> Nut/OS allows to use native interrupt handlers. For the AT91 you can use
>> static void MySpiIrqHandler(void) __attribute__ ((naked));
>> void MySpiIrqHandler(void)
>> {
>>    IRQ_ENTRY();
>> ...your code here...
>>    IRQ_EXIT();
>> }
>> In your main code you need to write the address of the handler into the
>> related AIC service routine register.
>> outr(AIC_SVR(SPI0_ID), (unsigned int) MySpiIrqHandler);
> Interesting, this might give me back the 1us which I loose between an IRQ and the calling of my IRQ-routine using the NutOS way.
> But that's not the main problem. How do I get the SPI-Value in my 30us slot? I see these ways:
> Is there a way to generate a fast context-switch from IRQ to the thread which is allowed to call bus_transfer()? Can this be done reliable in ca. 10us?
> Then I can use the NutOS SPI routines.
> An other way is the read the data in the IRQ. But then I have to code a function like bus_transfer() by myself (?). Not a nice chance...
> A simple solution would be working without an IRQ and do everything in a thread without allowing a context-switch. So the sam7 will freeze for up to 50ms (i have to read 1000 values).
>> And...not to forget...it is always a good idea to check the datasheet. ;-)
> Not that I have completely memorized it, but Ive read it several times. The problem seems clear. Im too slow.
> There is also a 128kSPS variant of the chip available. Then the time slot is less than 8us. Im happy that Im not forced to solve this problem....
> Greetings Klaus
> _______________________________________________
> http://lists.egnite.de/mailman/listinfo/en-nut-discussion

More information about the En-Nut-Discussion mailing list