Electronic – Controlling Multiple ESCs with ATMEGA128 PWM

avrmotor controllerpwm

So i've been working on this Quadcopter for a while, finally got it all built and put together. Trying to write some code to control the ESC (RedBrick HobbyKing 30A with UBEC). I managed to arm an ESC according to the documentation and program it using 50Hz Fast PWM on an ATMEGA128, controlling one motor.

The problem i'm having now is that when I try to use multiple motors, im having issues, they all seem to arm properly, its just when i try to give it power, its failing. Either one motor will start spinning while the other keeps beeping or vice versa. Sometimes neither will run. I attempted to fix this issue by using an interrupt that will fire when the ICR1 is reached (TCCR1A). Take a look at the code for more details.

PS. I would like to understand the logic behind this problem, why its occurring and what are some common solutions to it. Keep in mind, im new to the AVR programming so generic terminology is preferred. Thanks!

#include <avr/io.h>
#include <avr/interrupt.h>
#include <util/delay.h>

#define ESC_LOW             1000    //low duty cycle in milliseconds
#define ESC_HIGH            2000    //high duty cycle in milliseconds
#define motors_ddr          DDRB    //the data direction register the motors will operate on
#define motors_port         PORTB   //the port the motors will operate on
#define north_motor         4       //pin number for this motor
#define south_motor         5       //pin number for this motor
#define east_motor          6       //pin number for this motor
#define west_motor          7       //pin number for this motor


void init_ESCs(void) {

    //set all motors pins to output
    //motors_ddr |= 1<<north_motor | 1<<south_motor | 1<<east_motor | 1<<west_motor;
    motors_ddr = 0xFF;
    /*
        Set the Waveform Generation Mode to Fast PWM
        Set WGM11, WGM12 & WGM13 to select Fast PWM
        Prescaler: 8 (set the CS11 bit)
        Set OCIE1A bit in TIMSK to enable the Output Compare Interrupt Enabled Register
    */
    TCCR1A |= 1<<WGM11;
    TCCR1B |= 1<<WGM13 | 1<<WGM12 | 1<<CS11;
    TIMSK |= 1<<OCIE1A;

    //my CPU is at 16MHz
    //Set the TOP i.e. Top = [ cpu_clk_speed Hz / (Prescaler) * (Frequency Hz) ] - 1
    ICR1 = 39999;

    //how many times to repeat the signal
    volatile int armCount = 170;
    volatile int confirmCount = 150;

    //this loop sends the 2ms signal
    while(armCount > 0) {

            if(TCNT1 >= ESC_HIGH && bit_is_set(motors_port, north_motor) && bit_is_set(motors_port, south_motor) && bit_is_set(motors_port, east_motor) && bit_is_set(motors_port, west_motor)) {
                armCount--;
                motors_port &= ~(1<<north_motor | 1<<south_motor | 1<<east_motor | 1<<west_motor);
                //motors_port &= 0b11110000; //set motor PINS LOW
            }
    }

    //wait a while before confirming the Arming process (dunno if this is necessary)
    _delay_ms(500);

    //this loop sends the 1ms signal completing the arming
    while(confirmCount > 0) {
        if(TCNT1 >= ESC_LOW && bit_is_set(motors_port, north_motor) && bit_is_set(motors_port, south_motor) && bit_is_set(motors_port, east_motor) && bit_is_set(motors_port, west_motor)) {

            confirmCount--;
            //motors_port &= 0b11110000; //set motor PINS LOW
            motors_port &= ~(1<<north_motor | 1<<south_motor | 1<<east_motor | 1<<west_motor);
        }
    }
}


void speed_test(int speed) {
    TCCR1A |= 1<<WGM11;
    TCCR1B |= 1<<WGM13 | 1<<WGM12 | 1<<CS11;
    TIMSK |= 1<<OCIE1A;

    ICR1 = 39999;

    while(1) {
        if(TCNT1 >= speed && bit_is_set(motors_port, north_motor) && bit_is_set(motors_port, south_motor) && bit_is_set(motors_port, east_motor) && bit_is_set(motors_port, west_motor)) {

            //motors_port &= 0b11110000; //set motor PINS LOW
            motors_port &= ~(1<<north_motor | 1<<south_motor | 1<<east_motor | 1<<west_motor);
        }
    }   
}

int main(void) {

    //set enabled interrupt bit
    sei();

    init_ESCs();

    //speed_test(1500); //1.5ms signal

    while(1) {

    }   
}

ISR (TIMER1_COMPA_vect) {
    //set all motor pins to HIGH
    motors_port |= 1<<north_motor | 1<<south_motor | 1<<east_motor | 1<<west_motor; 
}

Best Answer

I discovered what the problem was. I was making 2 mistakes. The first was, not using an appropriate power supply. One motor would work fine but as soon as the others kicked in, there just wasnt enough power. The second problem was I was using interrupts when I should have been taking advantage of the 4 PWM channels on my atmega128. Also I wasnt enabling ALL 4 OCR channels, I forgot to turn them all on.

So if you are having similar issues dont forget to use an appropriate voltage source and make sure you are setting up and using the PWM channels correctly.