Out of timers for HCSR04

Go To Last Post
25 posts / 0 new
Author
Message
#1
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Hi
I am using an ATmega32@11.0592MHz.
I have all three timers used, timer1 for PWM for two motors, timer2 for Servo PWM and timer0 for PID sampling.

timer0 has a compare match interrupt at 72 clicks/sec rate.

now I have HC-SR04 UltraSonic Range finder, but no timer (specifically timer0), I tried lot of searching over the internet but couldn't find any of following

1. way to generate 50Hz PWM using software in a second thread (this is still not very useful as my SR04 needs a 16bit counter)

2. a software counter that I can use to count the signal width for measuring the range.

I tried doubling the use of timer0 but didn't succeed, got confused.
timer0 that is used for sampling the PID. I tried using a volatile uint32_t global to keep adding deltaTCNT0 to it (deltaTCNT0 is basically because when ever I get a chance every 72 times a sec to enter ISR(TIMER0_COMP_vect) I update the TCNT0Count with the increment in TCNT0. This isn't working probably because of overflow.

my current timer0 is setup as following

void timer0_Init(void)
{
	TCNT0=0x00;
	TCCR0 |= (1 << WGM01 ); // Configure timer 0 for CTC mode
	//TIMSK|=(1<<TOIE0); //Enable timer0 Overflow interrupts
	TIMSK |= (1 << OCIE0 ); // Enable CTC interrupt
	TCCR0|=(1<<CS02) | (1<<CS00); //Pre-scaler 1024, (42.353 interrupts/s in Normal Mode)
	
	OCR0=0x96; //72 clicks per second
}

//Timer ISR on compare match to run PID
ISR(TIMER0_COMP_vect)
{
	runPIDcount+=1;
	if (US_TIMER_ON) {
		TCNT0Count=TCNT0Count+(TCNT0-TCNT0Prev);
		TCNT0Prev=TCNT0;
	}		
	if (runPIDcount>=10){
		runPIDcount=0;
		//used for Quadrature Encoder - we will instead use MOUSE to get decoded quadrature code.
		//reading encoders on left and right motor
		scanMouse();
		actualPointLeft=mouseNewY;
		actualPointRight=mouseNewX;
		
		//if(pidSwitchLeft==1) doPIDLeft();

		//if(pidSwitchRight==1) doPIDRight();
		
		if(pidSwitchRight==1 || pidSwitchLeft==1) doPID();
		secondsCount+=1;

		SEND_ON_UART=1;
	}
}

Please suggest how can I tackle this.

Thanks
K

For reference my HCSR04 GetPulseWidth function is as following

uint16_t GetPulseWidth()
{
	uint32_t i,result;

	//Wait for the rising edge
	for(i=0;i<600000;i++)
	{
		if(!(US_PIN & (1<<US_ECHO_POS)))
		continue;	//Line is still low, so wait
		else
		break;		//High edge detected, so break.
	}

	if(i==600000)
	return US_ERROR;	//Indicates time out
	
	//High Edge Found

	TCNT0Count=0; US_TIMER_ON=1;
	
	//Now wait for the falling edge
	for(i=0;i<600000;i++)
	{
		if(US_PIN & (1<<US_ECHO_POS))
		{
			if(TCNT1 > 60000) break; else continue;
		}
		else
		break;
	}

	if(i==600000)
	return US_NO_OBSTACLE;	//Indicates time out

	//Falling edge found

	result=TCNT0Count;
	US_TIMER_ON=0;

	if(result > 60000)
	return US_NO_OBSTACLE;	//No obstacle
	else
	return (result>>1);
}
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

User timer1 input capture for measuring the hcsr04 time and use the output compares with interrupts for your pwm generation. You could use the one timer for all your timing if you are creative.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Kartman wrote:
User timer1 input capture for measuring the hcsr04 time and use the output compares with interrupts for your pwm generation. You could use the one timer for all your timing if you are creative.

I use timer1 for PWM generation for left and right motors
I do not have much knowledge about input capture. I will read about it.
Can you direct me somewhere where I can read about using a timer for multiple purpose simultaneously.

I am sure we can be creative to use one timer for all our needs but at this point probably my creativity is bound to my limited knowledge.

Your help is highly appreciated.

Thanks
K

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

The datasheet explains how the capture and compare units work. You need to think a little laterally - with the pwm, what's the frequency?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Kartman wrote:
The datasheet explains how the capture and compare units work. You need to think a little laterally - with the pwm, what's the frequency?

I have my PWM set at frequency 553Hz (750us)

Now I understand how I can use ICP1 on ATmega32@11.0592M

but it is still far from my imagination how I can use them together.

I have my timer1 running two separate PWM on channel A and B. see the initialization below

void motor_pwm_timer1_init(uint16_t top, uint16_t bottom_right, uint16_t bottom_left)
{
	/* setup phase corrected PWM for motor*/
	/* Set OCR1A/OCR1B on Compare Match when up-counting.
	Clear OCR1A/OCR1B on Compare Match when down-counting. */
	TCCR1A |= _BV(COM1A1) | _BV(COM1B1); // | _BV(COM1A0) | _BV(COM1B0);
	
	/* Phase Correct and Phase and Frequency Correct PWM */
	TCCR1B |= _BV(WGM13);
	
	/* Pre-scalar 1 */
	TCCR1B |= _BV(CS10);
	
	/* set the TOP value ICR1 */
	ICR1 = top; //10000
	
	/* Set OCR1A and OCR1B to desired Duty cycle */
	OCR1A = bottom_left; //left_motor 4300 init with this value to reach setpoint quickly (i have a fixed setpoint as of now, found this value by experiment)
	OCR1B = bottom_right; //right_motor 4150 init with this value to reach setpoint quickly
}

So when my ICP1 is triggered (low-to-high) it fire a inturrupt and I capture the value of (TCNT*, not sure which channel) and then make it ready for (high-to-low) and capture that.

So I am all stuck with
1. what TCNT* it would capture
2. what happen when there is an overflow in between my ICP1 triggers. how do I capture that.
3. I am not sure if my current phase and frequency corrected mode is suitable as There is a TOP defined to 10000 (I am not sure (I am all confused with timers now, its tricky) if Counter would ever reach overflow situation)

Please Help.

Thanks
K

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

First, you can't use ICR1 as top and use input capture. If you need two PWM channels and input capture, you'll have to use one of the fixed modes (8, 9, or 10 bit).

Note that there are no fixed phase-and-frequency correct modes.

It will also be challenging to use input capture with any of the phase correct modes, as TCNT1 counts up and down. This means you need to determine the direction of counting on both the rising and falling edge of the echo pulse, and correctly calculate the true distance between them. Edge cases (capture near BOTTOM or TOP) may be difficult to discriminate.

At 11.0592 MHz, here's a breakdown of some of the PWM frequencies available to you with fixed PWM modes:

Mode  Prescaler  Frequency
----  ---------  ---------
   1          1   21.7 kHz
              8    2.7 kHz
             64    339 Hz
   2          1   10.8 kHz
              8   1.35 kHz
             64    169 Hz
   3          1    5.4 kHz
              8    676 Hz
             64     84 Hz

The stated accuracy of the sensor is no better than 3 mm, corresponding to a pulse width delta of 58*0.3 = 17.4 us (from the formula given in the datasheet). At 11.0592 Mhz, that corresponds to just over 192 cpu cycles. Even with a TIMER prescaler of 64, this means 3 timer ticks will go by at the sensor's best resolution. This means that even at prescaler 64, input capture has 3 times better resolution than the sensor.

For the stated range of the sensor, 2 - 400 cm, the range of echo pulse widths you can expect to see are 58*2 = 116 us, through 58*400 = 23,200 us. That's about 1,283 through 256,573 cpu cycles. With a prescaler of 64, in timer ticks this is a range of about 20 ticks to 4008 ticks (conveniently on par with the range in mm).

4008 ticks doesn't fit into even the 10-bit fixed mode (2046 ticks), so you'll have to handle overflow. Usually a slim overflow ISR that increments a counter can handle those for you. You'll need to be even more careful handling edge cases (capture near overflow).

Also, since input capture can only be configured for one edge at a time, your capture ISR will have to flip the configured edge on each run.

In view of the fact that the sensor has a best-case resolution equal to 192 cpu cycles, I'd say input capture isn't really buying you anthing.

Stick with mode 8 as you have been doing, with TOP = ICR1 = 10000, and prescaler = 1. Set up an overflow interrupt on TIMER1 to count overflows, and use a pin-change interrupt to handle the echo pulse. When your pin-change ISR sees a rising edge on the echo pulse, note the current time (overflow count*20,000 + TCNT1). You'll also need to determine the direction that TCNT1 is counting to adjust the timestamp. Up-counting, no adjustment. Down-counting, add 10,000 to your timestamp.

On the falling edge the pin-change ISR does the same, then subtracts this timestamp from the rising edge timestamp to get a number of cpu cycles between edges of the echo pulse. A bit of math, and you've got your range.

You'll still need to handle edge cases carefully (pin-change near BOTTOM), as a mis-counted or uncounted overflow can put you off by 20,000 timer ticks, or about 31 cm!

Since the resolution of the sensor is at best 192 cpu cycles, interrupt latency won't really be much of a problem.

If either of the other timers were running a non-phase-correct mode with a prescaler of 64 or less, you could use it and not have to worry about determining counting direction, but the overflow math would of course be different.

BTW, there a dozen other ways to do this. These are by no means necessarily the 'best'.

JJ

I'm really tired of getting captcha'd for link-less posts :evil:

"Experience is what enables you to recognise a mistake the second time you make it."

"Good judgement comes from experience.  Experience comes from bad judgement."

"Wisdom is always wont to arrive late, and to be a little approximate on first possession."

"When you hear hoofbeats, think horses, not unicorns."

"Fast.  Cheap.  Good.  Pick two."

"We see a lot of arses on handlebars around here." - [J Ekdahl]

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Thanks a lot JJ,
I highly appreciate your effort to explain this. I just read thru and only 20% sunk in my head (its 2 am), I will try this tomorrow and keep everyone posted.

BTW, I just finished making the SR04 work standalone using ICP1 (The wire in ribbon connecting ECHO pin was faulty and it took time figuring out)

For someones reference

#ifndef F_CPU
#define F_CPU 11059200UL
#endif
#include 
#include 
#include 
#include "lcd.h"


#define SR_04_TRIGGER	PB3			//PD1 - OUTPUT			
#define SR_04_ECHO		PD6			//PD6 - ICP1

uint16_t getDistance() 
{
	uint16_t t1 = 0, t2 = 0;
	// Simple steps for generating a trigger pulse at trigger pin
	PORTB|=1<<SR_04_TRIGGER;
	_delay_us(15);
	PORTB&=~(1<<SR_04_TRIGGER);
	
	TCCR1A = 0;		
	TCCR1B |= (1<<CS11) | (1<<CS10);	// Timer operating freq is 11.0592MHz/64 (from pre-scalar) gives min. resolution of 5.79us
	
	// ICES1 = 1 for rising edge detection on ICP1
	TCCR1B |= (1<<ICES1);
	
	while((TIFR&(1<<ICF1))==0);	// polled until rising edge detected
	t1 = ICR1;					// Storing the timer value
	
	TIFR = 1<<ICF1;			// Clearing the flag for next use
	
	// ICES1 = 1 for rising edge detection on ICP1
	TCCR1B &=~_BV(ICES1);
	while((TIFR&(1<<ICF1))==0);	// polled until falling edge detected
	t2 = ICR1;					// Storing the timer value
	
	TIFR = 1<<ICF1;			// Clearing the flag for next use
	
	TCCR1B = 0;					//stop timer
	
	return ((t2-t1)*5.79)/58;
}

int main(void)
{
	DDRB|=(1<<SR_04_TRIGGER);
	uint16_t dist = 0;
	sei();
	
	/* initialize LCD display, cursor off */
	lcd_init(LCD_DISP_ON);
	lcd_clrscr();   /* clear display home cursor */
	
	DDRB |= 1<<SR_04_TRIGGER;
    while(1)
    {
		dist=getDistance();
        lcd_putixy(dist,0,0,4); //last parameter is number of digits placeholder
		lcd_putsxy(" cm",4,0);
		_delay_ms(100);
    }
}

Edit: Updated code as per following comment by kartman about clearing a flag and using ICR1 instead of ICR1L and ICR1H. and updated the distance calculation 'return ((t2-t1)*5.79)/58; ' (I was using 4 instead of timer resolution 5.79)

Thanks
K

Last Edited: Sun. Oct 20, 2013 - 07:41 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

As you've discovered, mixing pwm with icp doesn't work too well. My suggestion was to generate pwm using the compare feature. Doing pwm this way increases the cpu overhead as it needs to compute the next output transition. You add a value to the OCR register and set the compare bits to output high or low on compare. Overflow is handled by using uint16 calcs - the numbers cleanly wrap around. Get a pencil and paper and try it yourself - remember that the numbers are modulo 65536.

To clear flags, do not use |= as this will clear all flags in the register. Use only =
You can also read/ write the 16 bit registers in one statement. Omit the H or L . The compiler will take care of the order.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Kartman wrote:
Doing pwm this way increases the cpu overhead...
This also makes phase and phase/frequency correct pwm a bit more challenging, but no more so than trying to do capture with an up/down-counting timer.

You can probably get away with fast PWM anyway. Yes, motors generally accelerate/decelerate more smoothly with phase or phase/frequency correct PWM, but depending on your needs it may not really matter.

JJ

"Experience is what enables you to recognise a mistake the second time you make it."

"Good judgement comes from experience.  Experience comes from bad judgement."

"Wisdom is always wont to arrive late, and to be a little approximate on first possession."

"When you hear hoofbeats, think horses, not unicorns."

"Fast.  Cheap.  Good.  Pick two."

"We see a lot of arses on handlebars around here." - [J Ekdahl]

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Kartman wrote:

To clear flags, do not use |= as this will clear all flags in the register. Use only =

Do you mean if I have to clear ICF1 (i.e. ICF1 can be cleared by writing a logic one to its bit location)

I have to do TFIR=1<<ICF1
assuming TOV1 is set
TFIR=00010010
ICF1=4 (position5)
1<<ICF1 = 00010000
TFIR = 1<<ICF1 will write 00010000 (TOV1 value is set to zero)
So what will happen to TOV1?
But again, TOV1 can be cleared by writing a logic one to its bit location, so it will be unaffected.

So when, I use TFIR|=1<<ICF1
again,assuming TOV1 is set
TFIR=00010010
ICF1=4 (position5)
1<<ICF1 = 00010000
TFIR = TFIR | 1<<ICF1 will write 00010010 (TOV1 value is set to one) so now TOV1 is set to 1 and reset
:shock:

You are so right.

Thanks,
K

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Do you think i would lie to you? :D
You correctly identified the problem. However the reality may not be so grim as the compiler might be able to use a bit operation, in which case the correct operation would occur. Nevertheless, best to be explicit and not have any hidden nasties ready to bite you when you don't expect.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Kartman wrote:
However the reality may not be so grim as the compiler might be able to use a bit operation, in which case the correct operation would occur.

Oh wow, so this problem of using a read-modify-write operation on an interrupt flag register is even more hidden and only occurs if it's a multi-instruction operation (explicit read, explicit write), but not if it's a SBI instruction? So basically would only occur when one was clearing multiple flags. There should be a paragraph in the datasheets with reference from every interrupt flag register on this pitfall (if there isn't already), because it's something one might never notice if someone didn't tell them.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

blargg wrote:
Oh wow, so this problem of using a read-modify-write operation on an interrupt flag register is even more hidden and only occurs if it's a multi-instruction operation (explicit read, explicit write), but not if it's a SBI instruction? So basically would only occur when one was clearing multiple flags.
That depends on the device, the register, and the compiler.

Registers in the first 32 bytes of memory-mapped I/O are accessible at the bit level with CBI/SBI.

Some devices have flag registers in this area, like TIFR0/1/2 in the ATmega48/88/168/328. Some do not, like the ATmega32.

When possible, the decision to use CBI/SBI v.s. R-M-W is done by the compiler, irrespective of the contents and meaning of the register in question. An assembler programmer would make his own choices with the knowledge of the register's purpose and the needs of the application.

Quote:
There should be a paragraph in the datasheets with reference from every interrupt flag register on this pitfall (if there isn't already), because it's something one might never notice if someone didn't tell them.
There is plenty of information in the datasheet on this matter. For the ATmega32:
In the datasheet, Atmel wrote:
Do not use Read-Modify-Write instructions (SBI and CBI) to set or clear the MPCM bit. The MPCM bit shares the same I/O location as the TXC Flag and this might accidentally be cleared when using SBI or CBI instructions.
In the datasheet, Atmel wrote:
• Bit 4 – ADIF: ADC Interrupt Flag
This bit is set when an ADC conversion completes and the Data Registers are updated. The ADC Conversion Complete Interrupt is executed if the ADIE bit and the I-bit in SREG are set. ADIF is cleared by hardware when executing the corresponding interrupt handling vector. Alternatively, ADIF is cleared by writing a logical one to the flag. Beware that if doing a Read-Modify-Write on ADCSRA, a pending interrupt can be disabled.
Some other datasheets have additional warnings.

Some registers, like TIFR on the '32, contain only interrupt flag bits and nothing else. In this case it is most appropriate to do direct assignment instead of R-M-W when clearing a flag or several flags. This is due to the mechanism by which the flag is cleared, i.e. writing a 1, and because writing a 0 to a flag bit has no effect.

Some other registers have non-flag bits mixed with flag bits. For example, SPSR, UCSRA, TWCR, ACSR, and ADCSRA. In some cases the procedure for clearing a flag is subtly different, as in the case of SPIF in SPSR.

The following:

ACSR |= (1<<ACIC);

... might inadvertently clear ACI, but in most cases this isn't a problem, since the programmer would likely want to explicitly clear it anyway. Additionally, since ACSR is at 0x08, it can be accessed by CBI/SBI so the compiler should do so, but might not in the case of:

ACSR |= (1<<ACBG) | (1<<ACIC);

Again, not normally an issue.

If you truly want to avoid clearing a pending interrupt flag, and don't want to rely on the selection of CBI/SBI (which you shouldn't), use something like:

ACSR = (ACSR & ~(1<<ACI)) | (1<<ACBG) | (1<<ACIC);

As @Kartman has said, it is best to be explicit.

The datasheet isn't a guide for a specific implementation of compiler, or even a specific HLL. It is for a specific device. The developer must know his device.

JJ

"Experience is what enables you to recognise a mistake the second time you make it."

"Good judgement comes from experience.  Experience comes from bad judgement."

"Wisdom is always wont to arrive late, and to be a little approximate on first possession."

"When you hear hoofbeats, think horses, not unicorns."

"Fast.  Cheap.  Good.  Pick two."

"We see a lot of arses on handlebars around here." - [J Ekdahl]

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

OK I tried tried and tried and now I am tired tired and tired.

I tried to use Timer1 for both counting time as well as measuring time for HC-SR04 but I couldn't do it.

I thought of second way, I will use US sensor only when bot is not moving (I have IR sensors to detect obstacles).
Once obstacle is detected in front I will strop the bot and save current timer1 values and re-initialize it to be used for scanning around to get the best path, once scanning is complete, restore the variables of timer1 to be used as PWM.

Let me know if there is any other better option.

Thanks,
K

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

We have no idea of what you tried, so how can we help? Timer stuff can be tricky as the timer is always ticking and you need to think in terms of time. So you want input capture and two pwm outputs on one 16 bit timer. Show us your attempts.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

To get an interrupt within a constant interval, you can also use the UART or ADC.

Or use the pin compatible ATmega1284 with 4 timers.

Peter

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

That would spoil the fun Danni. This is a good opportunity to get close and personal with the timers and cope with some real time action. Via hackaday, i watched a video of the Activision guy explaining how they wrote games for the Atari 2600. Counting cycles and bytes and reusing stuff in obscure ways.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Kartman wrote:
That would spoil the fun Danni. This is a good opportunity to get close and personal with the timers and cope with some real time action. Via hackaday, i watched a video of the Activision guy explaining how they wrote games for the Atari 2600. Counting cycles and bytes and reusing stuff in obscure ways.
Racing the beam... those were the days... ;)

@Kartman, it almost sounds as though, sometimes, you like a challenge, too... even if there is another way...

Warms my heart :)

