input capture interrupts

cancel
Showing results for 
Show  only  | Search instead for 
Did you mean: 

input capture interrupts

16,329 Views
marti
Contributor I
Hi
 
As a new, and very green, user of microcontrollers I have a problem I hope someone can help me with. I am trying to implement an automatic voltage regulator for a small generator with 68hc12 (Technological Arts M68DKIT912C32 using codewarrior). At present I am measuring voltage level  using an ATD input and producing a pulse using PWM.
I want to monitor the frequency 50Hz, I have been attempting to use the timer module and input capture using interrupts i.e. caputring the rising edge on two channels. I have been stuggling to get the interrupts to work properly, do I need to specify an interrupt vector. I want to interrupt on an edge and will need to count the number of times the timer overflows. I am programming in C, any ideas would be gratefully received.
 
Dazed and confused
 
Martin 
Labels (1)
0 Kudos
10 Replies

1,363 Views
Steve
NXP Employee
NXP Employee
Hi Martin,
  If you green at this then you may find the processor expert tool the best way to quickly setup the timer.
  In general you will need an input capture interrupt and a timer overflow interrupt. In each of these you should clear the interrupt flag and process the appropriate data.
 Begin by creating an interrupt service routine for each source then add the routine name to the interrupt vector table at the correct location. The table can be either in the linker PRM file or as a separate table (depending on how you have set it up). If it is in the PRM file you need to attach the interrupt service routine to to the linker VECTOR directive. You will need to define the routine like this:
Code:
interrupt void timerhandler(void){/* clear the timer flag *//* do stuff */}

 The flag to clear will depend on the interrupt (overflow or input capture) - TFLG1 or TFPG2.
Add the timerhandler to the vector table at the address given in the C32 databook.
   Now you have to configure the timer to do the job. Firstly, enable it and setup the prescaler and channels as needed. Then enable the interrupts (TIE register, TSCR2 register). Now an overflow or input capture will cause the interrupt service routine to run.
  As I said it may be easier to use processor expert in your case.

Message Edited by Steve on 01-31-2006 11:02 AM

0 Kudos

1,363 Views
marti
Contributor I
Steve,
 
Thanks for your help.
I have found this example and altered it slightly for my purpose it should increment a count when a timer overflow flag occurs. I have included the table as a seperate file.
 
Code:
unsigned int count;void main(void) {       TSCR1 = 0x80;       /* Turn on timer */    TCTL3 = 0x04;       /* Capture on rising edge CH5 */    TIE = 0x20;         /* Enable timer overflow interrupt, CH5*/    TSCR2 = 0x80;       /* Enable toi set prs 0 */    TFLG2 = 0x80;       /* Clear timer interrupt flag */    asm cli             /* Enable interrupts (clear I bit) */    while (1);          /* Do nothing */  }interrupt void toi_isr(void){    TFLG2 = 0x80;       /* Clear timer interrupt flag */    count = count + 1;  /* Increment count */    }The vector table is included in vector.cvoid toi_isr();void (* const _vectab[])() = {                  /* 0x0B10 */        0,              /* BDLC            */        0,              /* ATD             */        0,              /* reserved        */        0,              /* SCI0            */        0,              /* SPI             */        0,              /* Pulse acc input */        0,              /* Pulse acc overf */        toi_isr,        /* Timer overf     */        0,              /* Timer channel 7 */        0,              /* Timer channel 6 */        0,              /* Timer channel 5 */        0,              /* Timer channel 4 */        0,              /* Timer channel 3 */        0,              /* Timer channel 2 */        0,               /* Timer channel 1 */        0,              /* Timer channel 0 */        0,              /* Real time       */        0,              /* IRQ             */        0,              /* XIRQ            */        0,              /* SWI             */        0,              /* illegal         */        0,              /* cop fail        */        0,              /* cop clock fail  */        (void *)0xff80, /* RESET           */        };

with header file vector.h
 
Code:
void toi_isr();

 
I have run this with the codewarrior simulation software, the interrupt occurs and the program stops but the count does not increment. I expected the program to run continuosly and the count to increment after each overflow. Is it possibly not returning from the interrupt properly. I attempted to include the table in the PRM file as suggested, but unsure how to do it. Any suggestions thanks.
 
