I need help if figuring out how to use the analog interrupt comparator on the Due.
I want to find the pulse width on an encoder while its changing speed during which my job is reading other analog pins and also reading acceleration values through I2C communication.
I need to find both falling and rising edge. Currently I am using a solid state relay to activate a digital interrupt but I believe the relay has some uncertainty in timing (less them ½ ms) and I would like to get a better accuracy (less then 10micro Sec).
Please help.
The TC embeds a quadrature decoder (QDEC) connected in front of the timers and driven by TIOA0, TIOB0 and TIOB1 inputs. Is that what you need ? There is no link with an ADC conversion !!
No, I want to measure very precisely the deceleration of the motor only when it is very close to 0 so I am relaying only on a few pulses.
I need to measure there length of each one very precisely and for that I need to use the internal analog interrupt not encoder.
OK , here is a snippet to trigger an interrupt on an high threshold (not tested). For a different thresthold, refer to ADC_CWR register in sam3x datasheet:
void setup() {
adc_setup();
}
void loop() {
}
void adc_setup ()
{
PMC->PMC_PCER1 |= PMC_PCER1_PID37; // ADC power ON
ADC->ADC_CR = ADC_CR_SWRST; // Reset ADC
ADC->ADC_MR |= ADC_MR_FREERUN_ON // ADC in free running mode
| ADC_MR_LOWRES_BITS_12 // 12 bits resolution
| ADC_MR_PRESCAL(1)
| ADC_MR_SETTLING_AST3
| ADC_MR_TRACKTIM(10)
| ADC_MR_TRANSFER(2);
ADC->ADC_EMR = ADC_EMR_CMPMODE_HIGH // Compare with high threshold
| ADC_EMR_CMPSEL(7) // Compare channel 7 = A0
| ADC_EMR_CMPFILTER(10); // Number of consecutive compare events necessary
// to raise the flag = CMPFILTER+1
ADC->ADC_CWR = ADC_CWR_HIGHTHRES(4000); // Compare with a high threshold of conversion
ADC->ADC_CHER = ADC_CHER_CH7; // Enable Channel 7 = A0
ADC->ADC_IER = ADC_IER_COMPE; // Interrupt on Compare match enable
NVIC_EnableIRQ(ADC_IRQn); // Enable ADC interrupt
}
void ADC_Handler() {
ADC->ADC_ISR; // Read and clear status register
// Do some stuff (keep this as short as possible, e.g. set a flag to be proceed in loop())
}
I have tried to use your code but without successes.
I have places in the ADC_Handler() a Serial.print command to see when I get an interrupt (with a Serial.begin in the Setup) and connected A0 to 3.3V trough a potentiometer and played with that but without any success.
I have no idea what is wrong. Can you please help?
If A0 receives a constant 3.3V, then you just have 1 event detected but in code:
ADC_EMR_CMPFILTER(10); needs 10+1 events to raise the flag (and an interrupt).
If you want to trigger an interrupt whenever the input signal is OUT of the comparison window, set parameters as :
void setup() {
adc_setup();
}
void loop() {
}
void adc_setup ()
{
PMC->PMC_PCER1 |= PMC_PCER1_PID37; // ADC power ON
ADC->ADC_CR = ADC_CR_SWRST; // Reset ADC
ADC->ADC_MR |= ADC_MR_FREERUN_ON // ADC in free running mode
| ADC_MR_LOWRES_BITS_12 // 12 bits resolution
| ADC_MR_PRESCAL(1)
| ADC_MR_SETTLING_AST3
| ADC_MR_TRACKTIM(10)
| ADC_MR_TRANSFER(2);
ADC->ADC_EMR = ADC_EMR_CMPMODE_OUT // Generates an event when the converted data
//is out of the comparison window.
| ADC_EMR_CMPSEL(7) // Compare channel 7 = A0
| ADC_EMR_CMPFILTER(0); // Number of consecutive compare events necessary
// to raise the flag = CMPFILTER+1
ADC->ADC_CWR = ADC_CWR_LOWTHRES(1000) | ADC_CWR_HIGHTHRES(4000); // Compare with a high and low thresholds of conversion
ADC->ADC_CHER = ADC_CHER_CH7; // Enable Channel 7 = A0
ADC->ADC_IER = ADC_IER_COMPE; // Interrupt on Compare match enable
NVIC_EnableIRQ(ADC_IRQn); // Enable ADC interrupt
}
void ADC_Handler() {
ADC->ADC_ISR; // Read and clear status register
// Do some stuff (keep this as short as possible, e.g. set a flag to be proceed in loop())
}
No, I want to detect both the falling edge and the rising edge of the squire wave to get the exact up and down time of the wave . in or out of the window will only give me one of them.
To calculate the width of your pulses is straightforward, you need a Timer Counter capture. Capture the rising edge, then the falling edge of each pulse, then calculate the time during which your pulse is up and that's it !
I told you it what not an ADC problem since my reply #1.
There is a recent thread on the DUE forum about Timer Capture.
Thanks I am using micros() to measure the pulse duration but I needed the interrupts to start and end (end is also the start of the next rising / falling edge time measurement) the time measurement and not as part of my loop().
The measured wave values are only between 0.9V - 1.6V so I can't use digital interrupts without hardware gain which I prefer not to do.
So I am left with ADC interrupts. I need to measure the pulse up time and the pulse low time with very high accuracy.
Time between pulse polarity change is between 40ms - 10ms (high & low duration changes every pulse) and every change is important so I much catch all of them at very high precision.
So I thought to detect the rising edge with your top example then switch to ADC_EMR_CMPMODE_LOW in the handler or in the loop() until the falling edge and switch back to HIGH ......
The Digital input did not react well to the voltage I get from the input wave and it did not make it activate the interrupt but I was still able to read it with the analog input.
Maybe I still have to use ADC interrupt after all.
can you explain me how to configure the ADC interrupt to trigger an event both on the falling edge and the rising edge?