"Experience is what enables you to recognise a mistake the second time you make it."

"Good judgement comes from experience.  Experience comes from bad judgement."

"Wisdom is always wont to arrive late, and to be a little approximate on first possession."

"When you hear hoofbeats, think horses, not unicorns."

"Fast.  Cheap.  Good.  Pick two."

"We see a lot of arses on handlebars around here." - [J Ekdahl]

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Of course, there are many ways to skin a cat. Its always good to investigate other avenues, even if it doesn't end up successful. Something will be learnt in the process.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Ignore

Last Edited: Sun. Oct 27, 2013 - 08:35 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Ignore

Last Edited: Sun. Oct 27, 2013 - 08:34 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Kartman wrote:
We have no idea of what you tried, so how can we help? Timer stuff can be tricky as the timer is always ticking and you need to think in terms of time. So you want input capture and two pwm outputs on one 16 bit timer. Show us your attempts.

Well here is the code,
It gives me the PWM at 549Hz but it doesn't give me distance, it shows 0 always.
Oscilloscope shows pulse returned from SR04 but distance is not calculated.

My entire code listing is here (AS6.1)

/*
 * HCSR04_32.c
 *
 * Created: 20-10-2013 00:35:05
 *  Author: Karmu
 */ 


#ifndef F_CPU
#define F_CPU 11059200UL
#endif
#include 
#include 
#include 
#include "lcd.h"