Marti

Message Edited by Alban on 02-02-2006 11:09 AM

0 Kudos

1,363 Views
Steve
NXP Employee
NXP Employee

Martin,

   In principle what you are doing is correct (I haven't checked all the bit settings) and you say you are getting an interrupt so you seem to be close to getting it working. Two things arise:

1/ You have enabled the input capture interrupt (TIE) but haven't declared an interrupt service routine for it so that interrupt would cause code to run away on the MCU and would probably cause the simulator to stop.

2/ You say the simulator takes the interrupt and then stops - can you see where it stops & how it got there? That will help work out the problem.

If your vector table is set up correctly there is no need to modify the VECTOR values in the PRM file so that won't be an issue. You should use the table or the PRM file.

0 Kudos

1,363 Views
marti
Contributor I

Steve,

Thanks for the advice I managed to set up the vectors I needed in the PRM file and the interrupts seem to work ok. Is there a way I can structure the interrupts to operate consecutively. I want to detect a rising edge on channel 5 then start the count of overflows until the next edge is detected on channel 6, I can then use this to calculate frequency.

The code I have at the moment is detecting edges and counting in no sort of order and hence the results are changing. Code below.

Martin

Code:

volatile unsigned int first, second,  count;volatile  long time;void main(){      for(;;){       asm cli     /* Turn on timer subsystem */     TSCR1 = 0x80;     TSCR2 = 0x80;     TFLG2 = 0x80; /* Setup for IC5 */     TIOS = TIOS & ~0x20;             /* Configure PT5 as IC */     TCTL3 =  0x08;                        /* Capture Rising Edge */     TFLG1 = 0x20;                        /* Clear IC5 Flag */     TIE = 0x20;                             /* Enable IC5 Interrupt */      /* Setup for IC6 */     TIOS = TIOS & ~0x40;             /* Configure PT2 as IC */     TCTL3 = 0x10;                        /* Capture Rising Edge */     TFLG1 = 0x40;     TIE = 0x40;                           /* Clear IC2 Flag */                                time = ((65536*count)+second) - first; /* Calculate total count */   }   }  interrupt void tim0_ch5_isr(void){        time = 0;    count = 0;    first = TC5;    TFLG1 = 0x20;    }    interrupt void tim0_overflow_isr(void){      count++;    TFLG2 = 0x80;        }     interrupt void tim0_ch6_isr(void){        second = TC6;    TFLG1 = 0x40;     }

Message Edited by Alban on 02-02-2006 11:10 AM

0 Kudos

1,363 Views
Steve
NXP Employee
NXP Employee
Marti,
  You general approach looks ok, but there are a couple of implementation problems. I assume that there are two separate signals on IC5 and IC6?  You say you want to arrange things consecutively but I guess it will depend on whether the IC6 or IC5 interrupt occurs first which is determined by the phase of the two. If you want to be sure the count only starts from IC5 you have to check whether it has occurred in the IC6 interrupt before you set the value of second.
  Also be aware that you are calculating the time in the main routine while the interrupts are occurring. This means that (depending on the assembly code produced) the values could change mid-calculation. You should either disable the interrupts while the calculation is occurring or make the calculation inside the IC6 ISR.

Message Edited by Steve on 02-02-2006 10:54 AM

0 Kudos

1,363 Views
marti
Contributor I

Steve,

Thanks for the tips I will give it a try. This is for my final year project at university so you have saved me some valuable time.

Thanks again

Marti

0 Kudos

1,363 Views
hdj100r
Contributor I
Marti,

As Steve said, you can not calculate the time in the main loop, as any of the variables could change (especially count). Rather than calculate the time inside the interrupt, I would normally save all the values required inside the last isr, and set a flag an calculate the time in the main loop, and then copy the values set in the interrupt in the main loop, to local variables, with the interrupts disabled. This way you are not spending execess time doing calculations etc in the interrupt code. Also be aware that some compilers may not have renterant math libraries.

You need to be very carefull with the above code, as there are race conditions, which depending on the exact timing of the interrupts, and the interrupt priority, you will very occasionally get the incorrect results.

For example, the in tim0_ch5_isr you reset the value of count to zero. But what if the input caputure occured when the timer had just overflowed. The value in first will be 0 (as TC5 is zero as the counter just overflowed), and count set to 0, but as the tim0_ch5_isr is higher priority than timer_overflow, the timer overflow interrupt has not yet executed. When tim0_ch5_isr completes, the tim0_overflow_isr will execute, and count will be incremented to one, which is not what you would want. Your time will be out by 65536.


David
0 Kudos

1,363 Views
marti
Contributor I

David,

Thanks for you input, I managed to occasionaly get the count I required when performing the calculation in the second interrupt. As you suggested I got unstable results due to the count changing whilst doing the calculation. I want to use this to monitor frequency as part of a larger program, I therefore don't want to spend lots of time doing this calculation.

I understand what you mentioned about interrupt priority so have attempted to produce code to compensate. I am using codwarrier so have no problems with the maths library. The code is below if you could give me any hints I have not yet tested this version so don't know if it works. This is my first microcontroller project and I am learning 'C' as I go so I appologise for my, probably, not very efficient code.

Confused and Fustrated

Martin


#define TRUE  1
#define FALSE 0
volatile unsigned int first, second, count, count1, count2, a, b;
volatile float time;

void main()
{  
   volatile unsigned int countstart, countend; 

    for(;:smileywink:{
     
    if(b == TRUE){
    
       asm SEI                      /* Disable interrupt */
       countend = count2;      /* Save globals as locals */
       countstart = count1;     /* Save globals as locals */
       time = ((65536*(countend-countstart))+second) - first; /* Calculate total count */
     }
          
    /* Turn on timer subsystem */
    TSCR1 = 0x80;           /* Enable */
    TFLG2 = 0x80;           /* Enable timer overflow interrupt */
  /* Setup for IC5 */
    TIOS = TIOS & ~0x20;             /* Configure PT5 as IC */
    TCTL3 =  0x04;                   /* Capture Rising Edge */
    TFLG1 = 0x20;                    /* Clear IC5 Flag */
    TIE = 0x20;                      /* Enable IC5 Interrupt */
     /* Setup for IC6 */
    TIOS = TIOS & ~0x40;             /* Configure PT6 as IC */
    TCTL3 = 0x20;                    /* Capture falling Edge */
    TFLG1 = 0x40;                    /* Clear IC6 Flag */
    TIE = 0x40;                      /* Enable IC6 Interrupt */
   
    asm CLI   
 
   }
}
 
 
interrupt void tim0_ch5_isr(void)
{  

    first = 0;
    second = 0;
    count = 0;
    first = TC5;
    TSCR2 = 0x80;       /* Enable counter */
    TFLG1 = 0x20;
    a = TRUE;        /* bit set to ensure overflow and ch6 interrupt occur after ch5 interrupt */
  

 
interrupt void tim0_overflow_isr(void)
{
    if(a == TRUE){
      TFLG2 = 0x80;     /* Clear overflow flag ch5 */
     
      if(first == 0){    /* code to conteract interrupt priority */
       count1 = count - 1;
       count1++;
      }else{
        count1 = count;
        count1++;
      }
    }
}
    
interrupt void tim0_ch6_isr(void)

    if(a == TRUE){     /* Operate only after ch5 interrupt has occurred */
      TFLG1 = 0x40;     /* Clear overflow flag ch6 */
      second = TC6;
      count2 = count;    /* Store final count */
      b == TRUE;     /* bit set when both ch interrupts have occured */
    }   
}

0 Kudos

1,363 Views
hdj100r
Contributor I

Hi Marti,

Couple of things.

1) You don't want to turn on the timer system in you main for loop, once should be sufficient.

2) You should re-enable interrupts before you calculate frequency, just after you save the globals to locals. The idea is to have interrupts disabled for a short a time as possible. You should also save second and first locally.

