Help with non-maskable interrupt
Posted: Fri Apr 27, 2012 8:00 am
Yes, me again. I've run into yet another snag getting one wire working. It involves the timing of interrupt handling. If you're willing to help, please bear with some explanation:
The project is a weather station, with a handful of devices using one wire, and some digital inputs I'm tying to IRQs.
The problem is that for one of the digital inputs, I need to measure the amount of time the signal is high, vs the amount it spends low, and these intervals can be a few microseconds in length, worst case. So when I get an edge, I have to measure the pin and collect the time, Right Now. As in, within a microsecond (us) or two. Having the interrupt delayed by other interrupts, or anything else, is going to mess this up.
For that reason, I'm not going to be able to use the usual approach for handling the one wire pin: lock out interrupts and do timing loops to decide when to fiddle the pin. One wire requires pin changes as quickly as 6 us and as slowly as 480 us. Locking out interrupts for 6us could affect the aforementioned digital input, and locking out interrupts for 480us could mess up all sorts of things. Timing loops are off the table.
No problem, thought I, I'll just use the DMA to generate interrupts at specific times, and run the one-wire stuff in the interrupt handler. So if I need to pull the pin low for 6 us, and then set it to hiz() for 9 us, and then read it and leave it alone for 60us, I'd just set interrupts to go off at those times, and do one operation in each interrupt. One-wire itself isn't a lot of work - when the interrupt goes off, I only have to set the pin low, or set the pin to hiz(), or read the pin. Easy.
As it turns out, it's not so easy. What I've been trying to do is set the pin in the interrupt handler, and then fiddle the value of the DMA .trr to set the timing interval for the next interrupt. What I'm finding is that interrupts don't get handled when scheduled - I can get called several microseconds late. I assume there's other interrupt stuff happening that's delaying me, or code somewhere that's locking out interrupts for microseconds at a time.
So what I need to do, I guess, is put all this stuff into unmaskable interrupts, so that Now means NOW, not "when you get around to it." All I'm going to do in that interrupt is set the timing of the next interrupt, and read or change a pin; I know about the severe coding limits that apply to interrupt (and especially NMI) code.
So... questions:
I've just come across PIT timers:
http://www.netburner.com/downloads/mod5 ... 70-PIT.pdf .
Am I better off using these timers instead of the DMA timer? (I'm using the DMA timer now only because I came across them first.)
Does anyone have sample code showing the use of PIT or DMA timers with NMI? I know there's assembler involved.
I'd also like anyone to comment on rewriting the DMA timer's .trr value (or the PIT countdown) from inside the timer's interrupt, because I'm not 100% certain this is working reliably with the DMA stuff (timing jitter aside.)
Guidance appreciated. This project is taking a lot longer than I'd hoped...
The project is a weather station, with a handful of devices using one wire, and some digital inputs I'm tying to IRQs.
The problem is that for one of the digital inputs, I need to measure the amount of time the signal is high, vs the amount it spends low, and these intervals can be a few microseconds in length, worst case. So when I get an edge, I have to measure the pin and collect the time, Right Now. As in, within a microsecond (us) or two. Having the interrupt delayed by other interrupts, or anything else, is going to mess this up.
For that reason, I'm not going to be able to use the usual approach for handling the one wire pin: lock out interrupts and do timing loops to decide when to fiddle the pin. One wire requires pin changes as quickly as 6 us and as slowly as 480 us. Locking out interrupts for 6us could affect the aforementioned digital input, and locking out interrupts for 480us could mess up all sorts of things. Timing loops are off the table.
No problem, thought I, I'll just use the DMA to generate interrupts at specific times, and run the one-wire stuff in the interrupt handler. So if I need to pull the pin low for 6 us, and then set it to hiz() for 9 us, and then read it and leave it alone for 60us, I'd just set interrupts to go off at those times, and do one operation in each interrupt. One-wire itself isn't a lot of work - when the interrupt goes off, I only have to set the pin low, or set the pin to hiz(), or read the pin. Easy.
As it turns out, it's not so easy. What I've been trying to do is set the pin in the interrupt handler, and then fiddle the value of the DMA .trr to set the timing interval for the next interrupt. What I'm finding is that interrupts don't get handled when scheduled - I can get called several microseconds late. I assume there's other interrupt stuff happening that's delaying me, or code somewhere that's locking out interrupts for microseconds at a time.
So what I need to do, I guess, is put all this stuff into unmaskable interrupts, so that Now means NOW, not "when you get around to it." All I'm going to do in that interrupt is set the timing of the next interrupt, and read or change a pin; I know about the severe coding limits that apply to interrupt (and especially NMI) code.
So... questions:
I've just come across PIT timers:
http://www.netburner.com/downloads/mod5 ... 70-PIT.pdf .
Am I better off using these timers instead of the DMA timer? (I'm using the DMA timer now only because I came across them first.)
Does anyone have sample code showing the use of PIT or DMA timers with NMI? I know there's assembler involved.
I'd also like anyone to comment on rewriting the DMA timer's .trr value (or the PIT countdown) from inside the timer's interrupt, because I'm not 100% certain this is working reliably with the DMA stuff (timing jitter aside.)
Guidance appreciated. This project is taking a lot longer than I'd hoped...