Variable frequency and duty cycle on atmega8

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

i am trying to achieve variable frequency and duty cycle
on atmega8

/*

#include 

#define F_CPU 1000000UL

#include 


uint16_t adc_read(uint8_t ch)
{
	// select the corresponding channel 0~7
	// ANDing with '7? will always keep the value
	// of "˜ch' between 0 and 7
	ch &= 0b00000111;  // AND operation with 7
	ADMUX = (ADMUX & 0xF8)|ch; // clears the bottom 3 bits before ORing
	
	// start single convertion
	// write '1? to ADSC
	ADCSRA |= (1<<ADSC);
	
	// wait for conversion to complete
	// ADSC becomes '0? again
	// till then, run loop continuously
	while(ADCSRA & (1<<ADSC));
	
	return (ADC);
}
void adc_init()
{
	// AREF = AVcc
	ADMUX = (1<<REFS0);
	
	// ADC Enable and prescaler of 128
	// 16000000/128 = 125000
	ADCSRA = (1<<ADEN)|(1<<ADPS2)|(1<<ADPS1)|(1<<ADPS0);
}


int main(void)
{
		uint16_t chan1,chan2;
		adc_init();
    
        DDRB = _BV(PB1) | _BV(PB2);
       

       
		
        TCCR1A = 242;

        TCCR1B |= (1 << CS10)|(1<<CS12);
		
		
		
		
		
        // set prescaler to 8 and starts PWM
		TCCR1B |= (1<<WGM12)|(1<<WGM13);

        while (1)
        {
	          chan1 = adc_read(0);
	          _delay_ms(10);
	          chan1 = adc_read(0);
			  chan2 = adc_read(1);
			  _delay_ms(10);
			  chan2 = adc_read(1);
			 
			  ICR1  = chan2;
			 OCR1A = (chan1*ICR1)/1023;
			  
			  
			  
        } 
    
}

but output is weird could anybody tell me where am i going wrong

This topic has a solution.
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:

output is weird

I fear you are going to have to help us a little with that! What on earth does "weird" mean?

Anyway I thought I'd look at what your code was doing at present but I'm utterly astonished by the line:

       TCCR1A = 242;

What on other possessed you to set control bit using a decimal constant? When I convert that to binary I find it is 0xF2 or 0b11110010. So I guess it is:

TCCR1A = (1 << COM1A1) | (1 << COM1A0) | (1 << COM1B1) | (1 << COM1B0) | (1 << WGM11);

but that required me to first convert it to binary and then to read the datasheet to find out which bits were being set. Is it not MUCH MUCH easier if you simply write:

TCCR1A = (1 << COM1A1) | (1 << COM1A0) | (1 << COM1B1) | (1 << COM1B0) | (1 << WGM11);

then the reader can see instantly which bits are set.

Anyway I finally got what I was hoping for - to find out which WGM bits you were setting to see which waveform generation mode you are using. So it looks like Mode 14 which is Fast PWM with ICR1 setting TOP (the frequency)

So I guess your setup involved two pots on pins 23 and 24 and you use them to vary the frequency and the duty. I guess you are some how monitoring activity on pin 15 to observe the output?

What is your thinking behind:

(1 << COM1A1) | (1 << COM1A0) | (1 << COM1B1) | (1 << COM1B0)

Channel B is not involved at all so why set the bits? Channel A is asking for "inverted PWM" - is that really what you were planning for?

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

Sorry for confusing code

I need only channel A

Problem i am having is that i am unable to achieve duty cycle of more than 40% (non-inverting mode).

When i tried to change frequency via pot most of the time duty cycle also change.

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

Quote:

Problem i am having is that i am unable to achieve duty cycle of more than 40% (non-inverting mode).

So is that because the ADC stuff is broken or the timer/PWM stuff?

If you forget the ADC all together to start with and simply put in a couple of for() loops with a delay at each step to vary first frequency and then duty across the entire range does that work?

This is the key to writing robust software - break it down into small segments and then test each at the limits of their operation. When you are sure the timer/PWm works for all combinations of OCR1A and ICR1 setting then you can worry about the ADC to change these things.

Or work it the other way round. Forget the timer/PWM stuff and concentrate on the ADC for the time being - if you turn the pots from one extreme to the other do both give a complete 0..1023 set of readings? Also is it linear or logarithmic? I rather suspect you may be using log pots.

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

Quote:

is it linear or logarithmic? I rather suspect you may be using log pots.

i am using a linear pot. I have checked adc its giving values from 0 to 1023

Thanks for your advice going to check for various combination of ICR1 and OCR1A

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0
             chan1 = adc_read(0); 
             _delay_ms(10); 
             chan1 = adc_read(0); 

What is the point of reading each channel twice, but using only one result? If you think that you need to throw out the first reading after a channel change, you are wrong. You need to throw out the first reading after changing reference voltage.

Regards,
Steve A.

The Board helps those that help themselves.

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

Quote:

What is the point of reading each channel twice, but using only one result? If you think that you need to throw out the first reading after a channel change, you are wrong. You need to throw out the first reading after changing reference voltage.

While one must indeed take action when starting up the ADC, or changing reference (and sometimes many reads), there have been rather extensive discussions here on "channel bleedthrough" on AVR8 ADC reads.

Indeed, if OP is "sitting on a single channel" then probably not necessary. And the 10ms delay is puzzling.

After the last such thread, I became a partial believer and have done double-converts in several of my production apps with good results. Let's see if I can find the thread.
http://www.avrfreaks.net/index.p...
While this thread concentrated on BG channel, I also found "bleedthrough" with thermistor channels (which are relatively high impedance).
Other references:
http://www.avrfreaks.net/index.p...
http://www.avrfreaks.net/index.p...

You can put lipstick on a pig, but it is still a pig.

I've never met a pig I didn't like, as long as you have some salt and pepper.

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

You need to recalc the duty every time you change the freq if duty is percent on. If duty is on time in usecs, make sure it is always less then total period. Doesnt make sense if the on time is greater than the period.

Imagecraft compiler user

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

Quote:

You need to recalc the duty every time you change the freq if duty is percent on. If duty is on time in usecs, make sure it is always less then total period. Doesnt make sense if the on time is greater than the period.

Isn't he achieving that with:

           ICR1  = chan2;
          OCR1A = (chan1*ICR1)/1023;

That sets the new TOP (ICR1) then calculates the duty as a fraction of this by reading ICR1 back again though personally I think it'd be more efficient to just re-use chan2 which should hold the same value as ICR1 anyway.

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

Quote:

Isn't he achieving that with:
Code:
ICR1 = chan2;
OCR1A = (chan1*ICR1)/1023;

That sets the new TOP (ICR1) then calculates the duty as a fraction of this


No-one has mentioned overflow considerations in the 16x16 multiply?

/1023 would lead to very small duty cycles in general, wouldn't it?

You can put lipstick on a pig, but it is still a pig.

I've never met a pig I didn't like, as long as you have some salt and pepper.

This reply has been marked as the solution. 
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:

in the 16x16 multiply?

Well really 10 x 10 - but as that's twenty and the int width is 16 you're right there's still a potential problem. So I guess we all agree on something like:

OCR1A = ((uint32_t)chan1 * chan2) / 1023;