Not Able to Get PWM at 0V or Vcc

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

So I've read of others having trouble getting the PWM output to go "fully off" (0V) or "fully on" (Vcc). I read about using PWM, Phase Correct mode to overcome this but I haven't been able to get 0V or Vcc as one of the outputs, I always see a glitch on the scope. I can cycle through some different PWN duty cycles so the basic code works I just need a modes that are fully off and fully on.

 

void pwm_setup (void)
{
	// Set Timer 0 prescaler to clock/8.
	// At 8.0 MHz this is 1.0 MHz.
	// See ATtiny20 datasheet, Table 11.9.
	TCCR0B |= (1 << CS01);
	
	// Set to 'Fast PWM' mode
	//TCCR0A |= (1 << WGM01) | (1 << WGM00);

	// Set to 'PWM Phase Correct' mode
	TCCR0A |= (1 << WGM02) | (1 << WGM00);
	
	// Clear OC0B output on compare match, upwards counting.
	TCCR0A |= (1 << COM0B1);
}

void pwm_write (int val)
{
	OCR0B = val;  //LED Control signal on PA7
}

and my while loop looks like this:

	while (1)
	{

		if (get_key_short( 1<<KEY1 )){
			LED_GREEN_SWAP;
			step+=85;
			if (step == 255)
			{
				//pwm_write(0);
				//LED_LIGHT_OFF;
				LED_RED_ON;
			}
			else {
				pwm_write(step);
				LED_RED_OFF;
			}
		}

		if (get_key_long( 1<<KEY1 )){
			LED_RED_SWAP;
		}

				
	}// End while

As you can see in the code I've tried different modes and tests without any luck yet. 

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

The problem is that there are 257 combinations you try to cover with 8 bit !

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0
	TCCR0A |= (1 << WGM02) | (1 << WGM00);

Nope. The very same error as in your other thread.

 

GeorgeIoak wrote:
I always see a glitch on the scope.
Because you haven't really selected the phase correct mode.

Edit: last comment is wrong. The accidentally selected mode is Phase Correct too.

Stefan Ernst

Last Edited: Fri. Jun 30, 2017 - 03:40 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Sorry, I'm on Holiday and don't have a hard copy of the datasheet. Trying to scroll through the datasheet on a 13" screen is getting the best of me! Of course I had to copy and paste the wrong code into this program..

 

This should be 

// Set to 'PWM Phase Correct' mode
TCCR0A |= (1 << WGM00);
TCCR0B |= (1 << WGM02);

When I set that I'm not able to cycle through different PWM duty cycles. When I power up the board the pin attached to OC0B starts at Vcc and then on the first button push it goes to 0V and stays there with every button push. I tried changing my while routine to this without any luck:

if (get_key_short( 1<<KEY1 )){
			LED_GREEN_SWAP;
			step+=85;
			if (step == 255)
			{
				pwm_write(step);
				LED_RED_ON;
				step = 0;
			}
			else {
				pwm_write(step);
				LED_RED_OFF;
			}
		}

 

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

GeorgeIoak wrote:
with every button push

 

Are you "debouncing" your button? 

 

If not you may be getting multiple counts due to button bounce.

 

 

Jim

 

 

(Possum Lodge oath) Quando omni flunkus, moritati.

"I thought growing old would take longer"

 

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

GeorgeIoak wrote:

So I've read of others having trouble getting the PWM output to go "fully off" (0V) or "fully on" (Vcc). 

 

Any 8 bit PWM cannot usually do both 0% and 100% cases in a direct manner.

Usually for PWM one end always has a pulse, so for static at both ends, you need special code that takes the special case, and disables the PWM and forces the pin.

That means other code branch has to re-enable the PWM and disable the direct pin drive.

Some tests may be needed to de-glitch this mode change.

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

If you absolutely have to have 0V and Vcc, for 0, disconnect the PWM and set the GPIO pin to logic low and for Vcc, disconnect the PWM and set the GPIO pin to logic high.

 

Jim

 

Until Black Lives Matter, we do not have "All Lives Matter"!

 

 

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

Yes, I'm using the "Danni" routine that works well

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

I had thought that this would have been a common situation yet I really didn't find it discussed as I had expected.

 

I must have done something wrong because from what I read I should be able to set COM0B0 and COM0B1 to 0 to disconnect the pin from the timer. From there I thought a standard pin toggling would work but it didn't.

 

Is there something else I need to do and are there any hints for glitch free operation?

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

