• Welcome to our site! Electro Tech is an online community (with over 170,000 members) who enjoy talking about and building electronic circuits, projects and gadgets. To participate you need to register. Registration is free. Click here to register now.

Setting up timers on ATMega2560 to pulse a pin

riccardo

Member
Hello. Me again!

I'm trying to set up the four 16-bit timers of an ATMega2560 to make a pulse on four output pins (to step 4 motors at different speeds). At the moment I am just trying to make one work and not succeeding. I was hoping to use the OCnA function so that when my program is doing other things, the motor steps are not interfered with signigficantly.

Reading the datasheet about the different configurations and registers is making my head spin and I think I am just making some mistakes in the register config and inmplementation.

The code below compiled in the Arduino IDE is what I have so far. My intention is to have it set a pin (M1_STEP) high for some time, then low for a different amount.

C:
#define LED_M1_SIG 36
#define M1_STEP 11     

volatile boolean pinState = LOW;

ISR(TIMER1_COMPA_vect) {
  TCNT1 = 0;        // Reset timer
  pinState = !pinState;               // OC1A pin should have just toggled state, so record it here.
  if (pinState) {
    OCR1A = 100;     // Set pin low time
  } else {
    OCR1A =  1000;   // Set pin High time
  }
  digitalWrite(LED_M1_SIG,pinState);  // Light LED just to show interrupt is working
}


void setup() {
  pinMode(LED_M1_SIG, OUTPUT);
  pinMode(M1_STEP, OUTPUT);
  OCR1A = 1000;
  TCCR1A |= (1 << COM1A0) | (1 << WGM10) | (1 << WGM11) | (1 << WGM12) | (1 << WGM13);    // Toggle pin on compare match for OC1 only // also sets WGM (Datasheet p145)
  TCCR1B |= (1 << CS01) | (1 << CS00);                                                    // Set prescaler
  TIMSK1 |= (1 << OCIE1A);                                                                // Set interrupt on compare match
  sei(); // set global interrupts
}

void loop() {
  // put your main code here, to run repeatedly:
}
The LED lights up and I can see a sqare wave on the LED pin so It seems the interrupt part works. However....
The duty seems stuck on 50%
Nothing is happening on the OC1A pin

Any suggestions welcome.
 

DrG

Active Member
I'm seeing this now and it has been a few days since you posted. Where are you with the project? Do you understand more about PWM and CTC mode with the timers? Are you working on an ArduinoMega or a custom 2560 board?
 

riccardo

Member
I figured it out for the most part, although I have a bug where at certain frequencies, somehitng goes wrong.
It's a custom board. But I have an Arduino Mega on hand for testing

C:
void setup() {
// Set up OC1A
    TCCR1A = (1<<COM1A1) | (0<<COM1A0) | (1<<WGM11) | (0<<WGM10);        // Set Pin Low on compare match + WGM to Fast PM (Mode 14, p145 Datasheet)
    TCCR3A = (1<<COM3A1) | (0<<COM3A0) | (1<<WGM31) | (0<<WGM30);        // Set Pin Low on compare match + WGM to Fast PM (Mode 14, p145 Datasheet)
    TCCR4A = (1<<COM4A1) | (0<<COM4A0) | (1<<WGM41) | (0<<WGM40);        // Set Pin Low on compare match + WGM to Fast PM (Mode 14, p145 Datasheet)
    TCCR5A = (1<<COM5A1) | (0<<COM5A0) | (1<<WGM51) | (0<<WGM50);        // Set Pin Low on compare match + WGM to Fast PM (Mode 14, p145 Datasheet)
}


void SetPWM(unsigned long frequency, uint8_t percentage, uint16_t prescaler, uint8_t channel) {
    uint16_t resolution = F_CPU/((uint32_t)prescaler * frequency);
   
    switch (channel) {
        case 1:
            ICR1 = resolution - 1;
            OCR1A = (((uint32_t)percentage * resolution) / 100) - 1;
        break;
        case 2:
            ICR3 = resolution - 1;
            OCR3A = (((uint32_t)percentage * resolution) / 100) - 1;
        break;
        case 3:
            ICR4 = resolution - 1;
            OCR4A = (((uint32_t)percentage * resolution) / 100) - 1;
        break;
        case 4:
            ICR5 = resolution - 1;
            OCR5A = (((uint32_t)percentage * resolution) / 100) - 1;
        break;      
    } // end switch
} // ---