3) Even though you have declared time as a float, countend, countstart, second and first are unsigned ints. You will discover that C will calculate the line

  time = ((65536*(countend-countstart))+second) - first; /* Calculate total count */

as an unsigned int, and then promote the result to float. This will probably result in truncation. Read you C book for how it handles promotion of types.

4) In you code

    interrupt void tim0_overflow_isr(void)
    {
        if(a == TRUE){
         TFLG2 = 0x80;     /* Clear overflow flag ch5 */
    ....

 If 'a' is not true, TFLG2 will never be cleared, and the interrupt will never be cleared, and the interrupt routine will keep being called for ever and ever. You must clear the flag allways.

5) Your code to check for interrupt priority is probably not going to work. Firstly the example I gave when TC5 was zero, was only one case. It may be, depending on other interrupts in you system, and how long you disable interrupts for,  that TC5 may be three or four. I generally check the other interrupts flags to see if we have a priority problem.

   I generally do this by checking if the value of TC5 was between 0 and 0x7fff (simple branch on negative will work in assembler), and if so, then check to see the Timer Over Flag is set. If the Timer Over Flow flag is set, then timer overlow interrupt has not executed, so I increment the timer overflow count (in the TC5 interrupt) and clear the Timer OverFlow flag. 

6) If you are measuring 50 Hz, you really don't need two interrupts. One interrupt set for either rising or falling edge will work fine. You need to measure the time between two rising edges, or two falling edges, not the time between rising and falling edges, if you want to measure the frequency. If you measure between a rising and falling edge, the frequency will change with the duty cycle.