output to go "fully off" (0V) or "fully on" (Vcc).

always see a glitch on the scope

 I just need a modes that are fully off and fully on.

 

Maybe I should just stay out of this one, but I'm not sure I understand the actual problem.

 

Is the problem, "fully on / fully off", about the actual voltage level when the PWM is at 0 or 100 %?

As a logic gate output, the output voltage doesn't go to 0.0 volts, nor does it go to 5.0 volts.

There is always a transistor junction or two voltage drop, preventing actual rail-to-rail output voltage swings.

Particularly for some systems that truly need 0.0 V at the low end this can be problematic.

 

Is the problem that at 0% there is a 1 pulse width wide periodic pulse on the output?

Depending upon which specific micro you are using, and which PWM module, that is a "feature" of the hardware.

Zero doesn't mean Zero...

If you are using a chip with this "feature", then you need to test for setting the output to 0 %, and either switch back to a digital I/O pin without PWM and set the pin low, or simply invert the output and set the output to 100%, (Full On, inverted, equals fully off, without the extraneous pulses).

I don't know why this "feature" was never fixed, years ago, by Atmel.

It IS in the data sheets for the chips.

This "feature", (unless corrected, as mentioned above), will also elevate the output voltage if one feeds the PWM through an RC filter, etc., even slightly higher than the baseline hardware minimal output voltage.

 

Is the "Glitch" a narrow noise spike on the PWM output transition points?

What else is connected to the board, anything inductive?

Does the glitch exist with a simple resistive load, (10K resistor, for example), or with a resistor & LED on the output?

Often these glitches are artifactual, and are a function of the O'scope probe, the Ground connections, etc.

They aren't worth worrying about.

(True reverse EMF "Glitches" on inductive loads are a separate issue.)

Based upon what the PWM output is driving the glitches just usually aren't an issue.

If you state what the output will be used for, and what it will be driving, one might provide a better answer in this regard.

 

Make sure, of course, that you have as wide of Ground tracks as possible, or a full Ground plane on the PCB, and proper By-Pass caps on every Vcc and AVcc / Ground pin pairs; and a properly filtered power supply.

 

JC

 

 

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

I've done a bunch of experimenting so I'll need to go back and measure and capture to share exactly what I saw. Right now I've messed something up because in my while loop I change the variable "uint_8 step" in increments of 85 and verify that by sending step to a software UART. The pwm_write function just sets OCR0B to the step value yet I'm not getting any dimming. What happens is:

  1. Apply power and LEDs are ON and scope show full Vcc with no glitches on PA7/OC0B
  2. I press the button and I see 0V with no glitches on PA7/OC0B
  3. I press the button again and it stays at 0V on PA7/OC0B yet I can see the step value change reading the UART

 

Here is the current code:

void pwm_setup (void)
{
	// Set Timer 0 prescaler to clock/8.
	// At 8.0 MHz this is 1.0 MHz.
	// See ATtiny20 datasheet, Table 11.9.
	TCCR0B |= (1 << CS01);
	
	// Set to 'Fast PWM' mode
	//TCCR0A |= (1 << WGM01) | (1 << WGM00);
	//TCCR0A |= (1 << WGM02) | (1 << WGM01) | (1 << WGM00);

	// Set to 'PWM Phase Correct' mode 5
	TCCR0A |= (1 << WGM00);
	TCCR0B |= (1 << WGM02);
	
	// Clear OC0B output on compare match, upwards counting.
	TCCR0A |= (1 << COM0B1); //for phase correct it's down counting
}

void pwm_write (int val)
{
	OCR0B = val;  //LED Control signal on PA7
}

...

while (1)
	{

		if (get_key_short( 1<<KEY1 )){
			LED_GREEN_SWAP;
			step+=85;
			byte_to_usart_in_decimal(step);
			byte_to_usart_in_decimal(9); //End of xfer flag for step
			if (step == 255)
			{
				pwm_write(step);
				//TCCR0A &= ~(1 << COM0B1) | ~(1 << COM0B0);
				//LED_LIGHT_ON;
				LED_RED_ON;
				step = 0;
			}
			else {
				//TCCR0A |= (1 << COM0B1);
				pwm_write(step);
				LED_RED_OFF;
			}
		}

		if (get_key_long( 1<<KEY1 )){
			LED_RED_SWAP;
		}
	}// End while