void SetOutputs() {
uint16_t prescalerUsed = 1;
           
            // Set up prescaler to accommodate mDelay size
            TIMSK1 &= ~ (1 << OCIE1A); // DISABLE interrupt on compare match
            TIMSK3 &= ~ (1 << OCIE3A); // DISABLE interrupt on compare match
            TIMSK4 &= ~ (1 << OCIE4A); // DISABLE interrupt on compare match
            TIMSK5 &= ~ (1 << OCIE5A); // DISABLE interrupt on compare match
       for (uint8_t mNo=1; mNo<=4; mNo++) {                    // For each motor        
            if (mDelay[mNo-1] / 1024 < 65535) {        // Max timer can hold
                if (mNo == 1) TCCR1B = (0<<ICNC1) | (0<<ICES1) | (1<<WGM13) | (1<<WGM12) | (1<<CS12) | (0<<CS11) | (1<<CS10); //1024
                if (mNo == 2) TCCR3B = (0<<ICNC3) | (0<<ICES3) | (1<<WGM33) | (1<<WGM32) | (1<<CS32) | (0<<CS31) | (1<<CS30); //1024
                if (mNo == 3) TCCR4B = (0<<ICNC4) | (0<<ICES4) | (1<<WGM43) | (1<<WGM42) | (1<<CS42) | (0<<CS41) | (1<<CS40); //1024
                if (mNo == 4) TCCR5B = (0<<ICNC5) | (0<<ICES5) | (1<<WGM53) | (1<<WGM52) | (1<<CS52) | (0<<CS51) | (1<<CS50); //1024
                prescalerUsed = 1024;
            }
            if (mDelay[mNo-1] / 256 < 65535) {
                if (mNo == 1) TCCR1B = (0<<ICNC1) | (0<<ICES1) | (1<<WGM13) | (1<<WGM12) | (1<<CS12) | (0<<CS11) | (0<<CS10); //256
                if (mNo == 2) TCCR3B = (0<<ICNC3) | (0<<ICES3) | (1<<WGM33) | (1<<WGM32) | (1<<CS32) | (0<<CS31) | (0<<CS30); //256
                if (mNo == 3) TCCR4B = (0<<ICNC4) | (0<<ICES4) | (1<<WGM43) | (1<<WGM42) | (1<<CS42) | (0<<CS41) | (0<<CS40); //256
                if (mNo == 4) TCCR5B = (0<<ICNC5) | (0<<ICES5) | (1<<WGM53) | (1<<WGM52) | (1<<CS52) | (0<<CS51) | (0<<CS50); //256
                prescalerUsed = 256;
            }
            if (mDelay[mNo-1] / 64 < 65535) {
                if (mNo == 1) TCCR1B = (0<<ICNC1) | (0<<ICES1) | (1<<WGM13) | (1<<WGM12) | (0<<CS12) | (1<<CS11) | (1<<CS10); //64
                if (mNo == 2) TCCR3B = (0<<ICNC3) | (0<<ICES3) | (1<<WGM33) | (1<<WGM32) | (0<<CS32) | (1<<CS31) | (1<<CS30); //64
                if (mNo == 3) TCCR4B = (0<<ICNC4) | (0<<ICES4) | (1<<WGM43) | (1<<WGM42) | (0<<CS42) | (1<<CS41) | (1<<CS40); //64
                if (mNo == 4) TCCR5B = (0<<ICNC5) | (0<<ICES5) | (1<<WGM53) | (1<<WGM52) | (0<<CS52) | (1<<CS51) | (1<<CS50); //64
                prescalerUsed = 64;
            }
            if (mDelay[mNo-1] / 8 < 65535) {
                if (mNo == 1) TCCR1B = (0<<ICNC1) | (0<<ICES1) | (1<<WGM13) | (1<<WGM12) | (0<<CS12) | (1<<CS11) | (1<<CS10); //8
                if (mNo == 2) TCCR3B = (0<<ICNC3) | (0<<ICES3) | (1<<WGM33) | (1<<WGM32) | (0<<CS32) | (1<<CS31) | (1<<CS30); //8
                if (mNo == 3) TCCR4B = (0<<ICNC4) | (0<<ICES4) | (1<<WGM43) | (1<<WGM42) | (0<<CS42) | (1<<CS41) | (1<<CS40); //8
                if (mNo == 4) TCCR5B = (0<<ICNC5) | (0<<ICES5) | (1<<WGM53) | (1<<WGM52) | (0<<CS52) | (1<<CS51) | (1<<CS50); //8
                prescalerUsed = 8;
            }
            if (mDelay[mNo-1]  < 65535) {
                if (mNo == 1) TCCR1B = (0<<ICNC1) | (0<<ICES1) | (1<<WGM13) | (1<<WGM12) | (0<<CS12) | (0<<CS11) | (1<<CS10); // none
                if (mNo == 2) TCCR3B = (0<<ICNC3) | (0<<ICES3) | (1<<WGM33) | (1<<WGM32) | (0<<CS32) | (0<<CS31) | (1<<CS30); // none
                if (mNo == 3) TCCR4B = (0<<ICNC4) | (0<<ICES4) | (1<<WGM43) | (1<<WGM42) | (0<<CS42) | (0<<CS41) | (1<<CS40); // none
                if (mNo == 4) TCCR5B = (0<<ICNC5) | (0<<ICES5) | (1<<WGM53) | (1<<WGM52) | (0<<CS52) | (0<<CS51) | (1<<CS50); // none
                prescalerUsed = 1;
            }
            // Set the PWM frequency if not at zero, otherwise stop the timer
            if (mDelay[mNo-1] > 0) {
                SetPWM(100000000 / mDelay[mNo-1], 50 , prescalerUsed, mNo);
            } else {
                if (mNo == 1) TCCR1B = (0<<ICNC1) | (0<<ICES1) | (1<<WGM13) | (1<<WGM12) | (0<<CS12) | (0<<CS11) | (0<<CS10); // stopped
                if (mNo == 2) TCCR3B = (0<<ICNC3) | (0<<ICES3) | (1<<WGM33) | (1<<WGM32) | (0<<CS32) | (0<<CS31) | (0<<CS30); // stopped
                if (mNo == 3) TCCR4B = (0<<ICNC4) | (0<<ICES4) | (1<<WGM43) | (1<<WGM42) | (0<<CS42) | (0<<CS41) | (0<<CS40); // stopped
                if (mNo == 4) TCCR5B = (0<<ICNC5) | (0<<ICES5) | (1<<WGM53) | (1<<WGM52) | (0<<CS52) | (0<<CS51) | (0<<CS50); // stopped  
            }
      } //end for,loop                         
            TIMSK1 |= (1 << OCIE1A);    //ENABLE interrupt on compare match
            TIMSK3 |= (1 << OCIE3A);    //ENABLE interrupt on compare match
            TIMSK4 |= (1 << OCIE4A);    //ENABLE interrupt on compare match
            TIMSK5 |= (1 << OCIE5A);    //ENABLE interrupt on compare match          
}
"mDelay" is an array containing the period in us for the timers
"mNo" is the channel to be used
So this basically works, but if mDelay is around 4120, suddenly the output goes to some high freqency. At other certain mDelay values near there, it will even lock up the MCU.
Now I assume I must be makig some divide by zero or something similar somewhere.
Also I think that the way I select the prescaler is not a good way. It feels a bit like a hack. I'd appriciate some suggestions on improving that.
 

