0

I wrote a simple program to generate PWM wave with 50% duty cycle. Then I went for debugging in AtmelStudio. All registers except OCR0 were assigned there respective values. Why OCR0 not assigned any value. ATmega32, Fast PWM.

#include <avr/io.h>
int main(void)
{
DDRB = (1 << PB3);
TCCR0 |= (1 << WGM01) | (1 << WGM00) | (1 << COM01);
OCR0 = 127;
TCCR0 |= (1 << CS02);
return 0;
}
Lundin
  • 195,001
  • 40
  • 254
  • 396
Ganesh S
  • 21
  • 1
  • 4
  • 2
    Where is your never ending loop? – unalignedmemoryaccess Oct 06 '17 at 06:10
  • What makes you think that your attempt to write a value did not succeed? Does the hardware specification describe the OCR0 register as readable? Is it perhaps write-only? – Yunnosch Oct 06 '17 at 06:31
  • @tilz0R Please elaborate. What is a never ending loop needed for? – Yunnosch Oct 06 '17 at 06:32
  • @Yunnosch Because this is a bare metal embedded system? – Lundin Oct 06 '17 at 06:34
  • @Yunnosch https://stackoverflow.com/questions/44429456/what-is-super-loop-in-embedded-c-programming-language/44429507#44429507 – unalignedmemoryaccess Oct 06 '17 at 06:36
  • Not having an endless loop is neither an obstacle for setting up a pwm for a fixed period, nor a reason why the value does not get into OCR0 (or for being unable to read it back). I do not see how this is influenced by being a bare metal embedded system. Please explain. I am aware that an endless loop is needed for anyhing useful and for making sure that the execution does not go amok through memory by the way. Thanks for the link. – Yunnosch Oct 06 '17 at 06:36
  • Could it be that you are reading out the wrong OCR0? That register is double buffered when PWM mode is active to ensure glitch free operation. – kolrabi Oct 06 '17 at 07:07
  • @tilz0R> on this platform, returning from `main()` causes the program to enter an infinite loop with empty body. – spectras Oct 06 '17 at 08:09
  • 1
    @Lundin> that very compiler (namely avr-gcc) uses `int main(void)` and `return0;` in its documentation. It does make sense on a microcontroller, it has whatever sense its C runtime library gives it. On large systems it's *"cleanup and terminate process with that error code"*, but a runtime library could perfectly define *"exit main with non-zero and I shall blink some red led furiously"*. – spectras Oct 06 '17 at 08:15
  • @spectras No, even though a particular compiler might support it, it is nonsense. Bare metal systems do not return from `main()`. Why would you want some useless overhead code for calling/returning to `main()` to crap up your RAM needlessly? RAM is valuable - sacrificing it to allow PC programmers to write crap code is a bad rationale. Instead, simply compile the code correctly. `gcc -ffreestanding`. – Lundin Oct 06 '17 at 08:42
  • Btw if the runtime environment starts to write to I/O ports out of its own initiative, it is horribly broken. Documented or not. – Lundin Oct 06 '17 at 08:44
  • @Lundin> the compiler for that platform explicitly discourages freestanding. And the libcrt does sensible things, namely it copies initial values into the ram, zeroes out the bss and installs interrupt handlers. Unless you want to do it yourself, there is no point using freestanding. And the return from main is implemented by a single two-byte instruction, that would be there anyway if you hand-coded a `for(;;) {}` yourself. – spectras Oct 06 '17 at 08:47
  • @spectras What the crt does has nothing to do with freestanding vs hosted, that code remains the same. Sensible implementations jump from the crt to `main()` without stacking anything. It may be that this particular implementation is some garage hobbyist one, but that's not an excuse to not learn proper embedded programming. – Lundin Oct 06 '17 at 08:50
  • @Lundin> it doesn't stack anything, nor does it need to. Init section is immediately followed by main, it doesn't even have to jump to it (actually it depends on whether you're using c++ and have static constructors to run). And in the generated binary, that return 0 within main is compiled to `jmp -2` (again, unless you're using c++ and have destructors to run). Those garage hobbyist apparently know how their compiler works. Working on embedded systems is not an excuse not to use proper compiler optimizations. – spectras Oct 06 '17 at 09:19
  • 1
    Don't get me wrong, I don't like seeing that either, even if I do want to freeze the device I'd rather put it in standby/sleep mode. I simply wanted to point out it is valid and it makes sense as long as the platform documentation says so. And it generates the exact same binary you would with a manual `for(;;) {}`. So there is no reason to call him out on that. Actually there is one thing it does better when using c++: it invokes destructors of objects in main()'s scope, which the manual infinite loop does not do. – spectras Oct 06 '17 at 09:32

1 Answers1

1

So anyway.

You're using the 8-bit counter0 on your Atmega32. Let's see how you set it up:

// Set Pin B3 as output, others as input
DDRB = (1 << PB3);

// Set Clear on compare match + Fast PWM mode + Counter stopped
TCCR0 |= (1 << WGM01) | (1 << WGM00) | (1 << COM01);

// Set comparator value to 127
OCR0 = 127;

// Enable clkIO/256 from prescaler, turning on the counter
TCCR0 |= (1 << CS02);

Okay. First, a few things:

  • On initial setup, you usually want to assign the value and not or it, to be certain of its state.
  • Even after, setting it instead of or-ing it avoids a useless read. No impact on behavior for this register, but might be a bit better for performance.
  • The documentation recommends only enabling the output after you have set it up properly, to avoid spurious output. So you should move the first line last.

I'll be reading from that version of the datasheet.

Now, in fast PWM mode, according to table 38, and 40, the counter behaves like this:

  • It counts from BOTTOM to MAX (0 to 0xFF).
  • OCR0 is only used to toggle OC0 pin, not to reset the counting.
  • OCR0 has double-buffering. Its actual value is not updated until next cycle.

This might be your issue. If any of those are true:

  • Counter doesn't start properly (could happen if CS2-0 are not correct due to or-ing them instead of setting them).
  • Counter is stopped early (because your program ends and if the studio detects it, it could disable it at that point - I d'ont use the studio so I cannot really tell).

Then it is possible the value you write to the double buffer never makes it to the actual register. Alas the datasheet doesn't explain in detail how this is handled. Nor does it tell whether reading OCR0 while double buffering is active returns current value or awaiting value.

spectras
  • 13,105
  • 2
  • 31
  • 53