I mostly need 0V on PA7 because it is connected to a LED driver and 0V turns the driver OFF. It needs to be fully OFF since this runs on battery. Full Vcc at "full brightness" is ideal but obviously small glitches are not going to be seen by the user since we're running at a higher PWM frequency than the eye can discern.

 

I hope that helps clarify my goal.

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

If you select a timer mode where OCR0A defines TOP, then it might be a good idea to set OCR0A to something different than 0.

Stefan Ernst

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

Yes, I tried changing the pwm_write to:

OCR0A = val;  //LED Control signal on PA7

instead of OCR0B but I get the same results. To be honest, it was a little confusing that if I want to control the pin connected to OC0B i still use the OCR0A register but everything else has A/B control. 

 

So assuming that changing OCR0A is correct, I still am not getting a PWM signal on PA7 which I can't find out why. I'll need to go back and double check but I think this happened when I changed from Fast PWM mode to Phase Correct mode because I know I can dim the LEDs.

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

GeorgeIoak wrote:
instead of OCR0B
Instead of? You have to set both. With the timer mode you selected OCR0A is TOP of the timer, so basically defines frequency and resolution of the PWM. And OCR0B is the duty cycle, OCR0B = 0 = fully off, OCR0B = OCR0A = fully on.

Stefan Ernst

Last Edited: Sat. Jul 1, 2017 - 05:07 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Ah, the light bulb was just coming on when you wrote this! So for clarification, if I want different LED Brightness levels

 

  1. OFF (0V), OCR0B=0
  2. DIM Level 1, OCR0A=100, OCR0B=85
  3. DIM Level 2, OCR0A=100, OCR0B=170
  4. ON (Vcc), OCR0A=255, OCR0B=255

 

I don't think the actual frequency matters too much, the datasheet of the driver just says:

1. Using a PWM Signal to EN Pin With the PWM signal applied to the EN pin, the AP5724 is turned on or off by the PWM signal. The LEDs operate at either zero or full current. The average LED current increases proportionally with the duty cycle of the PWM signal. A 0% duty cycle will turn off the AP5724 and corresponds to zero LED current. A 100% duty cycle corresponds to full current. The typical frequency range of the PWM signal is below 2 kHz. 

 

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

You still don't get what TOP actually means. How about reading the data sheet again about the descriptions of the different timer modes?

Stefan Ernst

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

An important note: logic output will NEVER be precisely 0V or precisely Vcc unless (1) its CMOS (Atmel microcontrollers are) AND (2) there is NO LOAD. Specifically, "load" includes an RC averager. A good digital voltmeter should be close enough to infinite input resistance on low scales that you should be able to ignore its loading; not so with a "moving needle" meter. This applies if you try to measure the pin voltage at the pin with a load connected, because the potential voltage drops are inside the micro.

 

This effect is in ADDITION to imperfections due to duty cycle that is not precisely 0% and not precisely 100%. Thus, you will see these voltage drops even if you set the logic state of an output pin in software to a specific logic level (eg, no PWM). 

 

Jim

 

Until Black Lives Matter, we do not have "All Lives Matter"!

 

 

Last Edited: Sat. Jul 1, 2017 - 05:39 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I had to run some experiments to try and get a better grasp on this. It's still not crystal clear to me so I'll list my test results so perhaps this will help the next confused person.

 

I ran some experiments where I set OCR0A to fixed value outside of the while loop, like this:

    OCR0B = 0;
	OCR0A = 255;

	while (1)
	{

		if (get_key_short( 1<<KEY1 )){
			LED_GREEN_SWAP;
			step+=85;
			byte_to_usart_in_decimal(step);
			byte_to_usart_in_decimal(9); //End of xfer for step
			if (step == 255)
			{
				pwm_write(step);
				//TCCR0A &= ~(1 << COM0B1) | ~(1 << COM0B0);
				//LED_LIGHT_ON;
				LED_RED_ON;
				step = 0;
			}
			else {
				//TCCR0A |= (1 << COM0B1);
				pwm_write(step);
				LED_RED_OFF;
			}
		}

		if (get_key_long( 1<<KEY1 )){
			LED_RED_ON;
			pwm_write(0);
		}
	}// End while

Then I would power up the board and measure the signal on PA7/OC0B. Once I cycled through the button presses I went back and changed OCR0A and ran the experiment again:

 

Phase Correct PWM 7 OC0A/OC0B
  OCR0A=0 OCR0A=100 OCR0A=200 OCR0A=255
