CCS C Software and Maintenance Offers
FAQFAQ   FAQForum Help   FAQOfficial CCS Support   SearchSearch  RegisterRegister 

ProfileProfile   Log in to check your private messagesLog in to check your private messages   Log inLog in 

CCS does not monitor this forum on a regular basis.

Please do not post bug reports on this forum. Send them to support@ccsinfo.com

ADC, floating point and interrupts

 
Post new topic   Reply to topic    CCS Forum Index -> General CCS C Discussion
View previous topic :: View next topic  
Author Message
xieliwei



Joined: 13 Nov 2011
Posts: 5

View user's profile Send private message

ADC, floating point and interrupts
PostPosted: Sun Nov 13, 2011 1:36 am     Reply with quote

I'm facing a problem trying to get a piece of code working. After some testing, it appears that my floating point operations and printing gets interrupted by the ADC interrupt.

Here's a condensed version of the code running on a DSPIC30F4013 at 120/4=30MHz using FRC_PLL16
Code:

#define NUMADC 12

volatile unsigned int16 adcval[NUMADC] = {0};
volatile int curradc = 0;

#int_ADC1
void  ADC1_isr(void)
{
   int oldadc = curradc;
   unsigned int16 tempadc;
   
   tempadc = read_adc(ADC_READ_ONLY);
   adcval[sno[curradc]] = tempadc;
   curradc = (curradc+1)%NUMADC;
   set_adc_channel(curradc);

   read_adc(ADC_START_ONLY);
   return;
}

void main(){
   float meep=0.351245242;

   setup_adc_ports(sAN0 | sAN1 | sAN2 | sAN3 | sAN4 | sAN5 | sAN6 | sAN7 | sAN8 | sAN9 | sAN10 | sAN11);
   setup_adc(ADC_CLOCK_DIV_64 | ADC_TAD_MUL_31);

   enable_interrupts(INT_ADC1);
   enable_interrupts(INTR_GLOBAL);

   fprintf(debug,"Moop!\r\n");
   fprintf(debug,"%5f\r\n", meep);

   set_adc_channel(curradc);
   read_adc(ADC_START_ONLY);
   
   fprintf(debug,"Meep!\r\n");
   fprintf(debug,"%5f\r\n", meep);
}


Output
Code:
Moop!
0.351245
MeeMeeMeeMeeMeeMee


I understand that that's how interrupts work, but I require the ADC values to be as fresh as possible while my code in the main body processes it. The ADC acquire rate is already as slow as the compiler allows me to set it. My current solution is to make the read_adc(ADC_START_ONLY); call in a timer set to go off at a slower rate, but is there a better solution?

Also, why does the print statement end up restarting after servicing the interrupt? The compiler version is 4.124.
FvM



Joined: 27 Aug 2008
Posts: 2337
Location: Germany

View user's profile Send private message

PostPosted: Sun Nov 13, 2011 3:34 am     Reply with quote

Please provide the complete code. There are missing variable definitions, so we can't see if your code is possibly writing out of bounds, which could be simple explanation for unexpected behaviour.

The shown output seems to refer to a different code with different order of print statements.
Ttelmah



Joined: 11 Mar 2010
Posts: 19432

View user's profile Send private message

PostPosted: Sun Nov 13, 2011 3:44 am     Reply with quote

Some comments:
Avoid, is using the '%' operator inside the interrupt. This itself uses the int16 division operator (using int16 value, which is the default on the DSPIC), unless used with binary values (8, 16 etc.). This then implies that interrupts will be disabled in all int16 divisions in the main code (including those inside the printf etc.)....
CCS, in some of their examples, use the %, but then carefully use binary values, and don't point out that other values may introduce problems...
So:

1) Change the declaration of curradc to int8. No point in using int16 for this.
2) Get rid of the '%' operator. Use:
Code:

    if (++curradc == NUMADC) curradc=0;

Which gives the same effect.

Then you don't show your fuses, so we can't tell if it may be restarting for a watchdog. However I'd suspect something tied up with re-awakening from sleep. You should not have the code 'dropping off the end' as it does. You need to remember that there is no 'operating system', running outside the code, for the code to drop back to. CCS fills the memory after the code (by default), with sleep instructions, and if you look at all examples, the code loops, so it does not drop off the end..... Your printout, won't have actually finished when the fprintf returns (hardware buffer), so the processor goes to sleep with characters still to send (hence the Mee). The behaviour then seems to be implying that one interrupt at least is still working (the ADC should have stopped).

Then, you don't show the declaration of sno[], so your saved adc value could be going into odd parts of memory. Do you really need this?. Accessing an array, is _slow_. Accessing an array indexed by another array, is even slower... Doing this inside an interrupt, is taking a lot of time.