DrG

Active Member
I guess you are getting there but that high frequency jump is, obviously, a problem and bizzare (until you track it down). I can't tell anything from a cursory glance at the code fragment.
 

wkrug

Active Member
Is it iportant that the pulses will given out in an fixed order?
Then I would use only one 16Bit Timer an set the needed cycles in the Comparematch Interrupt.

When only the duty Cycles are Importent You can it be running in Hardware without any Interrupt Handling.
Use 2x 16 Bit Timers and the According OCRxA an OCRxB Registers for setting the Duty Cycle.
The used Output Pins are fixed at the ATMEGA Controllers.
 

riccardo

Member
The timers are for providing a step signal to a motor driver IC. They don't need to be in sync, but I want them running independant of the main program. I've made a function to set up the timers (below). I also set up the interrupts just to flash an LED every 200 pulses.
The bug I was suffering before looks like it was due to a number getting too big for the uint16. I re-wrote the function so it works properly now.

C:
void InitTimers(void) {
    // Timer Configuration ----------------------------------------------------------------------------------------------------
    /*
    Using Timers 1,3,4,5 (16-bit)
    M1: OC1A
    M2: OC3A
    M3: OC4A
    M4: OC5A
    */
    // Set up OCnA (1,3,4,5)
    TCCR1A = (1<<COM1A1) | (0<<COM1A0) | (1<<WGM11) | (0<<WGM10);        // Set Pin Low on compare match + WGM to Fast PM (Mode 14, p145 Datasheet)
    TCCR3A = (1<<COM3A1) | (0<<COM3A0) | (1<<WGM31) | (0<<WGM30);        // Set Pin Low on compare match + WGM to Fast PM (Mode 14, p145 Datasheet)
    TCCR4A = (1<<COM4A1) | (0<<COM4A0) | (1<<WGM41) | (0<<WGM40);        // Set Pin Low on compare match + WGM to Fast PM (Mode 14, p145 Datasheet)
    TCCR5A = (1<<COM5A1) | (0<<COM5A0) | (1<<WGM51) | (0<<WGM50);        // Set Pin Low on compare match + WGM to Fast PM (Mode 14, p145 Datasheet)

     TCNT1 = 0;                    // Reset Timer
    TCNT3 = 0;                    // Reset Timer
    TCNT4 = 0;                    // Reset Timer
    TCNT5 = 0;                    // Reset Timer
    TIMSK1 &= ~(1 << OCIE1A);    // DISABLE interrupt on compare match
    TIMSK3 &= ~(1 << OCIE3A);    // DISABLE interrupt on compare match
    TIMSK4 &= ~(1 << OCIE4A);    // DISABLE interrupt on compare match
    TIMSK5 &= ~(1 << OCIE5A);    // DISABLE interrupt on compare match
    // ------------------------------------------------------------------------------------------------------------------------
}