Power Up Vcc 0V 0V 0V
OCR0B=85 0V 1.38ms/1.62ms 1.38ms/3.25ms 1.38ms/4.14ms
OCR0B=170 0V 0V 2.76ms/3.25ms 2.76ms/4.14ms
OCR0B=255 0V 0V 0V Vcc

 

Where the first number is the positive pulse width and the 2nd number is the period. Since I'm using Mode 5 the timer will count up to OCR0A and the larger the value the longer the period.

OCR0B changes the duty cycle, or the amount of ON time. Since OCR0B starts at 0 then when I set OCR0A=0 the board will power up with OCRA0=OCR0B which 100% duty cycle or FULL ON. When OCR0B=0 you have 0 amount of time high or 0 duty cycle so you are FULL OFF.

 

One thing that I cannot explain is when running my program is I don't reset the "step" variable to 0 I would see random numbers sent over the test serial port. They would be close to 85/170/255 but not those values so I would never get a step value of 0, why is that? Right now my code is working perfectly since I can't cycle through a OCR0B=0 state.

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

Try: if(step >=255)

MG

I don't know why I'm still doing this hobby

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

If I have step defined as

uint8_t step = 0;

how could it be >255? I know step==255 works because I have code in that IF statement that sets a LED. But I think that only happened the first time. I do know that if I don't reset step to 0 then I start getting odd values instead of just increments of 85 which confuses me.

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

GeorgeIoak wrote:
I do know that if I don't reset step to 0 then I start getting odd values instead of just increments of 85 which confuses me.
It confuses you? So you did expect to get the same result from 255+85 as from 0+85?

255 + 85 = 84 (within uint8_t)

 

Stefan Ernst

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

Sorry, my brain isn't working yet this morning! I had thought I had a little trick to cycle through different levels without relying on case statements. Since I'm obviously not the best programmer do you have a suggestion on the best method to achieve different PWM signals?

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

I tried 2 different methods. This isn't the most elegant but ti seems to work during my testing. I tried 2 different methods, one incrementing the brightness by a fixed amount and the using a array and stepping through the elements. The array method uses more memory but it allows for easy changes to brightness levels and steps if needed later. Here's the array method:

 

// Start with LEDs OFF
	LED_RED_OFF;
	LED_GREEN_OFF;
	LED_LIGHT_OFF;
	int16_t step = 0;
	uint16_t brightness[4] = {0,85,170,255};

	sei(); //  Enable global interrupts
	OCR0B = 0; // Controls the duty cycle
	OCR0A = 255; //Sets the total PWM cycle

	while (1)
	{
		if (get_key_short( 1<<KEY1 )){
			LED_GREEN_SWAP;
			step++;
			byte_to_usart_in_decimal(step);
			byte_to_usart_in_decimal(9); //End of xfer for step
			if (step == 4)
			{
				step = 0;
				pwm_write(brightness[step]);
				LED_RED_ON;
			}
			else {
				pwm_write(brightness[step]);
				LED_RED_OFF;
			}
		}

		if (get_key_long( 1<<KEY1 )){
			LED_RED_ON;
			pwm_write(0);
			step = 0;
		}
	}// End while

and here's the step method:

// Start with LEDs OFF
	LED_RED_OFF;
	LED_GREEN_OFF;
	LED_LIGHT_OFF;
	int16_t step = 0;

	sei(); //  Enable global interrupts
	OCR0B = 0; // Controls the duty cycle
	OCR0A = 255; //Sets the total PWM cycle

	while (1)
	{
		if (get_key_short( 1<<KEY1 )){
			LED_GREEN_SWAP;
			step+=64;
			byte_to_usart_in_decimal(step);
			byte_to_usart_in_decimal(9); //End of xfer for step
			if (step == 256)
			{
				pwm_write(255);
				LED_RED_ON;
				step = -64;
			}
			else {
				pwm_write(step);
				LED_RED_OFF;
			}
		}

		if (get_key_long( 1<<KEY1 )){
			LED_RED_ON;
			pwm_write(0);
			step = 0;
		}
	}// End while

This doesn't take into account the logarithmic nature of the brightness perceived by the eye but that could be easily tuned with the array method if needed.

 

Also, since I'm not changing the frequency of the PWM and I'm setting OCR0A=255 I can use Method 1 (PWM, Phase Correct, Top=0xFF.

 

So it appears that this portion of the code is solved and I'm on to interfacing with the battery charger and implementing power saving methods and wake triggers.