You need to check how the ADC is setup. You _need_ to have SSRC<2:0> set to 111, for auto conversion. Otherwise, when you trigger the conversion, acquisition will automatically end. Don't assume the compiler has automatically done this....

Realistically you should be using a timer to control this. Have the timer trigger the start of the ADC conversion, and then interrupt when the conversion completes. Selecting a conversion clock that is significantly below the optimum value, degrades accuracy on the ADC.

Best Wishes
FvM



Joined: 27 Aug 2008
Posts: 2337
Location: Germany

View user's profile Send private message

PostPosted: Sun Nov 13, 2011 7:01 am     Reply with quote

Quote:
Avoid, is using the '%' operator inside the interrupt. This itself uses the int16 division operator (using int16 value, which is the default on the DSPIC), unless used with binary values (8, 16 etc.). This then implies that interrupts will be disabled in all int16 divisions in the main code (including those inside the printf etc.)....

The assumption doesn't apply for PIC24/dsPIC, that uses a dedicated DIV instruction without involving reentrance problems.
Ttelmah



Joined: 11 Mar 2010
Posts: 19432

View user's profile Send private message

PostPosted: Sun Nov 13, 2011 9:47 am     Reply with quote

It seems to still apply, it just doesn't take so long. The setup code round the DIV, is still common, I've checked, and interrupts are disabled if you use %. The loss of time on the PIC24, is tiny compared to the PIC16/18, but it is still worth avoiding it....

Best Wishes
xieliwei



Joined: 13 Nov 2011
Posts: 5

View user's profile Send private message

PostPosted: Sun Nov 13, 2011 10:27 am     Reply with quote

Thank you for your replies!

Sorry that I didn't provide the full code earlier, was in a hurry to attend to something.

Here is the full skeletal code (with suggested changes) that replicates the problem:

main.h
Code:
#include <30F4013.h>
#device ADC=12

#FUSES NOWDT                    //No Watch Dog Timer
#FUSES FRC_PLL16                //Internal Fast RC Oscillator
#FUSES PUT64                    //Power On Reset Timer value 64ms
#FUSES BORV27                   //Brownout reset at 2.7V
#FUSES BROWNOUT                 //Reset when brownout detected
#FUSES MCLR                     //Master Clear pin enabled
#FUSES NOPROTECT                //Code not protected from reading
#FUSES NOWRT                    //Program memory not write protected
#FUSES NODEBUG                  //No Debug mode for ICD

#use delay(clock=120000000)

#use rs232(UART2,baud=115200,parity=N,bits=8,errors,stream=debug)


main.c
Code:
#include <main.h>

#define NUMADC 12

#byte ADCON1=0x02A2
#bit SSRC0=ADCON1.0
#bit SSRC1=ADCON1.1
#bit SSRC2=ADCON1.2

volatile unsigned int16 adcval[NUMADC] = {0};
volatile int8 curradc = 0;

int8 sno[NUMADC] = {3, 4, 5, 6, 7, 8, 9, 10, 11, 2, 1, 0};

#int_TIMER1
void  TIMER1_isr(void){
   read_adc(ADC_START_ONLY);
   return;
}

#int_ADC1
void  ADC1_isr(void)
{
   int oldadc = curradc;
   unsigned int16 tempadc;
   
   tempadc = read_adc(ADC_READ_ONLY);
   adcval[sno[curradc]] = tempadc;
   if (++curradc == NUMADC) curradc=0;
   set_adc_channel(curradc);

   //read_adc(ADC_START_ONLY);

   return;
}

void main(){
   float meep=0.351245242;

   setup_timer1(TMR_INTERNAL|TMR_DIV_BY_256, 0x1D4);

   setup_adc_ports(sAN0 | sAN1 | sAN2 | sAN3 | sAN4 | sAN5 | sAN6 | sAN7 | sAN8 | sAN9 | sAN10 | sAN11);
   setup_adc(ADC_CLOCK_DIV_64 | ADC_TAD_MUL_31);

   SSRC0=SSRC1=SSRC2=1;
   
   enable_interrupts(INT_TIMER1);
   enable_interrupts(INT_ADC1);
   enable_interrupts(INTR_GLOBAL);

   fprintf(debug,"Moop!\r\n");
   fprintf(debug,"%5f\r\n", meep);

   set_adc_channel(curradc);
   //read_adc(ADC_START_ONLY);
   
   fprintf(debug,"Meep!\r\n");
   fprintf(debug,"%5f\r\n", meep);

   while(1){
      fprintf(debug,"OK\r\n");
      delay_ms(10000);
   }
}


