Don't ad-block us - support your favorite websites. We have safe, unobstrusive, robotics related ads that you actually want to see - see here for more.
0 Members and 1 Guest are viewing this topic.
float pulsein(int pin_number){ //set up timer for prescaler 256 TCCR2B |= (1 << CS22); unsigned int positive_width, negative_width; positive_width = 0, negative_width=0; // for 4 pulse measurements while(TCNT2 <= 18750){ while ( PIND & (1<<pin_number)){ positive_width++; } negative_width++;} return (positive_width/4);} }unsigned int pulse_width = duration;Start Timer at 50 Hz.while(PIND & (1 << PIND2)){ pulse_width++;} negative_pulse_width++;}// End timer after 4 cycles at 50 HzDivide positive_pulse_width / 3Divide negative_pulse_width / 3pulse _width = positive_pulse_width / 3;
WORD pulsein(int pin_number){ switch(pin_number) { case 4: PCMSK2 |= (1<<PCINT20); // tell pin change mask to listen to pin21 break; case 5: PCMSK2 |= (1<<PCINT21); // tell pin change mask to listen to pin20 break; case 1: PCMSK2 |= (1<<PCINT17); // tell pin change mask to listen to pin17 break; } // end switch PCICR |= (1 << PCIE2); // enabled interrupts on PCINT23..16 pin SREG |= (1 << 7); // enable global interrupts // get timer after interrupt???? pulse_width = pulse_start - current timer; return pulse_width;}SIGNAL (SIG_PCINT){ // One of pins 21, 20, or 17 just changed states // check if rising edge on pin input if(PIND & (1 << PIN4)) //rising edge { // start timer to measure pulse width pulse_start = start timer } else(PIND & (1<<PIN5)) { // start timer to measure pulse width pulse_start = start timer } else(PIND & (1<<PIN1)){ // start timer to measure pulse width pulse_start = start timer}}
Use a hardware timer such as timer1 set with a prescaler appropriate for the expected duration of the pulse and the measurement resolution you desire. You don't have to do anything fancy with the timer, just run it in normal mode. Take a look at the datasheet for more information on timer1 and its registers.
Note: be sure that your pulse will happen before the timer overflows, otherwise your time measurement will be wrong. If you can't be sure of this, make sure you notice when the timer is about to overflow and set a flag that will tell you that the current pulse was too long to measure.
P.S. I think your PC interrupt ISR is wrong. The argument to the ISR should have a number indicating which pin-change bank the ISR is for. The mega168 has three pin change banks, each one of which has its own interrupt: one for port B, one for port C, and one for port D. As you seem to have already noticed, you use a pin change mask to select which pins in a bank should generate a PC interrupt for that bank (you can use the mask to restrict it to a single pin, which makes things easy, or you can set it up so multiple pins in the bank can trigger that bank's interrupt).
When the interrupt is triggered I start the timer with the prescaler set: TCCR2B |= (1 << CS22);
I think a CTC timer will work in this case? It is the clear timer count when reaching overflow.
I looked at the datasheet and can't get information on ISR for the interrupt ?
Assuming you're using a mega168, you have access to the following #defines (taken from the WinAVR file iomx8.h, which is included automatically in your program by <avr/io.h> if your device is set as mega168):/* Pin Change Interrupt Request 0 */#define PCINT0_vect _VECTOR(3)#define SIG_PIN_CHANGE0 _VECTOR(3)/* Pin Change Interrupt Request 1 */#define PCINT1_vect _VECTOR(4)#define SIG_PIN_CHANGE1 _VECTOR(4)/* Pin Change Interrupt Request 2 */#define PCINT2_vect _VECTOR(5)#define SIG_PIN_CHANGE2 _VECTOR(5)Use the one that corresponds to the bank that has the pin(s) you want to generate interrupts. For example, you would use a PCINT2_vect ISR for pins PCINT0 - PCINT7 (i.e. pins PB0 - PB7).- Ben
WORD pulsein(int pin_number){ switch(pin_number) { case 4: PCMSK2 |= (1<<PCINT20); // tell pin change mask to listen to pin20 break; case 5: PCMSK2 |= (1<<PCINT21); // tell pin change mask to listen to pin21 break; case 1: PCMSK2 |= (1<<PCINT17); // tell pin change mask to listen to pin17 break; } PCICR |= (1 << PCIE2); // enabled interrupts on PCINT23..16 pin SREG |= (1 << 7); // enable global interrupts while(1){ } return pwm_incoming;}ISR (PCINT2_vect){ uint8_t tcnt1; // One of pins 21, 20, or 17 just changed states // check if rising on pin input if((PIND & (1 << PIN4)) || (PIND & (1<<PIN5)) || (PIND & (1<<PIN1)) ) //rising edge { // start timer to measure pulse width TCNT1 = 0; return; } /* Stop timer 1, current value is pulse width. */ tcnt1 = TCNT1; TCCR1B = (1 << CS12); // prescale at 256 pwm_incoming = tcnt1; }
TCCR1B = (1 << CS12); // prescale at 256
QuoteTCCR1B = (1 << CS12); // prescale at 256You should move this outside of your ISR and call this as part of your program's initialization routine. As it is now, this line doesn't get called until the falling edge of your first pulse, meaning that your first pulse won't be measured. You only need to start the timer once. You can just leave it running after that.
Also, I see no way for you to get out of the infinite while loop in your pulsein() function. Is this intentional or are you just trying to delay until after you've detected a pulse? If it's the latter, that's not currently what your code does.
Other problems:1) You aren't currently making a distinction between your pins, and you have no way of telling which pin has changed. This means your code currently doesn't work. If you get a rising edge on PD1 and then a falling edge on PD4, your code will register that as a pulse. Even worse, if you detect a falling edge on PD1 but PD4 or PD5 is high, you will instead think you have detected a rising edge.You should have separate timing variables for each pin that can receive a pulse, and you need to store the previous state of your pins so that you can use this as a point of comparison in your ISR to determine which pin has changed. Does this make sense? This will require cleverer use of timer1 (rather than resetting it to 0 when a pulse starts, you should just record it as the start time and when the pulse ends, compute the pulse length as TCNT1 - startTime).
2) You aren't protecting yourself against abnormally long pulses that are longer than timer1 can measure. Trying to measure such a pulse will give you an incorrect value for the pulse length. I'm not sure if this is a concern, but a robust program would take precautions against such things.- Ben
WORD pulsein(int pin_number){ PCMSK2 = 0x00; switch(pin_number) { case 4: PCMSK2 |= (1<<PCINT20); // tell pin change mask to listen to pin20 break; case 5: PCMSK2 |= (1<<PCINT21); // tell pin change mask to listen to pin21 break; case 1: PCMSK2 |= (1<<PCINT17); // tell pin change mask to listen to pin17 break; } PCICR |= (1 << PCIE2); // enabled interrupts on PCINT23..16 pin SREG |= (1 << 7); // enable global interrupts return pwm_incoming;}ISR (PCINT2_vect){ uint8_t start_time5, start_time4, start_time1,tcnt1; // One of pins 21, 20, or 17 just changed states int p1=0, p4=0, p5=0; if(TCNT1 > 65535) TCNT1=0; // we only care if there was a change from 0 to 1 from the previous state // check if rising on pin input if((state_p4==0) && (PIND & (1 << PIN4))) { p4=1; start_time4 = TCNT1; state_p4 = 1; return; } else if((state_p5==0) && (PIND & (1<<PIN5))) { p5=1; start_time5 = TCNT1; state_p5 = 1; return; } else if((state_p1==0) && (PIND & (1<<PIN1))) { p1 = 1; start_time1 = TCNT1; state_p1 = 1; return; } /* Stop timer 1, current value is pulse width. */ tcnt1 = TCNT1; if(p4) pwm_incoming = tcnt1 - start_time4; if(p5) pwm_incoming = tcnt1 - start_time5; if(p1) pwm_incoming = tcnt1 - start_time1; }
// Global variablesint pwm_incoming;int state_p1=0; // initial state on pin1int state_p4=0; //initial state on pin 4int state_p5 = 0; //initial state on pin 5// init code (main) TCCR2B |= (1 <<CS22); //prescale clock at 256 TCNT2 = 0; // start clock TIMSK2 |= (1<<TOIE2); // enable check for overflow of timer/counter 2 state_p1=0; state_p4=0, state_p5=0; // set initial state on pins to 0WORD pulsein(int pin_number){ PCMSK2 = 0x00; // probably unnecessary step pwm_incoming = 0; switch(pin_number){ case 1: PCMSK2 |= (1<<PCINT17); break; case 4: PCMSK2 |= (1<<PCINT20); case 5: PCMSK2 |= (1<<PCINT21);}//end switchreturn pwm_incoming;} // end pulsein// an interrupt for detecting pin state changesISR (PCINT2_vect){ uint8_t start_t1; // store initial time at state change uint8_t tcnt1; // for final time if((state_p1==0) && (PIND & (1<<PIN1))){ start_t1= TCNT2; state_p1 = 1; } else if(!(PIND & (1<<PIN4))) state_p4 = 0; if((state_p4==0) && (PIND & (1<<PIN4))){ start_t1= TCNT2; state_p4 = 1; } else if(!(PIND & (1<<PIN4))) state_p4 = 0;if((state_p5==0) && (PIND & (1<<PIN5))){ start_t1= TCNT2; state_p5 = 1; } else if(!(PIND & (1<<PIN5))) state_p5 = 0; // stop the timer tcnt1 = TCNT2; pwm_incoming = tcnt1 - start_t1; }// timer interrupt if overflow on timer counter 2ISR (TIMER2_OVF_vect){ // stop the clock TCCR2B = 0x00; TCCR2B != (1 <<CS22); // prescale value of 256 TCNT2=0; //reset counter TIMSK2 |= (1<<TOIE2); // enable check for overflow pwm_incoming = 0; // may have gotten a bad pulse}
The atmega168 datasheet is vast >300 pages which is daunting to a beginner.