Don't give up. Once you get your head around the interrupts its pretty easy to measure the frequency with and HC12.

Cheers

David

 

 

 

 

 

 

 

0 Kudos

1,363 Views
marti
Contributor I

David,

Thanks for the advice, I seem to be getting closer to solving the problem. I hopefuly want to use the timer to both monitor frequency and to measure the phase angle between a voltage and current signal, from the output of a small generator. This is why I have been using the two interrupts, I thought if I could, figure this out I could easily implement frquency measurement using one interrupt. I realise I am measuring duty cycle but until I connect to the test generator I do not have a method of generating a two synchronised but out of phase signals.

I feel the problem I having is counting the timer overflow interrupts, I thought I would be able to enable the overflow interrupt during the interrupt for the first edge. and set the counter to zero and then count until the second edge occurs.


interrupt void tim0_ch5_isr(void)
{  

TFLG1 = 0x20;

   first =  TC5;
   count = 0;
   TSCR2 = 0x80
    a = TRUE;


 
interrupt void tim0_overflow_isr(void)
{
       TFLG2 = 0x80;
       count++;
 
}
   
 
interrupt void tim0_ch6_isr(void)

   TFLG1 = 0x40;
   if(a == TRUE){
   first1 = first;
     second = TC6;
     count1 = count;   
     b = TRUE;
   }
   

It does catch the correct value occasionally but often over and under counts. I have used the a and b variables so that the second time isn't taken until the first has occured and the time isn't calculated until both have occured. I haven't been able to establish any priority problems, could be due to lack of debugging knowledge, but the documentation states that the input capture events have priority over the overflow, i.e. ch5, ch6 then overflow.

I have removed the timer setup instrunctions into a seperate function called from outside the main loop and put the time calculation into a IF statement, which should only operate if both interrupts have occured. I am using the asm SEI statement to disable interrupts while save the variables, is this correct? I then enable interrupts do the calculation and clear a and b variables.  

void main()
{  
   unsigned int start, end, Tcount, time; 

 for(;:smileywink:{
  
  timerset();
         
   if((a == TRUE) && (b == TRUE)){
   asm SEI
  start = first1;
   end = second;

    asm CLI 
     time = ((65536*Tcount)+end) - start; /* Calculate total time */
   a = FALSE;
   b = FALSE;
   }
 }
}

For a 50Hz signal with the controller running in boot mode 24MHz, I calculated the timer overflow should occur 3 times (possibly 4 depending on timing of capture) . With the the interrupt timing problems you mentioned the overflow count should be no more than one count out from this value. I, however, am getting counts ranging from 0 to 12.  Any ideas?

Thanks

Marti

0 Kudos