Output
Code:
Moop!
0.351245
Meep!
0.351245
OK
Moop!
0.351245
Meep!
0.351245
OK
Moop!
0.351245
Meep!
0.351245
OK


Somehow the program keeps restarting with this test code.

Regarding the use of sno[], the adc values are supposed to be processed in sequential order. However, due to some hardware design issue, a few sensors had to be swapped around. So my thought was that since I would have to use some sort of sensor mapping anyway, I might as well remap at the adc interrupt instead of doing it multiple times in the main body. Is that line of thought sensible?
Ttelmah



Joined: 11 Mar 2010
Posts: 19432

View user's profile Send private message

PostPosted: Sun Nov 13, 2011 10:32 am     Reply with quote

Try increasing the default stack size.
By default on the PIC24, the value selected is too low, especially if you start using things like floating point printf....

Best Wishes
xieliwei



Joined: 13 Nov 2011
Posts: 5

View user's profile Send private message

PostPosted: Sun Nov 13, 2011 10:44 am     Reply with quote

Ttelmah wrote:
Try increasing the default stack size.
By default on the PIC24, the value selected is too low, especially if you start using things like floating point printf....

Best Wishes


After adding:
#build(stack=1024)

to main.h, it didn't seem to make any difference in execution.
Ttelmah



Joined: 11 Mar 2010
Posts: 19432

View user's profile Send private message

PostPosted: Sun Nov 13, 2011 11:14 am     Reply with quote

Add a diagnostic at the start, to find what the restart cause is.
Also post the fuses used.
The comment about sno, still applies though. Just have a second counter like curradc, set it to 3 on boot, and have it wrap the same way as curradc. Saves a lot of memory accesses.

Best Wishes
xieliwei



Joined: 13 Nov 2011
Posts: 5

View user's profile Send private message

PostPosted: Sun Nov 13, 2011 11:23 am     Reply with quote

Hmm, hang on...

Making the stack size change does fix the problem with the actual code! That's why experience is important! Thank you Ttelmah and others! Razz

Now I wonder what's causing the test code to reset?

Also, I'm seeing a interrupt disabled to prevent re-entrancy warning for @readbita when I place this code segment into my while(1) loop:
Code:
      if(!input(PIN_A11)){
         calibrate = 1;
      }else{
         calibrate = 0;
      }
      if(!input(PIN_D9)){
         if(run){
            run = 0;
            delay_ms(1000);
         }else{
            delay_ms(1000);
            run = 1;
         }
      }


The warning goes away if I replace the input(PIN_XX) with a dummy variable. Is there a way I can read a button input without having my interrupts disabled?
xieliwei



Joined: 13 Nov 2011
Posts: 5

View user's profile Send private message

PostPosted: Sun Nov 13, 2011 11:41 am     Reply with quote

Ttelmah wrote:
Add a diagnostic at the start, to find what the restart cause is.
Also post the fuses used.
The comment about sno, still applies though. Just have a second counter like curradc, set it to 3 on boot, and have it wrap the same way as curradc. Saves a lot of memory accesses.

Best Wishes


Using CCS's restart_cause(), it returns RESTART_TRAP_CONFLICT. Using the interrupt routine by FvM ( http://www.ccsinfo.com/forum/viewtopic.php?t=36479 ), it appears to be an address error. Trying to figure out where that address is now.

Are the fuses you want the ones listed in the main.h file I posted earlier?

Regarding your sno solution, great idea! Thank you!

Edit:

The offending instruction is at 06A6
Code:
  069A  E00000     cp0.w 0x0000
  069C  AF2042     btsc.b 0x0042,#1
  069E  370006     bra 0x0006ac
  06A0  09352B     repeat #13611
  06A2  000000     nop
  06A4  093FFE     repeat #16382
  06A6  000000     nop
  06A8  E90000     dec.w 0x0000,0x0000
  06AA  3AFFFA     bra nz, 0x0006a0
  06AC  060000     return


I'm not experienced with assembly at all...
FvM



Joined: 27 Aug 2008
Posts: 2337
Location: Germany

View user's profile Send private message

PostPosted: Sun Nov 13, 2011 5:03 pm     Reply with quote

Quote:
The offending instruction is at 06A6

Doesn't look like. It's a simple delay loop without any data accesses.

Once more, the related source code isn't shown.
Display posts from previous:   
Post new topic   Reply to topic    CCS Forum Index -> General CCS C Discussion All times are GMT - 6 Hours
Page 1 of 1

 
Jump to:  
You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot vote in polls in this forum


Powered by phpBB © 2001, 2005 phpBB Group