Generating PWM using non pwm mode CTC

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

Hello, I was wondering how would I generate a pwm signal using ctc mode or normal mode, I know the answer would be why would you generate a software pwm when you can hardware generate it, its for a school project to learn how to use them both in different applications- if its not a practical one lol- so what I was thinking was the following:

I am initializing the timer with a frequency of 1MHz with a 1024 prescaling.


#include <avr/io.h>
#include <avr/interrupt.h>
unsigned char dutyCycle=200;
unsigned char count = 0;
ISR(TIMER0_COMP_vect)
{
	count++;
	if(count <= dutyCycle)
	{
		PORTC = PORTC | (1<<PC0); //Pin output is high

	}
	else
		PORTC = PORTC & ~(1<<PC0); //Pin output is low
}
void timer0_init_CTC_mode(unsigned char tick)
{
	TCNT0 = 0; //timer initial value
	OCR0  = tick; //compare value
	TIMSK |= (1<<OCIE0); //enable compare interrupt
	/* Configure timer0 control register
	 * 1. Non PWM mode FOC0=1
	 * 2. CTC Mode WGM01=1 & WGM00=0
	 * 3. No need for OC0 in this example so COM00=0 & COM01=0
	 * 4. clock = F_CPU/1024 CS00=1 CS01=0 CS02=1
	 */
	TCCR0 = (1<<WGM01) | (1<<CS02) | (1<<CS00);
}
int main(void)
{
	DDRC  |= (1<<PC0);         // configure the led pin to be output pin.
	PORTC &= ~(1<<PC0);        // LED is off at the beginning(Positive Logic).
	SREG  |= (1<<7);           // enable global interrupts in MC.
	timer0_init_CTC_mode(dutyCycle); // start the timer.
    while(1)
    {

    }
}

 

however the result nothing close to a pwm signal, what am i doing wrong or what am i misunderstanding here?,

UPDATED: well this is just a shot from the signal, it seems here like it would perform as expected, however it stays on for like years and then go down low for years also, there is something i am not understanding on how to toggle the pin and when to and how to actually achieve the duty cycle I want ranging from 0-to 100%

This topic has a solution.

Sherif Nassar

Last Edited: Wed. Feb 12, 2020 - 12:06 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

You declare a global

uint8 cnt=0;

but then you use

count++;; 

'cnt' and 'count' are not the same variable!

 

Or is that not the real code that you posted?

 

sherifb96 wrote:
the result nothing close to a pwm signal

Go on - show what you're actually getting!

 

See Tip #1 (in my signature, below; may not be visible on mobile) for how to post pictures ...

Top Tips:

  1. How to properly post source code - see: https://www.avrfreaks.net/comment... - also how to properly include images/pictures
  2. "Garbage" characters on a serial terminal are (almost?) invariably due to wrong baud rate - see: https://learn.sparkfun.com/tutorials/serial-communication
  3. Wrong baud rate is usually due to not running at the speed you thought; check by blinking a LED to see if you get the speed you expected
  4. Difference between a crystal, and a crystal oscillatorhttps://www.avrfreaks.net/comment...
  5. When your question is resolved, mark the solution: https://www.avrfreaks.net/comment...
  6. Beginner's "Getting Started" tips: https://www.avrfreaks.net/comment...
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Updated the post, nah the variable was correct I just copied an old version of it which was wrong.

Sherif Nassar

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

In one of the timer tutorials in the Tutorial Forum I remember adding my poorman's "soft PWM" stuff to show an example of how to do it easily (I wanted to control the brightness of an LCD using PWM but on a pin that was not one of the timer OC pins). I'll see if I can find it...

 

EDIT: astonishing, I actually managed to find something for once!!

 

https://www.avrfreaks.net/comment/518563#comment-518563

Last Edited: Wed. Feb 12, 2020 - 10:43 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

well thank you it has been a great help and yes now I can fully control the duty cycle I want, but if you dont mind the question, is there a way to only use the CTC mode? I think my code or the idea I am following is somehow correct but there is something I am missing is messing up the whole output signal.

Sherif Nassar

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

sherifb96 wrote:
Updated the post

Not good to update the post after there have been replies to it in its original form!

 

Better to reply with the corrections!

 

I just copied an old version of it which was wrong.

That is completely different to what you posted originally!!

