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

Hardware Interupt Question

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



Joined: 08 Sep 2003
Posts: 16

View user's profile Send private message

Hardware Interupt Question
PostPosted: Fri Sep 26, 2003 11:45 am     Reply with quote

I am working with a 16F873 with a pulse input connected to B0. I would like to measure the time between a Low going pulse and the next High going pulse and place it in a variable to be used in some math calculations. Will I need to use the hardware interupt with a internal timmer? I have searched the fourm and didn't find much on using the hardware interupt for measuring time between pulses. I am trying to continue where another person left off. Any code snippets or pointers would be greatly appreciated.
Mark



Joined: 07 Sep 2003
Posts: 2838
Location: Atlanta, GA

View user's profile Send private message Send e-mail

PostPosted: Fri Sep 26, 2003 12:32 pm     Reply with quote

Code:

long Counter = 0;
short Got_Value = FALSE;
short First_Edge = TRUE;

void main()
{
  enable_interrupts(INT_EXT);
  ext_int_edge(H_to_L);
  enable_interrupts(GLOBAL);

  while(TRUE)
  {
    if(Got_Value)
    {
      Got_Value = FALSE;
      printf("\r\nCounter \%lu ", Counter);
      delay_ms(500); // choose your update time interval
      enable_interrupts(INT_EXT);
    }
}

#int_EXT
edge_detect_isr()
{
  if(First_Edge) // Falling edge
  {
    ext_int_edge(L_to_H);
    set_timer1(0);
    setup_timer_1 ( T1_INTERNAL | T1_DIV_BY_1 );
    First_Edge = FALSE;
  }
  else
  { // Rising edge
    setup_timer_1 ( T1_DISABLED);
    Counter = get_timer1();
    Got_Value = TRUE;
    First_Edge = TRUE;
    ext_int_edge(H_to_L);
    disable_interrupts(INT_EXT);
  }
}
Fábio Pereira



Joined: 26 Sep 2003
Posts: 1
Location: Joinville - SC

View user's profile Send private message Send e-mail Visit poster's website

PostPosted: Fri Sep 26, 2003 12:40 pm     Reply with quote

Hi wmeade,

I think you could use the timer1 configured as a synchronous timer (let's say, with a clock of 1us) and then use it's counting to get the period of the inactive time of the pulse.

First, you will need to configure the INT interruption to activate HIGH to LOW, then, in your ISR code, you will need to:
1- if the RB0 is low, clear TMR1, or sample it's actual count;
2- if the RB0 is high, sample TMR1 (if the TMR1 was not cleared in pass 1, then you will need to subtract the previous TMR1 count from the actual count). The result is the pulse period in us.

Of course, you could also use the CCP module, configured in capture mode and on each CCPxIF, you will need to change the capture mode. In that way, you will capture either high and low pulse periods. A simple code in the CCPxIF ISR will prevent from capturing the high phase of the pulse.

Regards,

Fábio Pereira
wmeade



Joined: 08 Sep 2003
Posts: 16

View user's profile Send private message

PostPosted: Fri Sep 26, 2003 3:48 pm     Reply with quote

Thanks for the quick responses. I have a couple more questions concerning the code above.

In the code above, what is the measurement in? Is it in us or ms? I have built a test fixture in the past that uses a 16F84 to send pulses to our equipment for testing speed inputs. I have it set to output a high pulse for 4ms then go low for a set amount of time using a delay, depending on the BCD switch position. In position 1 the delay is 340 ms between pulses. I am getting a reading around 27000 - 28000 using the code above. Shouldn't the counts be closer to 340? I will study up on the timers and external interupt tonight. I have until Monday to get it figured out.
Mark



Joined: 07 Sep 2003
Posts: 2838
Location: Atlanta, GA

View user's profile Send private message Send e-mail

PostPosted: Fri Sep 26, 2003 4:55 pm     Reply with quote

It is in timer1 ticks. The divider is set to 1 so it would be 1/(clock/4) * counter

You will have to adjust the timer divider based on your clock frequency and the signal that you want to measure and how accurate you want this to be. Be careful of the timer1 value rolling over. This can be trapped by the interrupt flag for timer1 or by having another counter in timer1 interrupt.

The code was posted as an example to give you an idea of how to do this. It will probably not work for you as posted. You will need to adjust it to suit your needs or better describe what you are trying to do.

Mark
wmeade



Joined: 08 Sep 2003
Posts: 16

View user's profile Send private message

PostPosted: Sat Sep 27, 2003 8:00 am     Reply with quote

I will try to explain what I am trying to measure. I have a roller with two magnetic targets on it that are pickups for a hall effect switch. When the switch passes over the targets it sends a 5vdc pulse. If the switch should stop over a magnet the signal will remain high until the roller starts turning again. I would lik to measure the time between pulses in milli-seconds and store that value in a variable. I will use that variable to calculate speed of the roller. The board has already been made with the signal comming in on the external interupt pin B0. I really appreciate the help that I am receiving.
Mark



Joined: 07 Sep 2003
Posts: 2838
Location: Atlanta, GA

View user's profile Send private message Send e-mail

PostPosted: Sat Sep 27, 2003 8:54 am     Reply with quote

What would be the range of time you would like to measure? The min and max time. Is 1 ms accurate enough for the speed measurement? What is the distance between the sensors and the total distance a single sensor can travel? What is the maximum speed?

Answer the questions and I can better tell you how to accomplish your goal.

Mark
wmeade



Joined: 08 Sep 2003
Posts: 16

View user's profile Send private message

PostPosted: Sat Sep 27, 2003 3:12 pm     Reply with quote

Mark,

Quote:

What would be the range of time you would like to measure?

Between 30ms and 800ms

Quote:

Is 1 ms accurate enough for the speed measurement?

1 mS accuracy should be ok because I will average the results.

Quote:

What is the distance between the sensors and the total distance a single sensor can travel?

The distance between the sensors will normally be 7.85 inches and will travel 15.7 inches each time before passing the hall effect switch.

Quote:

What is the maximum speed?

The max speed I would like to calculate for is 1200 FPM

Here is how I came to these conclusions:

ROll Dia: 5"
Pi: 3.14
Sensors: 2

((5 * 3.14) / 2) = 7.85 inches between sensors

I would like to calculate between 50 and 1200 FPM

- 30 mS between pulses would be 1308.33 FPM

((((1000 / 30) * 7.85) / 12) * 60) = 1308.33

- 800 mS between pulses would be 49.06 FPM

((((1000 / 800) * 7.85) / 12) * 60) = 49.06

I thought that using time between pulses would be more accurate than pulses per second. Is the way I am trying to calculate FPM the best way or is there a better way?

Thanks again for you help.

Bill
wmeade



Joined: 08 Sep 2003
Posts: 16

View user's profile Send private message

I got it working with a minor problem.
PostPosted: Mon Sep 29, 2003 3:33 pm     Reply with quote

I am able to measure the milli-seconds between pulses and averaging 8 of them before sending out the serial port. While I am receiving pulses I press a buton to save the current milli-second pulse average to the internal eeprom. The problem is that when I press the button it throughs the numbers off that are going into the averaging array. for example:

Serial Output

Counter = 109.00 Normal Speed = 108.99
Counter = 109.00 Normal Speed = 108.99
Counter = 109.00 Normal Speed = 108.99
Counter = 109.00 Normal Speed = 108.99
Counter = 109.00 Normal Speed = 108.99
Counter = 109.00 Normal Speed = 108.99
Counter = 109.00 Normal Speed = 108.99
Counter = 109.00 Normal Speed = 108.99
Counter = 109.00 Normal Speed = 109.00 Pressed The button Here
Counter = 110.37 Normal Speed = 109.00
Counter = 110.54 Normal Speed = 109.00
Counter = 110.56 Normal Speed = 109.00
Counter = 110.57 Normal Speed = 109.00
Counter = 110.57 Normal Speed = 109.00
Counter = 110.57 Normal Speed = 109.00
Counter = 110.57 Normal Speed = 109.00
Counter = 109.19 Normal Speed = 109.00
Counter = 109.02 Normal Speed = 109.00
Counter = 109.00 Normal Speed = 109.00 Back to normal here
Counter = 109.00 Normal Speed = 109.00
Counter = 109.00 Normal Speed = 109.00
Counter = 109.00 Normal Speed = 109.00
Counter = 109.00 Normal Speed = 109.00


I have tried several ways of stopping the interrupts and starting them again but nothing has worked. What could be going on here? Code is below:

Code:

#include <16f873.h>                                         // PIC Header File
#fuses HS,NOWDT,PUT,NOPROTECT,BROWNOUT,NOLVP,WRT            // set fuses
#use delay(clock=20000000)                                  // 20 MHz Crystal
#use rs232(baud=19200, xmit=PIN_C6, rcv=PIN_C7)             // setup 232 serial communication

//#include <filters.c>                                        // averaging sub-routines

// MUST HAVE ONE OF THE FOLLOWING LINES FOR BOOTLOADER
//#ORG 0x1F00,0x1FFF void loader16F877(void) {}             // for the 8k 16F876/7
#ORG 0x0F00, 0x0FFF void loader16F873(void) {}              // for the 4k 16F873/4

#define SET_SPEED_BTN     PIN_B4                            // pin 18 on 18F873

int16 Counter = 0;                                          // initialize counter variable
short Got_Value = FALSE;                                    // interrupt flag
short First_Edge = TRUE;                                    // first edge flag
long  ptics[7];
float avalue;
float norm_speed;
byte i,j,k,mode;

// ***************** START OF MAIN PROGRAM *******************
void main()
{
   enable_interrupts(INT_EXT);                              // enable external interrupt (B0)
   enable_interrupts(INT_TIMER2);                           // enable timer2 interrupt
   ext_int_edge(H_to_L);                                    // interrupt on high to low pulse edge
   enable_interrupts(GLOBAL);                               // enable all interrupts

   For (i=0;i<4;i++)                                        // read normal speed from internal eeprom
      *(&norm_speed + i) = read_eeprom(i+0);

   k=0;
   mode = 0; 

   do{
   
      // CALCULATE PULSES
      If (mode == 0) {
     
         If(Got_Value)
         {
            Got_Value = FALSE;
           
            ptics[k++] = Counter;                              // store new counter value in avg array
            if ( k==7 ) k = 0;                                 // reset

            for ( j=0; j<7; ) avalue += ptics[j++];            // sum the 8 values
            avalue /= 8.0;                                       // get rolling average
           
            printf("\r\nCounter = %6.2f   Normal Speed = %6.2f ",avalue,norm_speed);
            enable_interrupts(INT_EXT);
            enable_interrupts(INT_TIMER2);
         }
         
         If (!input(SET_SPEED_BTN))                         // check to see if (set speed btn) pressed
         {
            while (!input(SET_SPEED_BTN));                  // Wait for (set speed btn) to be let up
            mode = 1;
         }
      }         

      // SET NORMAL SPEED
      If (mode == 1) {

         norm_speed = avalue;
         For (i=0;i<4;i++)                               // write normal speed to internal eeprom
         write_eeprom(i+0, *(&norm_speed + i));
         mode = 0;
      }
   } While (TRUE);
}

#INT_EXT
edge_detect_isr()
{
   if(First_Edge) // Falling edge
   {
      ext_int_edge(L_to_H);
      Counter = 0;
      setup_timer_2 ( T2_DIV_BY_4,250,5);                   // set up for 1ms interrupt timmer
      First_Edge = FALSE;
     
   }else{  // Rising edge
   
      Got_Value = TRUE;
      First_Edge = TRUE;
      ext_int_edge(H_to_L);
      disable_interrupts(INT_EXT);
      disable_interrupts(INT_TIMER2);
   }
}

#INT_TIMER2
timer2_isr()
{
   Counter++;
}
Mark



Joined: 07 Sep 2003
Posts: 2838
Location: Atlanta, GA

View user's profile Send private message Send e-mail

PostPosted: Mon Sep 29, 2003 5:21 pm     Reply with quote

The problem I believe is due to the time required to write to the eeprom.

Code:

#include <16f873.h>                                         // PIC Header File
#fuses HS,NOWDT,PUT,NOPROTECT,BROWNOUT,NOLVP,WRT            // set fuses
#use delay(clock=20000000)                                  // 20 MHz Crystal
#use rs232(baud=19200, xmit=PIN_C6, rcv=PIN_C7)             // setup 232 serial communication

//#include <filters.c>                                        // averaging sub-routines

// MUST HAVE ONE OF THE FOLLOWING LINES FOR BOOTLOADER
//#ORG 0x1F00,0x1FFF void loader16F877(void) {}             // for the 8k 16F876/7
#ORG 0x0F00, 0x0FFF void loader16F873(void) {}              // for the 4k 16F873/4

#define SET_SPEED_BTN     PIN_B4                            // pin 18 on 18F873

int16 Counter = 0;                                          // initialize counter variable
short Got_Value = FALSE;                                    // interrupt flag
short First_Edge = TRUE;                                    // first edge flag
long  ptics[7];
float avalue;
float norm_speed;
byte i,j,k,mode;

// ***************** START OF MAIN PROGRAM *******************
void main()
{
   enable_interrupts(INT_EXT);                              // enable external interrupt (B0)
   enable_interrupts(INT_TIMER2);                           // enable timer2 interrupt
   ext_int_edge(H_to_L);                                    // interrupt on high to low pulse edge
   enable_interrupts(GLOBAL);                               // enable all interrupts

   For (i=0;i<4;i++)                                        // read normal speed from internal eeprom
      *(&norm_speed + i) = read_eeprom(i+0);

   k=0;
   mode = 0; 

   do{
   
      // CALCULATE PULSES
      If (mode == 0) {
     
         If(Got_Value)
         {
            Got_Value = FALSE;
           
            ptics[k++] = Counter;                              // store new counter value in avg array
            if ( k==7 ) k = 0;                                 // reset

//------> you probably want to initialize avalue to 0
            avalue = 0;
            for ( j=0; j<7; ) avalue += ptics[j++];            // sum the 8 values
            avalue /= 8.0;                                       // get rolling average
           
            printf("\r\nCounter = %6.2f   Normal Speed = %6.2f ",avalue,norm_speed);
            enable_interrupts(INT_EXT);
            enable_interrupts(INT_TIMER2);
         }
         
         If (!input(SET_SPEED_BTN))                         // check to see if (set speed btn) pressed
         {
            while (!input(SET_SPEED_BTN));                  // Wait for (set speed btn) to be let up
            mode = 1;
         }
      }         

      // SET NORMAL SPEED
      If (mode == 1) {

         norm_speed = avalue;
         // We are about to write to the internal eeprom so disable the speed measurement
         disable_interrupts(INT_EXT);
         disable_interrupts(INT_TIMER2);
         For (i=0;i<4;i++)                               // write normal speed to internal eeprom
         write_eeprom(i+0, *(&norm_speed + i));

         // Reset the flags.  Counter will be reset in ISR
         Got_Value = FALSE;
         First_Edge = TRUE;
         // Setup for the falling edge
         ext_int_edge(H_to_L);                                   
         enable_interrupts(INT_EXT);
         mode = 0;
      }
   } While (TRUE);
}

#INT_EXT
edge_detect_isr()
{
   if(First_Edge) // Falling edge
   {
      ext_int_edge(L_to_H);
      Counter = 0;
      setup_timer_2 ( T2_DIV_BY_4,250,5);                   // set up for 1ms interrupt timmer
      First_Edge = FALSE;
     
   }else{  // Rising edge
   
      Got_Value = TRUE;
      First_Edge = TRUE;
      ext_int_edge(H_to_L);
      disable_interrupts(INT_EXT);
      disable_interrupts(INT_TIMER2);
   }
}

#INT_TIMER2
timer2_isr()
{
   Counter++;
}


Not sure if you are planning on using the floating point math in a real application or if it is just there for debugging. If you plan on using it, I would use fixed point instead. You are dividing by 8. Dividing by a binary number makes it ideal for fixed point. It saves a lot of code and the speed is much greater.

Code:

int8 j, fraction;
int16 value;

value = 0;
for ( j=0; j<7; j++)
  value += ptics[j++];            // sum the 8 values

whole = value/8;
fraction = (int8)value  & 0x07;


Now if you actually want to display the fraction portion, a lookup array with 8 values wil work perfectly.

Regards
Mark
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