#define TOP			10000
#define PWM_PORT	PORTD
#define PWM_DDR		DDRD
#define A_PWM		PD5			//OC1A
#define B_PWM		PD4			//OC1B

#define M_R_PORT	PORTD
#define M_R_DDR		DDRD
#define M_R_IN1		PD3			//IN3
#define M_R_IN2		PD2			//IN4

#define SR_04_DDR		DDRB
#define SR_04_TRIGGER	PB3			//PD1 - OUTPUT			
#define SR_04_ECHO		PD6			//PD6 - ICP1



void timer1_Init(void)
{
		/* setup phase corrected PWM for motor*/
	/* Set OCR1A/OCR1B on Compare Match when up-counting.
	Clear OCR1A/OCR1B on Compare Match when down-counting. */
	TCCR1A |= _BV(COM1A1) | _BV(COM1B1); // | _BV(COM1A0) | _BV(COM1B0);
	
	/* Phase Correct and Phase and Frequency Correct PWM */
	TCCR1B |= _BV(WGM13);
	
	/* Pre-scalar 1 */
	TCCR1B |= _BV(CS10);
	
	/* set the TOP value ICR1 */
	ICR1 = TOP;
	
	/* Set OCR1A and OCR1B to desired Duty cycle */
	OCR1A = 7000;
	OCR1B = 7000;
}
void timer1_Init_Fast(void)
{
		/* setup fast PWM for motor*/
	/* Set OCR1A/OCR1B on Compare Match(OC1A/OC1B) when up-counting.
	Clear OCR1A/OCR1B on BOTTOM when down-counting. */
	TCCR1A |= _BV(COM1A1) | _BV(COM1B1); // | _BV(COM1A0) | _BV(COM1B0);
	
	/* Fast PWM - Mode 14*/
	TCCR1A |= _BV(WGM11);
	TCCR1B |= _BV(WGM13) | _BV(WGM12);
	
	/* Pre-scalar 1 */
	TCCR1B |= _BV(CS10);
	
	/* set the TOP value ICR1 */
	ICR1 = 20108;
	
	/* Set OCR1A and OCR1B to desired Duty cycle */
	OCR1A = 15054; //(75%)
	OCR1B = 10054; //(50%)
}
uint16_t getDistance()
{
	uint16_t t1 = 0, t2 = 0;
	// Simple steps for generating a trigger pulse at trigger pin
	PORTB|=1<<SR_04_TRIGGER;
	_delay_us(15);
	PORTB&=~(1<<SR_04_TRIGGER);
	
	//TCCR1A = 0;TCCR1B = 0;
	//TCCR1B |= (1<<CS11) | (1<<CS10);	// Timer operating freq is 11.0592MHz/64 (from pre-scalar) gives min. resolution of 5.79us
	
	// ICES1 = 1 for rising edge detection on ICP1
	TCCR1B |= (1<<ICES1);
	
	while((TIFR&(1<<ICF1))==0);	// polled until rising edge detected
	t1 = ICR1;					// Storing the timer value
	
	TIFR = 1<<ICF1;			// Clearing the flag for next use
	
	// ICES1 = 1 for rising edge detection on ICP1
	TCCR1B &=~_BV(ICES1);
	while((TIFR&(1<<ICF1))==0);	// polled until falling edge detected
	t2 = ICR1;					// Storing the timer value
	
	TIFR = 1<<ICF1;			// Clearing the flag for next use
	
	//TCCR1B = 0;					//stop timer
	return ((t2-t1)*5.79)/.1; //what is the timer resolution, i am not sure if it is .1 (PreScalar/FCPU)
}
void init(){
	SR_04_DDR |= _BV(SR_04_TRIGGER);
	PWM_DDR |= _BV(A_PWM) | _BV(B_PWM);
	M_R_DDR |= _BV(M_R_IN1) | _BV(M_R_IN2) ;
	timer1_Init_Fast();
	sei();
}
int main(void)
{
	init();
	
	uint16_t dist = 0;
	sei();
	
	/* initialize LCD display, cursor off */
	lcd_init(LCD_DISP_ON);
	lcd_clrscr();   /* clear display home cursor */
	
	//
	M_R_PORT |=  _BV(M_R_IN2);		/* set DIR pin */
	M_R_PORT &= ~_BV(M_R_IN1);		/* clear DIR pin */
	
    while(1)
    {
		dist=getDistance();
        lcd_putixy(dist,0,0,4); //last parameter is number of digits placeholder
		lcd_putsxy(" cm",4,0);
		//_delay_ms(100);
    }
}

Thanks,
K

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Wasn't there a discussion on how pwm modes will affect input capture? Did I not say something about using compare interrupts and not a pwm mode? These were the hints.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Kartman wrote:
As you've discovered, mixing pwm with icp doesn't work too well. My suggestion was to generate pwm using the compare feature. Doing pwm this way increases the cpu overhead as it needs to compute the next output transition. You add a value to the OCR register and set the compare bits to output high or low on compare. Overflow is handled by using uint16 calcs - the numbers cleanly wrap around. Get a pencil and paper and try it yourself - remember that the numbers are modulo 65536.

Kartman, thanks for your efforts, but this just went over my head.

PWM using compare feature - You mean a CTC mode? but then how do I change the duty cycle in CTC mode(12)?
And what calculation do I need to do when there is an Overflow?

Sorry for my silly questions, but believe me I am totally confused with this approach.

Regards,
K

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Have the timer free run.

0xe000 + 0x2000 = ????

If you add x to the output compare register, it will interupt x ticks in the future. When you get that interrupt the set/clear a port bit then add the next compare to the compare register. Start with adding a constant value and toggling a port bit. Then progress to having a variable on time but fixed overall period. Then expand that to do multiple outputs concurrently. You do not need to touch TCNT and only need to use output compares.