- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
Hi PSoC Community,
I'm working with the firmware of a product we will be launching soon. We are using PSoC 4 (CY8C4245AZI-473) with internal clock set to 48 MHz and VCC to 5V. I have enabled the ADC as hardware triggered so I can start a conversion manually using ADC_StartConvert() function. This is the code for ADC initializing:
ADC_Start(); // Starts ADC
ADC_IRQ_StartEx(ADCINT_SERVICE); // Function assigned to ADC interrupt
ADC_EnableInjection(); // Enable Inj channel for microcontroller temperature reading
ADC_SetLimitMask(0); // Avoids limit values interrupt
ADC_SetSatMask(0); // Avoids saturation values interrupt
ADC_StartConvert(); // Start ADC conversions
This is the interrupt code:
CY_ISR(ADCINT_SERVICE) // Sets flag indicating end of conversion
{
uint32 intr_status;
intr_status=ADC_SAR_INTR_REG; // Reads interrupt flags from ADC
if(intr_status&1) // Masks all interrupt flags except end of conversion
FlagAdc=1; // Indicates conversion ready
ADC_SAR_INTR_REG=intr_status; // Clear interrupt flags
}
This code goes inside main():
if(FlagAdc) // Ha terminado una conversion del ADC?
{
FlagAdc=0;
AdcWDT=0;
adc_read(Conversions); // Procesa las medidas capturadas por el ADC
}
And this is the function to read the ADC conversions:
void adc_read(uint16 *Ptr)
{
int16 TempAdc; // Used to read ADC values
uint8 AdcChannel; //ADC channel
if(Flag.MicroTempRead) // Time to read microcontroller temperature?
{
Flag.MicroTempRead=0;
Ptr[TM]=(uint16)(ADC_GetResult16(TM)/8); // Reads microcontroller temperature
}
for(AdcChannel=0;AdcChannel<TM;AdcChannel++)
{
TempAdc=ADC_GetResult16(AdcChannel)/8; // Gets average of each conversion
if(TempAdc<0) // Negative values?
TempAdc=0; // Convert them to zero
Ptr[AdcChannel]=(uint16)TempAdc;
}
if(Flag.MicroTempAsk) // Ready for microcontroller temperature?
{
Flag.MicroTempAsk=0;
ADC_EnableInjection(); // Enables injection channel for microcontroller temperature
Flag.MicroTempRead=1; // Indicates microcontroller temperature reading next time
}
ADC_StartConvert(); // Starts next conversions
}
The problem is that sometimes the conversions stops updating, like if the ADC end of conversion interrupt was suddenly disabled. This happens randomly, the product could run nice for several hours and then the problem appears. We have tested about ten units, but two of them shows this issue. All of the other interrupts and functions (PWM, timer, Watchdog timer, I2C, UART, etc.) keeps running smoothly.
Question: What could cause the ADC interrupt EOC to stop without user consent?
Thanks for your help.
Solved! Go to Solution.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
I read one note in TRM "If the injection channel is tailgating a scan (Injection channel tailgating is enabled by default), the EOS_INTR is raised in parallel to starting the injection channel conversion. The injection channel is not considered part of the scan. " This means EOS interrupt occur after completing a scan of all the enabled channels except INJ channel, INJ channel trigger INJ_EOC_INTR interrupt when Injection channel end of conversion. So when FlagAdc=1, the INJ channel may still being in conversion.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
Would be of some interest where the program stops. Probably FlagAdc (which needs to be declared volatile) is not set. This can happen as far as I can see due to a race condition in the main loop and the interrupt handler.
Bob
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
Hi Bob,
Thanks for replying. This is the declaration of FlagAdc:
volatile uint8 FlagAdc=0; // End of conversion of ADC
The program behaves as if after the last conversion, the interrupt never happens again. The strange thing is that this problem shows very random.
I was wondering about maybe some stack overflow that could overwrite the value of FlagAdc. This is the memory usage:
Flash used: 30474 of 32768 bytes (93.0 %). Bootloader: 5376 bytes. Application: 24970 bytes. Metadata: 128 bytes.
SRAM used: 3028 of 4096 bytes (73.9 %). Stack: 1024 bytes. Heap: 0 bytes.
At first I used to have many variables declared inside main() but the compiler did not tell me the right amount of RAM used, so I put these as globlal variables and the usage changed from near 75% to 93%. If other functions use static variables to keep values, could this cause a stack overflow and corrupt variable FlagAdc?
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
Static variables are not allocated on the stack. so no overflow by those.
Stack overflow: You may put an address breakpoint near top of stack
you can check FlagAdc for being out of range (> 1)
Bob
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
Hi Bob,
I have checked the stack usage as you said, it seems the problem is not related to stack overflow. I'll try debugging inside the ADC interrupt to see if I can find a clue.
Thanks.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
Did you connect any hardware signal to SOC terminal of SARADC? If it is possible the soc signal is asserted while SARADC is busy(), then the data in the result register interprets it as incorrect and the Component will be stalled. So when your SARADC is stopped, is the last conversion result correct? You may try to check the busy state of ADC before trigger the next scan conversion and see what happened.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
Hi xzng,
The SOC terminal of the ADC is tied to logic '0'. The last result conversion is correct. I'm using 6 channels and the injection channel. I start the conversions by software, using ADC_StartConvert(). I masked any other interrupt, except EOC. This is the sequence I use:
1. Start conversions
2. Work with functions in main
3. (Inside ISR) ADC interrupt? If it was EOC, activate FlagADC
4. (Inside main) FlagADC active? Clear FlagADC, reads conversions and start conversions again
5. Return to step 2
It looks like there is a moment where FlagADC is not set anymore. Unfortunately this happens very random and seldom. I wonder, as you say, if maybe I'm triggering the start of conversion during a current conversion, the component would stop. If so, how would be the procedure to restart the ADC normally?
Thanks.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
I read one note in TRM "If the injection channel is tailgating a scan (Injection channel tailgating is enabled by default), the EOS_INTR is raised in parallel to starting the injection channel conversion. The injection channel is not considered part of the scan. " This means EOS interrupt occur after completing a scan of all the enabled channels except INJ channel, INJ channel trigger INJ_EOC_INTR interrupt when Injection channel end of conversion. So when FlagAdc=1, the INJ channel may still being in conversion.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
Hi xzng,
You are right, I was masking the injection EOC interrupt thinking that the EOS would do the same trick, but not. I will modify the software to detect both interrupts separately (EOS and INJ EOC), read each conversion also separately and trigger the ADC with a timer giving enough time for total ADC conversion so the trigger does not collide with any conversion in progress.
I'll do some tests and let you know.
Thanks.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
Hi everybody,
Several hours and no glitch, looks like the problem was solved.
Thanks!