void SetPWM(unsigned long period, uint16_t duty, uint8_t channel) {
    uint16_t periodInTiks = 1;
    uint16_t onTimeInTiks = 1;
    uint16_t prescaler = 1;
    
    // Calculate smallest possible prescaler
    if (period <= 65535) {        // if the period can fit in a 16-bit register
        prescaler = 1;                // No prescaler needed
    } else {                    // Else set up a prescaler
        if (period <= 65535 * 1024) prescaler = 1024;
        if (period <= 65535 * 256) prescaler = 256;
        if (period <= 65535 * 64) prescaler = 64;
        if (period <= 65535 * 8) prescaler = 8;
    }
    
    // Set the Prescaler registers
    switch (prescaler) {
        case 1024:
            if (channel == 1) TCCR1B = (0<<ICNC1) | (0<<ICES1) | (1<<WGM13) | (1<<WGM12) | (1<<CS12) | (0<<CS11) | (1<<CS10); //1024
            if (channel == 2) TCCR3B = (0<<ICNC3) | (0<<ICES3) | (1<<WGM33) | (1<<WGM32) | (1<<CS32) | (0<<CS31) | (1<<CS30); //1024
            if (channel == 3) TCCR4B = (0<<ICNC4) | (0<<ICES4) | (1<<WGM43) | (1<<WGM42) | (1<<CS42) | (0<<CS41) | (1<<CS40); //1024
            if (channel == 4) TCCR5B = (0<<ICNC5) | (0<<ICES5) | (1<<WGM53) | (1<<WGM52) | (1<<CS52) | (0<<CS51) | (1<<CS50); //1024
        break;
        case 256:
            if (channel == 1) TCCR1B = (0<<ICNC1) | (0<<ICES1) | (1<<WGM13) | (1<<WGM12) | (1<<CS12) | (0<<CS11) | (0<<CS10); //256
            if (channel == 2) TCCR3B = (0<<ICNC3) | (0<<ICES3) | (1<<WGM33) | (1<<WGM32) | (1<<CS32) | (0<<CS31) | (0<<CS30); //256
            if (channel == 3) TCCR4B = (0<<ICNC4) | (0<<ICES4) | (1<<WGM43) | (1<<WGM42) | (1<<CS42) | (0<<CS41) | (0<<CS40); //256
            if (channel == 4) TCCR5B = (0<<ICNC5) | (0<<ICES5) | (1<<WGM53) | (1<<WGM52) | (1<<CS52) | (0<<CS51) | (0<<CS50); //256
        break;
        case 64:
            if (channel == 1) TCCR1B = (0<<ICNC1) | (0<<ICES1) | (1<<WGM13) | (1<<WGM12) | (0<<CS12) | (1<<CS11) | (1<<CS10); //64
            if (channel == 2) TCCR3B = (0<<ICNC3) | (0<<ICES3) | (1<<WGM33) | (1<<WGM32) | (0<<CS32) | (1<<CS31) | (1<<CS30); //64
            if (channel == 3) TCCR4B = (0<<ICNC4) | (0<<ICES4) | (1<<WGM43) | (1<<WGM42) | (0<<CS42) | (1<<CS41) | (1<<CS40); //64
            if (channel == 4) TCCR5B = (0<<ICNC5) | (0<<ICES5) | (1<<WGM53) | (1<<WGM52) | (0<<CS52) | (1<<CS51) | (1<<CS50); //64
        break;
        case 8:
            if (channel == 1) TCCR1B = (0<<ICNC1) | (0<<ICES1) | (1<<WGM13) | (1<<WGM12) | (0<<CS12) | (1<<CS11) | (1<<CS10); //8
            if (channel == 2) TCCR3B = (0<<ICNC3) | (0<<ICES3) | (1<<WGM33) | (1<<WGM32) | (0<<CS32) | (1<<CS31) | (1<<CS30); //8
            if (channel == 3) TCCR4B = (0<<ICNC4) | (0<<ICES4) | (1<<WGM43) | (1<<WGM42) | (0<<CS42) | (1<<CS41) | (1<<CS40); //8
            if (channel == 4) TCCR5B = (0<<ICNC5) | (0<<ICES5) | (1<<WGM53) | (1<<WGM52) | (0<<CS52) | (1<<CS51) | (1<<CS50); //8
        break;
        default:
            if (channel == 1) TCCR1B = (0<<ICNC1) | (0<<ICES1) | (1<<WGM13) | (1<<WGM12) | (0<<CS12) | (0<<CS11) | (1<<CS10); // none
            if (channel == 2) TCCR3B = (0<<ICNC3) | (0<<ICES3) | (1<<WGM33) | (1<<WGM32) | (0<<CS32) | (0<<CS31) | (1<<CS30); // none
            if (channel == 3) TCCR4B = (0<<ICNC4) | (0<<ICES4) | (1<<WGM43) | (1<<WGM42) | (0<<CS42) | (0<<CS41) | (1<<CS40); // none
            if (channel == 4) TCCR5B = (0<<ICNC5) | (0<<ICES5) | (1<<WGM53) | (1<<WGM52) | (0<<CS52) | (0<<CS51) | (1<<CS50); // none
        break;
    }
    
    periodInTiks = (period / prescaler) - 1;
    unsigned long tempTime = ((duty * periodInTiks) / 100) - 1;    // done here so that the first multiplication doesn't overflow the 16 bits
    onTimeInTiks = (uint16_t)tempTime;

    // ICR1  = The period
    // OCR1A = On Time   
    switch (channel) {
        case 1:
            TIMSK1 &= ~ (1 << OCIE1A); // DISABLE interrupt on compare match
            ICR1 = periodInTiks;
            OCR1A = onTimeInTiks;
            TCNT1 = 0;                    // Reset Timer
            TIMSK1 |= (1 << OCIE1A);    //ENABLE interrupt on compare match
        break;
        case 2:
            TIMSK3 &= ~ (1 << OCIE3A); // DISABLE interrupt on compare match
            ICR3 = periodInTiks;
            OCR3A = onTimeInTiks;
            TCNT3 = 0;                    // Reset Timer
            TIMSK3 |= (1 << OCIE3A);    //ENABLE interrupt on compare match
        break;
        case 3:
            TIMSK4 &= ~ (1 << OCIE4A); // DISABLE interrupt on compare match
            ICR4 = periodInTiks;
            OCR4A = onTimeInTiks;
            TCNT4 = 0;                    // Reset Timer
            TIMSK4 |= (1 << OCIE4A);    //ENABLE interrupt on compare match
        break;
        case 4:
            TIMSK5 &= ~ (1 << OCIE5A); // DISABLE interrupt on compare match
            ICR5 = periodInTiks;
            OCR5A = onTimeInTiks;
            TCNT5 = 0;                    // Reset Timer
            TIMSK5 |= (1 << OCIE5A);    //ENABLE interrupt on compare match
        break;       
    } // end switch
} // end function
 

Latest threads

EE World Online Articles

Loading

 
Top