5

I'm trying to enable a PWM on an Arduino Mega (ATmega2560), but I'm facing to some issues.

First, I'm trying to program this in Ada. I desire to use the three Timer3 channels with FastPWM, so I wrote

procedure Main is
begin

   -- Nullify Timer3 buffers
   TCCR3A := 0;
   TCCR3B := 0;
   TCCR3C := 0;

   -- Waveform Generation Mode
   --  Fast PW, 8-bit, TOP = 0x00FF, Update OCR3x at BOTTOM, TOV3 Flag Set on TOP
   --  => WGM33|WGM32|WGM31|WGM30 = 0|1|0|1
   TCCR3A := TCCR3A or TCCR3A_WGM30;
   TCCR3B := TCCR3B or TCCR3B_WGM32;

   -- Compare Output Mode:
   --  Fast PWM, non-inverting mode
   --  => COM3A1|COM3A0|COM3B1|COM3B0|COM3C1|COM3C0 = 1|0|1|0|1|0
   TCCR3A := TCCR3A or TCCR3A_COM3A1 or TCCR3A_COM3B1 or TCCR3A_COM3C1;

   -- Clock Select: clk/1024 => CS32|CS31|CS30 = 1|1|1
   TCCR3B := TCCR3B or TCCR3B_CS32 or TCCR3B_CS31 or TCCR3B_CS30;

   -- Set Timer3 pins as output :
   -- Channel A : Digital Pin 5 / Chip Pin 5 (PE3/OC3A/AIN1)
   -- Channel B : Digital Pin 2 / Chip Pin 6 (PE4/OC3B/INT4)
   -- Channel C : Digital Pin 3 / Chip Pin 7 (PE5/OC3C/INT5)
   DDRE := DDRE_DDE3 or DDRE_DDE4 or DDRE_DDE5;

   OCR3AH := 0;
   OCR3AL := 250;

   OCR3BH := 0;
   OCR3BL := 250;

   OCR3CH := 0;
   OCR3CL := 250;

end Main;

The hardware connections are OK; I tested it using simple code on the Arduino IDE. So for me it's very clear that the code is lacking something or making something wrong, and this should be caused by an issue in PWM initialization. Could someone explain me where did I make such a mistake?

Thanks in advance.

Update

If Ada can be difficult to get the whole logic, the equivalent code in C is (you can build it using AS6, the result is the same, i.e., no signal is generated):

int main(void){
    TCCR3A = 0;
    TCCR3B = 0;
    TCCR3C = 0;

    /* Waveform Generation Mode
    Fast PW, 8-bit, TOP = 0x00FF, Update OCR3x at BOTTOM, TOV3 Flag Set on TOP
    => WGM33|WGM32|WGM31|WGM30 = 0|1|0|1 */
    TCCR3A = TCCR3A|(1<<WGM30);
    TCCR3B = TCCR3B|(1<<WGM32);

    /* Compare Output Mode:
    Fast PWM, non-inverting mode
    => COM3A1|COM3A0|COM3B1|COM3B0|COM3C1|COM3C0 = 1|0|1|0|1|0*/
    TCCR3A = TCCR3A|(1<<COM3A1)|(1<<COM3B1)|(1<<COM3C1);

    /* Clock Select: clk/1024 => CS32|CS31|CS30 = 1|1|1 */
    TCCR3B = TCCR3B|(1<<CS32)|(1<<CS31)|(1<<CS30);

    /* Set Timer3 pins as output :
    Channel A : Digital Pin 5 / Chip Pin 5 (PE3/OC3A/AIN1)
    Channel B : Digital Pin 2 / Chip Pin 6 (PE4/OC3B/INT4)
    Channel C : Digital Pin 3 / Chip Pin 7 (PE5/OC3C/INT5)*/
    DDRE = DDRE|(1<<DDE3)|(1<<DDE4)|(1<<DDE5);

    /* Set PWM Duty Cycles */
    OCR3AH = 0;
    OCR3AL = 250;

    OCR3BH = 0;
    OCR3BL = 250;

    OCR3CH = 0;
    OCR3CL = 250;
}
iama
  • 103
  • 7
Rego
  • 1,118
  • 1
  • 18
  • 40
  • 1
    This might be a bit too localized to receive an answer here. Particularly without knowing what those variables of yours are tied to. Perhaps your vendor would be more helpful. – T.E.D. Sep 20 '12 at 13:38
  • 1
    BTW: I do hope you put the fire out before posting this. :-) – T.E.D. Sep 20 '12 at 13:38
  • 1
    Maybe this should be migrated to http://electronics.stackexchange.com/ – Gerald Schneider Sep 21 '12 at 19:58
  • 1
    @T.E.D., the names of the registers are the same of the ones used in C, just the syntax is different, so the variables used here are no different from the ones used in C with WinAVR or AVR Studio. For example, if I set DDRE := DDRE_DDE2 in Ada it just means in C that I'm doing DDRE = 0b00000200. The C libraries in AVR Studio also use named variables like DDE2. – Rego Sep 22 '12 at 02:45
  • 1
    @T.E.D. lol yes, as soon as it started. – Rego Sep 22 '12 at 02:47
  • @GeraldSchneider, I don't think electronics.stackexchange.com treats software questions. And I use to read several micro-controllers software questions like this in SO almost every day. – Rego Sep 22 '12 at 02:51
  • @Rego It'll be clearer if you post the code for just one channel. Anyway, I didn't check your code as I dunno whether you've solved the problem. Let me know if you still couldn't figure it out.. – Anubis Jan 02 '13 at 06:28

4 Answers4

1

Looking at www.atmel.com/Images/doc2549.pdf page 136, I see a note:

"The Power Reduction Timer/Counter3 bit, PRTIM3, in “PRR1 – Power Reduction Register 1” on page 57 must be written to zero to enable Timer/Counter3 module."

May be relevant?

I don't know what the defaults are for the power management regs, and I haven't used the 2560.

The rest looks OK to me, though I have used the timer libraries avr-timer0.adb etc on the 328p instead of rolling my own.

1

I'm not sure, but if you forget the endless loop at the end of your main function, the program stops, all interrupts will be disabled and the controllers enters an empty loop.

And without interrupts no PWM.

int main(void){
....
while (1) {
}
return 0;
}
Maik
  • 33
  • 1
  • 8
  • Actually the timers only use interrupts to notify software. They can run a steady PWM signal with no further instruction. – Yann Vernier Jul 29 '17 at 12:03
1

You need to set SEI flag in SREG to enable interrupts and add endless loop in main. Avr programs doesn't terminate or hang after reaching end of main function, but start it all over again. http://www.atmel.com/webdoc/avrassembler/avrassembler.wb_SEI.html

Hubert Bossy
  • 140
  • 1
  • 11
1

One observation pops out at once -- almost sure this isn't right:

--  => COM3A1|COM3A0|COM3B1|COM3B0|COM3C1|COM3C0 = 1|0|1|0|1|0
TCCR3A := TCCR3A or TCCR3A_COM3A1 or TCCR3A_COM3B1 or TCCR3A_COM3C1;

Why? Because the named constants like COM3A0 are just aliases for integers which indicate bit position (0 to 7). In C, when we set these we do something like:

REGISTER |= (1<<COM3A0);

and COM3A0 = 3, then it becomes (1<<3), or 8 that is ORed.

Which shifts a 1 the number of places leftward before ORing it with the current register value. So unless your COM3A0 for example is already bit-shifted, all this statement is doing is ORing several ones and zeroes and assigning the single result to TCCR3A.

TomServo
  • 7,248
  • 5
  • 30
  • 47