Top Tips:

  1. How to properly post source code - see: https://www.avrfreaks.net/comment... - also how to properly include images/pictures
  2. "Garbage" characters on a serial terminal are (almost?) invariably due to wrong baud rate - see: https://learn.sparkfun.com/tutorials/serial-communication
  3. Wrong baud rate is usually due to not running at the speed you thought; check by blinking a LED to see if you get the speed you expected
  4. Difference between a crystal, and a crystal oscillatorhttps://www.avrfreaks.net/comment...
  5. When your question is resolved, mark the solution: https://www.avrfreaks.net/comment...
  6. Beginner's "Getting Started" tips: https://www.avrfreaks.net/comment...
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

sherifb96 wrote:

unsigned char count = 0;
ISR(TIMER0_COMP_vect)
{
	count++;
	if(count <= dutyCycle)
	{
		PORTC = PORTC | (1<<PC0); //toggle led every 0.5 second
		//clear the tick counter again to count a new 0.5 second
	}
	else
		PORTC = PORTC & ~(1<<PC0);
}

Looks like a prime example of where a static local would be more appropriate:

ISR(TIMER0_COMP_vect)
{
    static uint8_t count;
    
    count++;
    if(count <= dutyCycle)
    {
        PORTC = PORTC | (1<<PC0); //toggle led every 0.5 second
        //clear the tick counter again to count a new 0.5 second
    }
    else
        PORTC = PORTC & ~(1<<PC0);
}

 

Top Tips:

  1. How to properly post source code - see: https://www.avrfreaks.net/comment... - also how to properly include images/pictures
  2. "Garbage" characters on a serial terminal are (almost?) invariably due to wrong baud rate - see: https://learn.sparkfun.com/tutorials/serial-communication
  3. Wrong baud rate is usually due to not running at the speed you thought; check by blinking a LED to see if you get the speed you expected
  4. Difference between a crystal, and a crystal oscillatorhttps://www.avrfreaks.net/comment...
  5. When your question is resolved, mark the solution: https://www.avrfreaks.net/comment...
  6. Beginner's "Getting Started" tips: https://www.avrfreaks.net/comment...
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

and a quick question in regards to the method you mentioned in your post, how can the timer reaches over flow if the OCR0 value is 10? shouldnt it just keep resetting tcnt once it reaches the ocr0 value which is 10? and then it would never overflow, but how can the output pin in the isr for ovf be set?

Sherif Nassar

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

awneil wrote:

sherifb96 wrote:
Updated the post

Not good to update the post after there have been replies to it in its original form!

 

Better to reply with the corrections!

 

I just copied an old version of it which was wrong.

That is completely different to what you posted originally!!

well its not that different i just used unsigned char instead of uint8 and I added the rest of the application - my main- just for sake of checking the initialization and how I call the main, but the main problem is with the ISR

Sherif Nassar

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

awneil wrote:

sherifb96 wrote:


 

Looks like a prime example of where a static local would be more appropriate:


    static uint8_t count;

 

well static or not static the count variable is global anyways, its value would be stored for the whole program, my problem is with when to toggle my bit, because it seems the count less than duty cycle is not achieving what it's supposed to, and the signal varies after a very long long time

Sherif Nassar

Last Edited: Wed. Feb 12, 2020 - 11:19 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Please don't block-quote the entire post - it just clogs up the discussion!

See this tutorial on how to do replies: https://www.avrfreaks.net/forum/...

 

sherifb96 wrote:
static or not static the count variable is global anyways, its value would be stored for the whole program

That is true.

 

But, as a matter of programming style & good practice, it is better not to expose stuff more widely than it needs to be.

 

It helps to protect you from all sorts of weird bugs & maintenance nightmares ...

Top Tips:

  1. How to properly post source code - see: https://www.avrfreaks.net/comment... - also how to properly include images/pictures
  2. "Garbage" characters on a serial terminal are (almost?) invariably due to wrong baud rate - see: https://learn.sparkfun.com/tutorials/serial-communication
  3. Wrong baud rate is usually due to not running at the speed you thought; check by blinking a LED to see if you get the speed you expected
  4. Difference between a crystal, and a crystal oscillatorhttps://www.avrfreaks.net/comment...
  5. When your question is resolved, mark the solution: https://www.avrfreaks.net/comment...
  6. Beginner's "Getting Started" tips: https://www.avrfreaks.net/comment...
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

awneil wrote:

 

But, as a matter of programming style & good practice, it is better not to expose stuff more widely than it needs to be.

It helps to protect you from all sorts of weird bugs & maintenance nightmares ...

yeah I understand that is a good advice, however I am still facing the same problem with how to toggle the pin on correct timings to achieve my duty cycle, i am doing something wrong that's letting the signal take years to toggle and thats not what i intended it to do. any ideas what am I doing wrong?

