|
|
View previous topic :: View next topic |
Author |
Message |
wmeade
Joined: 08 Sep 2003 Posts: 16
|
Hardware Interupt Question |
Posted: Fri Sep 26, 2003 11:45 am |
|
|
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
|
|
Posted: Fri Sep 26, 2003 12:32 pm |
|
|
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
|
|
Posted: Fri Sep 26, 2003 12:40 pm |
|
|
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
|
|
Posted: Fri Sep 26, 2003 3:48 pm |
|
|
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
|
|
Posted: Fri Sep 26, 2003 4:55 pm |
|
|
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
|
|
Posted: Sat Sep 27, 2003 8:00 am |
|
|
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
|
|
Posted: Sat Sep 27, 2003 8:54 am |
|
|
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
|
|
Posted: Sat Sep 27, 2003 3:12 pm |
|
|
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
|
I got it working with a minor problem. |
Posted: Mon Sep 29, 2003 3:33 pm |
|
|
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
|
|
Posted: Mon Sep 29, 2003 5:21 pm |
|
|
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 |
|
|
|
|
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
|