Sherif Nassar

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

In the updated OP, sherifb96 wrote:

ISR(TIMER0_COMP_vect)
{
	count++;
	if(count <= dutyCycle)
	{
		PORTC = PORTC | (1<<PC0); //toggle led every 0.5 second
		//clear the tick counter again to count a new 0.5 second
	}
	else
		PORTC = PORTC & ~(1<<PC0);
}

There isn't any code which does what that comment says!

 

Please post a 'scope screenshot which shows a full cycle - or a couple of full cycles - of the output.

 

sherifb96 wrote:
it stays on for like years and then go down low for years also

Measure the actual times!

 

sherifb96 wrote:
I am initializing the timer with a frequency of 1MHz

Are you sure?

 

See Tip #3 ...

Top Tips:

  1. How to properly post source code - see: https://www.avrfreaks.net/comment... - also how to properly include images/pictures
  2. "Garbage" characters on a serial terminal are (almost?) invariably due to wrong baud rate - see: https://learn.sparkfun.com/tutorials/serial-communication
  3. Wrong baud rate is usually due to not running at the speed you thought; check by blinking a LED to see if you get the speed you expected
  4. Difference between a crystal, and a crystal oscillatorhttps://www.avrfreaks.net/comment...
  5. When your question is resolved, mark the solution: https://www.avrfreaks.net/comment...
  6. Beginner's "Getting Started" tips: https://www.avrfreaks.net/comment...
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

awneil wrote:

 

There isn't any code which does what that comment says!

sherifb96 wrote:
I am initializing the timer with a frequency of 1MHz

Are you sure?

 

See Tip #3 ...

 

Updated the comments to match the code. Yes I am sure the timer is initialized with FCPU/1024 which is 1MHz/1024.

how can i measure actual timings with digital oscillioscope on proteus? I cant get a screenshot of a whole period because the change from high level to low level takes ages that the screen cant even show.
 

Sherif Nassar

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

sherifb96 wrote:
well thank you it has been a great help and yes now I can fully control the duty cycle I want, but if you dont mind the question, is there a way to only use the CTC mode?
Not sure why you would need this but think about what actually happens in PWM. In each cycle there are important two points. One is the point at which the signal switches phase and one is the point where the entire cycle completes. The latter sets the frequency and hence the entire period of the wave and the other sets a variable switching point. So you need TWO events to define such a wave. That is done in my example code by OVF setting period/frequency and CTC setting switch over (duty) within it.

 

I suppose what you might do is use just CTC and when the comparison even occurs you then set the compare value again for the "other half" of the wave. So say you wanted a 40% wave with a period for the entire of counting to 100 you set the signal high and the compare to 40. When the compare event occurs you flip the signal low and set a new compare of 60. When that triggers the ISR you switch back to 40 and high and so on. The entire period would be 100 with 40 high and 60 low.

 

Note however that there is an overhead on the way into an ISR so you'd have to take account of the cycles on the way in each time - If your goal was 40/60 you might find it took 12 cycles to get into the ISR (on a good day!) so you would need to set 40-12 and 60-12 etc etc.

 

(it's not a very good way to do PWM!)

 

PS hope my post actually gives some useful information and does not clog this thread with irrelevant nonsense about your programming style or the way you quote posts - none of which really matter.

Last Edited: Wed. Feb 12, 2020 - 11:47 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

sherifb96 wrote:
Yes I am sure the timer is initialized with FCPU/1024 which is 1MHz/1024.

but how are you sure that FCPU is really 1MHz ?

 

how can i measure actual timings with digital oscillioscope on proteus?

That's a question for your Proteus documentation, or a Proteus forum ...

 

Top Tips:

  1. How to properly post source code - see: https://www.avrfreaks.net/comment... - also how to properly include images/pictures
  2. "Garbage" characters on a serial terminal are (almost?) invariably due to wrong baud rate - see: https://learn.sparkfun.com/tutorials/serial-communication
  3. Wrong baud rate is usually due to not running at the speed you thought; check by blinking a LED to see if you get the speed you expected
  4. Difference between a crystal, and a crystal oscillatorhttps://www.avrfreaks.net/comment...
  5. When your question is resolved, mark the solution: https://www.avrfreaks.net/comment...
  6. Beginner's "Getting Started" tips: https://www.avrfreaks.net/comment...
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

clawson wrote:

PS hope my post actually gives some useful information and does not clog this thread with irrelevant nonsense about your programming style or the way you quote posts - none of which really matter.

 

yeah well, took the words out of my mouth.

Sherif Nassar