Запуск бесколлекторного двигателя от hdd.

DevAlone
Offline
Зарегистрирован: 06.08.2016

Пробую раскрутить двигатель от жёсткого диска с помощью arduino. После долгих попыток оно закрутилось, но очень медленно, при разгоне дальше ротор стопорится на одном месте. Схема вот такая, только для всех трёх обмоток (извиняюсь за paint, но так было быстрее, чем скачивать специальный софт):

Пробовал подавать импульсы последовательно(даже через регистры портов не помогает), что-то вроде синусоиды и всё равно не крутится. Вот этот код http://arrduinolab.blogspot.com/2014/09/blog-post.html даёт примерно такой же результат. Кто знает, в чём может быть проблема?

Мосфеты IRFS640A.

P.S. без оптронов тоже самое.

roman2712@mail.ru
Offline
Зарегистрирован: 16.01.2014

почитайте http://www.avislab.com/blog/brushless02/ + ссылки на статьи внизу

Такому мотору 6 ключей необходимо и правильное переключение обмоток (в том числе и по времени)

Так как Вы его подключили, нормально работать не будет и жрать будет немеренно. Втакой схеме его можно раскрутить постепенно уменьшая задержку между переключениями, но работает это не стабильно. Любое внешнее воздействие, и мотор останавливается.

То что написано в http://arrduinolab.blogspot.ru/2014/09/blog-post.html - будет крутить, но медленно... И так синусойду для таких двигателей не формируют... для синусоидального управления ардуины не хватит.

Это мой код (на SPI и LED можете не заморачиваться, это из проекта в целом)

#include <SPI.h>

#define CLEAR_ALL_TIMER1_INT_FLAGS    (TIFR1 = TIFR1)
#define DISABLE_ALL_TIMER1_INTS       (TIMSK1 = 0)
#define SET_TIMER1_INT_COMMUTATION    (TIMSK1 = (1 << OCIE1A))
#define SET_TIMER1_INT_HOLDOFF        (TIMSK1 = (1 << OCIE1B))

#define EDGE_FALLING          1
#define EDGE_RISING           0

#define COMMUTATION_CORRECTION 50

#define COMMUTATION_TIMING_IIR_COEFF_A      1
#define COMMUTATION_TIMING_IIR_COEFF_B      7

#define ZC_DETECTION_HOLDOFF_TIME_US (filteredTimeSinceCommutation / 2)

#define ADC_MUX_U                 0x0
#define ADC_MUX_V                 0x1
#define ADC_MUX_W                 0x2
#define ADC_MID_P                 0x3
#define ADC_RES_ALIGNMENT_BEMF    (1 << ADLAR)
#define ADC_REF_CHANNEL           ((0 << REFS1) | (1 << REFS0))
#define ADMUX_U                   (ADC_REF_CHANNEL | ADC_RES_ALIGNMENT_BEMF | ADC_MUX_U)
#define ADMUX_V                   (ADC_REF_CHANNEL | ADC_RES_ALIGNMENT_BEMF | ADC_MUX_V)
#define ADMUX_W                   (ADC_REF_CHANNEL | ADC_RES_ALIGNMENT_BEMF | ADC_MUX_W)
#define ADMUX_MID_P               (ADC_REF_CHANNEL | ADC_RES_ALIGNMENT_BEMF | ADC_MID_P)
#define ADC_PRESCALER_4           ((0 << ADPS2) | (1 << ADPS1) | (0 << ADPS0))
#define ADC_PRESCALER_8           ((0 << ADPS2) | (1 << ADPS1) | (1 << ADPS0))
#define ADC_PRESCALER_16          ((1 << ADPS2) | (0 << ADPS1) | (0 << ADPS0))

#define TICKS_PER_SECOND    2000000UL
#define TICKS_PER_MINUTE    (TICKS_PER_SECOND * 60)

//--------------------------------------------------------

#define UH    PB0
#define UL    PB1
#define VH    PD4
#define VL    PD5
#define WH    PD7
#define WL    PD6
#define LED_G   PB2
#define LED_R   PB3
#define LED_B   PB4

/*
  #define UH_ON PORTB |= (1 << UH)
  #define VH_ON PORTD |= (1 << VH)
  #define WH_ON PORTD |= (1 << WH)
  #define UH_OFF  PORTB &= ~(1 << UH)
  #define VH_OFF  PORTD &= ~(1 << VH)
  #define WH_OFF  PORTD &= ~(1 << WH)
*/

#define UH_OFF PORTB |= (1 << UH)
#define VH_OFF PORTD |= (1 << VH)
#define WH_OFF PORTD |= (1 << WH)
#define UH_ON  PORTB &= ~(1 << UH)
#define VH_ON  PORTD &= ~(1 << VH)
#define WH_ON  PORTD &= ~(1 << WH)


#define UL_ON PORTB |= (1 << UL)
#define VL_ON PORTD |= (1 << VL)
#define WL_ON PORTD |= (1 << WL)
#define UL_OFF  PORTB &= ~(1 << UL)
#define VL_OFF  PORTD &= ~(1 << VL)
#define WL_OFF  PORTD &= ~(1 << WL)

#define LED_G_ON PORTB |= (1 << LED_G)
#define LED_G_OFF  PORTB &= ~(1 << LED_G)
#define LED_R_ON PORTB |= (1 << LED_R)
#define LED_R_OFF  PORTB &= ~(1 << LED_R)
#define LED_B_ON PORTB |= (1 << LED_B)
#define LED_B_OFF  PORTB &= ~(1 << LED_B)

//--------------------------------------------------------
volatile unsigned char nextDrivePattern;
volatile unsigned char zcPolarity;
volatile unsigned char nextCommutationStep;
volatile unsigned char currentADMUX;
volatile unsigned int filteredTimeSinceCommutation;
volatile unsigned long ms_time = 0;

volatile byte zc_detection;
volatile unsigned int a_time;
volatile unsigned int b_time;
volatile unsigned int c_time;
volatile byte massive_led;
byte massive[128];
byte massive_1[128];
byte massive_2[128];
byte color;

#define LAT 10

static void InitPorts(void)
{
  DDRD |= (1 << VL) | (1 << VH) | (1 << WL) | (1 << WH);
  DDRB |= (1 << UH) | (1 << UL) | (1 << LED_G) | (1 << LED_R) | (1 << LED_B);
  DIDR0 = (1 << ADC5D) | (1 << ADC4D) | (1 << ADC3D) | (1 << ADC2D) | (1 << ADC1D) | (1 << ADC0D);
}

static void InitTimers(void)
{
  TCCR0A = (0 << COM0A1) | (0 << COM0A0) | (0 << COM0B1) | (0 << COM0B0) | (1 << WGM01) | (0 << WGM00);
  TCCR0B = (0 << WGM02)  | (0 << CS02)   | (1 << CS01)   | (1 << CS00);

  TCCR2A = (0 << COM2A1) | (0 << COM2A0) | (1 << COM2B1) | (1 << COM2B0) | (0 << WGM21) | (1 << WGM20);
  TCCR2B = (0 << WGM22)  | (0 << CS22)   | (0 << CS21)   | (1 << CS20);

  zc_detection = 0;
  TIFR2 = TIFR2;
  TIMSK2 |= (1 << TOIE2);

  TCCR1A = (0 << COM1A1) | (0 << COM1A0) | (0 << COM1B1) | (0 << COM1B0) | (0 << WGM21) | (0 << WGM20);
  TCCR1B = (0 << CS12) | (1 << CS11) | (0 << CS10);
}

static void InitADC(void)
{
  ADCSRA = (1 << ADEN) | (0 << ADSC) | (0 << ADATE) | (0 << ADIF) | (0 << ADIE) | ADC_PRESCALER_4;
}

void motorStep(byte CommutationStep) {
  switch (CommutationStep) {
    case 0:
      UH_OFF; VH_ON; WH_OFF; UL_ON; VL_OFF; WL_OFF;
      currentADMUX = ADMUX_W;
      break;
    case 1:
      UH_OFF; VH_OFF; WH_ON; UL_ON; VL_OFF; WL_OFF;
      currentADMUX = ADMUX_V;
      break;
    case 2:
      UH_OFF; VH_OFF; WH_ON; UL_OFF; VL_ON; WL_OFF;
      currentADMUX = ADMUX_U;
      break;
    case 3:
      UH_ON; VH_OFF; WH_OFF; UL_OFF; VL_ON; WL_OFF;
      currentADMUX = ADMUX_W;
      break;
    case 4:
      UH_ON; VH_OFF; WH_OFF; UL_OFF; VL_OFF; WL_ON;
      currentADMUX = ADMUX_V;
      break;
    case 5:
      UH_OFF; VH_ON; WH_OFF; UL_OFF; VL_OFF; WL_ON;
      currentADMUX = ADMUX_U;
      break;
  }
  zcPolarity = CommutationStep & 0x01;
}

void nextStepReadData(void) {
  nextCommutationStep++;
  if (nextCommutationStep >= 6)  nextCommutationStep = 0;
}

ISR (TIMER2_OVF_vect)
{
  if (zc_detection == 1) {

    unsigned char temp;
    unsigned char voltageMidPoint;

    ADMUX = ADMUX_MID_P;
    ADCSRA |= (1 << ADSC);
    while (ADCSRA & (1 << ADSC));
    voltageMidPoint =  ADCH;

    ADMUX = currentADMUX;
    ADCSRA |= (1 << ADSC);
    while (ADCSRA & (1 << ADSC));
    temp =  ADCH;

    if (((zcPolarity == EDGE_RISING) && (temp > voltageMidPoint)) || ((zcPolarity == EDGE_FALLING) && (temp < voltageMidPoint)))
    {
      unsigned int timeSinceCommutation;
      timeSinceCommutation = TCNT1;
      TCNT1 = COMMUTATION_CORRECTION;

      filteredTimeSinceCommutation = (COMMUTATION_TIMING_IIR_COEFF_A * timeSinceCommutation
                                      + COMMUTATION_TIMING_IIR_COEFF_B * filteredTimeSinceCommutation)
                                     / (COMMUTATION_TIMING_IIR_COEFF_A + COMMUTATION_TIMING_IIR_COEFF_B);

      OCR1A = filteredTimeSinceCommutation;

      SET_TIMER1_INT_COMMUTATION;
      CLEAR_ALL_TIMER1_INT_FLAGS;
      zc_detection = 0;
    }
  }
  ++a_time;
  ++c_time;
  if (c_time == 64) {
    ++b_time;
    c_time = 0;
  }
}

ISR(TIMER1_COMPA_vect)
{
  motorStep(nextCommutationStep);
  nextStepReadData();
  TCNT1 = 0;

  CLEAR_ALL_TIMER1_INT_FLAGS;
  SET_TIMER1_INT_HOLDOFF;
  OCR1B = ZC_DETECTION_HOLDOFF_TIME_US;
}

ISR(TIMER1_COMPB_vect)
{
  CLEAR_ALL_TIMER1_INT_FLAGS;
  DISABLE_ALL_TIMER1_INTS;
  zc_detection = 1;
}

void start_motor(void) {
  InitTimers();
  InitADC();
  InitPorts();

  nextCommutationStep = 3;
  motorStep(nextCommutationStep);
  filteredTimeSinceCommutation = 10000;

  TCNT1 = 0;
  TIMSK1 = (1 << OCIE1A);
}


ISR (TIMER0_COMPA_vect)
{
  if (massive_led < 128){ //128) {
    //led_on(massive_led/16);
    //if (massive[massive_led]) led_on(color);
    //else led_off();
    //led_on(massive[massive_led]);
    if (massive_led > 16) {
      SPI.transfer(B00000000);
      digitalWrite(10, HIGH);
      digitalWrite(10, LOW);
    }
    ++massive_led;
  }
  else led_off();
}

void holl(void) {

  unsigned int temp;
  static unsigned int b_time;

  if (a_time < 32) return; //неизвестный баг :)

  cli();
  temp = a_time;
  a_time = 0;
  TCNT0 = 0;
  TIFR0 = TIFR0;
  sei();

  b_time = (temp + 7 * b_time) / 8;

  OCR0A = b_time / 16; //- 1;

  //led_on(massive[0]);
  //if (massive[0]) led_on(color);
  //else led_off();
  SPI.transfer(B11111111);
  digitalWrite(10, HIGH);
  digitalWrite(10, LOW);
  massive_led = 1;
}

void led_on(byte c) {
  switch (c) {
    case 0:
      LED_R_OFF;
      LED_G_OFF;
      LED_B_OFF;
      break;
    case 1:
      LED_R_ON;
      LED_G_OFF;
      LED_B_OFF;
      break;
    case 2:
      LED_R_ON;
      LED_G_ON;
      LED_B_OFF;
      break;
    case 3:
      LED_R_OFF;
      LED_G_ON;
      LED_B_OFF;
      break;
    case 4:
      LED_R_OFF;
      LED_G_ON;
      LED_B_ON;
      break;
    case 5:
      LED_R_OFF;
      LED_G_OFF;
      LED_B_ON;
      break;
    case 6:
      LED_R_ON;
      LED_G_OFF;
      LED_B_ON;
      break;
    case 7:
      LED_R_ON;
      LED_G_ON;
      LED_B_ON;
      break;
    default:
      LED_R_OFF;
      LED_G_OFF;
      LED_B_OFF;
      break;
  }
}

void led_off() {
  LED_R_OFF;
  LED_G_OFF;
  LED_B_OFF;
}

void setup() {
  //massive = massive;
  
  start_motor();

  for (int i = 0; i < 128; ++i) {
    massive[i] = 0;
  }

  Serial.begin(115200);
  Serial.println("START");

  color = 0;

  pinMode(3, OUTPUT);
  pinMode(2, INPUT_PULLUP);
  //pinMode(13, OUTPUT);

  pinMode(LAT, OUTPUT);
  digitalWrite(LAT, LOW);
  
  SPI.begin();
  SPI.setClockDivider(SPI_CLOCK_DIV2);
  SPI.setBitOrder(MSBFIRST);

  attachInterrupt(0, holl, FALLING);
  TIMSK0 |= (1 << OCIE0A);

  //digitalWrite(3, LOW);

  //OCR2B = 255;
  //delay_ms(1000);
  OCR2B = 96;

  for (int i = 0; i < 128; ++i) {
    massive_1[i] = i / 16;
  }

  SPI.transfer(B00000001);
  digitalWrite(10, HIGH);
  digitalWrite(10, LOW);
}

void delay_ms(unsigned int t) {
  unsigned int temp = b_time;
  while (temp + t > b_time);
}


void led_off_all(void) {
  for (int i = 0; i < 128; ++i) {
   massive_1[i] = 7;
  }
}


void loop() {
/*
  for (int i = 0; i < 128; ++i) {
    led_off_all();

    massive[i+1] = 0;
    massive[i] = 3;
    massive[i-1] = 0;
    
    massive[0] = 1;
    massive[63] = 5;
    delay_ms(10);
    //Serial.println(*massive());
  }
*/
/*
  SPI.transfer(B00000001);
  digitalWrite(10, HIGH);
  digitalWrite(10, LOW);
  delay_ms(300);
  SPI.transfer(B00000010);
  digitalWrite(10, HIGH);
  digitalWrite(10, LOW);
  delay_ms(300);
  SPI.transfer(B00000100);
  digitalWrite(10, HIGH);
  digitalWrite(10, LOW);
  delay_ms(300);
  SPI.transfer(B00001000);
  digitalWrite(10, HIGH);
  digitalWrite(10, LOW);
  delay_ms(300);
  SPI.transfer(B00010000);
  digitalWrite(10, HIGH);
  digitalWrite(10, LOW);
  delay_ms(300);
  SPI.transfer(B00100000);
  digitalWrite(10, HIGH);
  digitalWrite(10, LOW);
  delay_ms(300);
  SPI.transfer(B01000000);
  digitalWrite(10, HIGH);
  digitalWrite(10, LOW);
  delay_ms(300);
  SPI.transfer(B10000000);
  digitalWrite(10, HIGH);
  digitalWrite(10, LOW);
  delay_ms(300);
*/
  //digitalWrite(13, !digitalRead(13));
  //delay_ms(500);
  /*
    for (int i = 0; i < 128; ++i) {
      for (int j = 0; j < 128; ++j) {
        massive[j] = 0;
      }
      massive[i] = 1;
      massive[127 - i] = 1;
      massive[0] = 1;
      massive[127 / 2] = 1;
      delay_ms(5);
    }

    ++color;
    if (color == 8) color = 0;
  */
}

 

DevAlone
Offline
Зарегистрирован: 06.08.2016

6 ключей как раз сегодня реализовал, но то ли я неправильно переключаю, то ли хз вообще что, результат тот же, не разгоняется. Спасибо, поизучаю.

roman2712@mail.ru
Offline
Зарегистрирован: 16.01.2014

Ну так надо детекитрование 0 обеспечить. 

Также, мосфетам верхнего плеча неплохо было бы драйверы поставить. Ну или N канальные поставить.

У меня сейчас в схеме верхние плечи на N канальные, нижние P канальные. Питание 5В (что бы с драйвером не заморачиваться, управляю сразу с ардуины). Крутиться до 3000 об\мин. Выше мне не надо было.

Раньше делал верхнее плечо с инвертирующим транзистором с подключением к 12В - крутил до 12 000 - 13 000 (чуть не улетел :) ), вместе с установленными блинами.

DevAlone
Offline
Зарегистрирован: 06.08.2016

Сейчас схема вот такая:

А код такой:

#define O1 2        // + на обмотку
#define O2 3
#define O3 4
 
#define I1 5        // - на обмотку
#define I2 6
#define I3 7
 
void setPosition(int pos);
 
void setup()
{
    pinMode(O1, OUTPUT);
    pinMode(O2, OUTPUT);
    pinMode(O3, OUTPUT);
 
    pinMode(I1, OUTPUT);
    pinMode(I2, OUTPUT);
    pinMode(I3, OUTPUT);
}
int pause = 200;
int position = 0;
void loop()
{
    setPosition(position++);
    if (position > 5)
        position = 0;
 
    delay(pause);
    pause-=1;
    if (pause < 10)
        pause = 10;
}
 
void setPosition(int pos)
{
    switch (pos)
    {
    case 0:
        PORTD &= B00000011;
        PORTD |= B01000100;
        /*digitalWrite(O1, HIGH);
        digitalWrite(O2, LOW);
        digitalWrite(O3, LOW);
 
        digitalWrite(I1, LOW);
        digitalWrite(I2, HIGH);
        digitalWrite(I3, LOW);*/
        break;
    case 1:
        PORTD &= B00000011;
        PORTD |= B10000100;
        /*digitalWrite(O1, HIGH);
        digitalWrite(O2, LOW);
        digitalWrite(O3, LOW);
 
        digitalWrite(I1, LOW);
        digitalWrite(I2, LOW);
        digitalWrite(I3, HIGH);*/
        break;
    case 2:
        PORTD &= B00000011;
        PORTD |= B10001000;
        /*digitalWrite(O1, LOW);
        digitalWrite(O2, HIGH);
        digitalWrite(O3, LOW);
 
        digitalWrite(I1, LOW);
        digitalWrite(I2, LOW);
        digitalWrite(I3, HIGH);*/
        break;
    case 3:
        PORTD &= B00000011;
        PORTD |= B00101000;
        /*digitalWrite(O1, LOW);
        digitalWrite(O2, HIGH);
        digitalWrite(O3, LOW);
 
        digitalWrite(I1, HIGH);
        digitalWrite(I2, LOW);
        digitalWrite(I3, LOW);*/
        break;
    case 4:
        PORTD &= B00000011;
        PORTD |= B00110000;
        /*digitalWrite(O1, LOW);
        digitalWrite(O2, LOW);
        digitalWrite(O3, HIGH);
 
        digitalWrite(I1, HIGH);
        digitalWrite(I2, LOW);
        digitalWrite(I3, LOW);*/
        break;
    case 5:
        PORTD &= B00000011;
        PORTD |= B01010000;
        /*digitalWrite(O1, LOW);
        digitalWrite(O2, LOW);
        digitalWrite(O3, HIGH);
 
        digitalWrite(I1, LOW);
        digitalWrite(I2, HIGH);
        digitalWrite(I3, LOW);*/
        break;
    default:
        digitalWrite(O1, LOW);
        digitalWrite(O2, LOW);
        digitalWrite(O3, LOW);
 
        digitalWrite(I1, LOW);
        digitalWrite(I2, LOW);
        digitalWrite(I3, LOW);
        break;
    }
}

Но не работает.

roman2712@mail.ru
Offline
Зарегистрирован: 16.01.2014

Специально проект свой подчистил. 

Схема и код. Работает от 5В. Если надо 12В, то на верхние плечи надо согласующий транзистор ставить + добавить делитель на детектор 0, дабы аналоговый порт Ардуины не спалить. Если надо еще больше, то надо ставить драйверы полевиков.

При наличие у мотора средней точки, AD3 подключается напрямую к средней точке, без R36-38

Схема (транзисторы указаны условно). Обязательно в верхнем плече P канальные, в нижнем N канальные. Иначе - ставить драйверы.

Код

#define CLEAR_ALL_TIMER1_INT_FLAGS    (TIFR1 = TIFR1)
#define DISABLE_ALL_TIMER1_INTS       (TIMSK1 = 0)
#define SET_TIMER1_INT_COMMUTATION    (TIMSK1 = (1 << OCIE1A))
#define SET_TIMER1_INT_HOLDOFF        (TIMSK1 = (1 << OCIE1B))

#define EDGE_FALLING          1
#define EDGE_RISING           0

#define COMMUTATION_CORRECTION 50

#define COMMUTATION_TIMING_IIR_COEFF_A      1
#define COMMUTATION_TIMING_IIR_COEFF_B      3

#define ZC_DETECTION_HOLDOFF_TIME_US (filteredTimeSinceCommutation / 2)

#define ADC_MUX_U                 0x0
#define ADC_MUX_V                 0x1
#define ADC_MUX_W                 0x2
#define ADC_MID_P                 0x3
#define ADC_RES_ALIGNMENT_BEMF    (1 << ADLAR)
#define ADC_REF_CHANNEL           ((0 << REFS1) | (1 << REFS0))
#define ADMUX_U                   (ADC_REF_CHANNEL | ADC_RES_ALIGNMENT_BEMF | ADC_MUX_U)
#define ADMUX_V                   (ADC_REF_CHANNEL | ADC_RES_ALIGNMENT_BEMF | ADC_MUX_V)
#define ADMUX_W                   (ADC_REF_CHANNEL | ADC_RES_ALIGNMENT_BEMF | ADC_MUX_W)
#define ADMUX_MID_P               (ADC_REF_CHANNEL | ADC_RES_ALIGNMENT_BEMF | ADC_MID_P)
#define ADC_PRESCALER_8           ((0 << ADPS2) | (1 << ADPS1) | (1 << ADPS0))

//--------------------------------------------------------

#define UH    PB0
#define UL    PB1
#define VH    PB2
#define VL    PB3
#define WH    PB4
#define WL    PB5

/*
  #define UH_ON PORTB |= (1 << UH)
  #define VH_ON PORTD |= (1 << VH)
  #define WH_ON PORTD |= (1 << WH)
  #define UH_OFF  PORTB &= ~(1 << UH)
  #define VH_OFF  PORTD &= ~(1 << VH)
  #define WH_OFF  PORTD &= ~(1 << WH)
*/

#define UH_OFF PORTB |= (1 << UH)
#define VH_OFF PORTB |= (1 << VH)
#define WH_OFF PORTB |= (1 << WH)
#define UH_ON  PORTB &= ~(1 << UH)
#define VH_ON  PORTB &= ~(1 << VH)
#define WH_ON  PORTB &= ~(1 << WH)

#define UL_ON PORTB |= (1 << UL)
#define VL_ON PORTB |= (1 << VL)
#define WL_ON PORTB |= (1 << WL)
#define UL_OFF  PORTB &= ~(1 << UL)
#define VL_OFF  PORTB &= ~(1 << VL)
#define WL_OFF  PORTB &= ~(1 << WL)

//--------------------------------------------------------
volatile unsigned char nextDrivePattern;
volatile unsigned char zcPolarity;
volatile unsigned char nextCommutationStep;
volatile unsigned char currentADMUX;
volatile unsigned int filteredTimeSinceCommutation;

volatile byte zc_detection;

static void InitPorts(void)
{
  DDRB |= (1 << UL) | (1 << UH) | (1 << VL) | (1 << VH) | (1 << WL) | (1 << WH);
  DIDR0 = (1 << ADC4D) | (1 << ADC3D) | (1 << ADC2D) | (1 << ADC1D) | (1 << ADC0D);
}

static void InitTimers(void)
{
  TCCR2A = (0 << COM2A1) | (0 << COM2A0) | (0 << COM2B1) | (0 << COM2B0) | (0 << WGM21) | (1 << WGM20);
  TCCR2B = (0 << WGM22)  | (0 << CS22)   | (0 << CS21)   | (1 << CS20);

  TIFR2 = TIFR2;
  zc_detection = 0;
  TIMSK2 |= (1 << TOIE2);

  TCCR1A = (0 << COM1A1) | (0 << COM1A0) | (0 << COM1B1) | (0 << COM1B0) | (0 << WGM21) | (0 << WGM20);
  TCCR1B = (0 << CS12) | (1 << CS11) | (0 << CS10);
}

static void InitADC(void)
{
  ADCSRA = (1 << ADEN) | (0 << ADSC) | (0 << ADATE) | (0 << ADIF) | (0 << ADIE) | ADC_PRESCALER_8;
}

void motorStep(byte CommutationStep) {
  switch (CommutationStep) {
    case 0:
      UH_OFF; VH_ON; WH_OFF; UL_ON; VL_OFF; WL_OFF;
      currentADMUX = ADMUX_W;
      break;
    case 1:
      UH_OFF; VH_OFF; WH_ON; UL_ON; VL_OFF; WL_OFF;
      currentADMUX = ADMUX_V;
      break;
    case 2:
      UH_OFF; VH_OFF; WH_ON; UL_OFF; VL_ON; WL_OFF;
      currentADMUX = ADMUX_U;
      break;
    case 3:
      UH_ON; VH_OFF; WH_OFF; UL_OFF; VL_ON; WL_OFF;
      currentADMUX = ADMUX_W;
      break;
    case 4:
      UH_ON; VH_OFF; WH_OFF; UL_OFF; VL_OFF; WL_ON;
      currentADMUX = ADMUX_V;
      break;
    case 5:
      UH_OFF; VH_ON; WH_OFF; UL_OFF; VL_OFF; WL_ON;
      currentADMUX = ADMUX_U;
      break;
  }
}

void nextStepReadData(void) {
  zcPolarity = nextCommutationStep & 0x01;
  nextCommutationStep++;
  if (nextCommutationStep >= 6)  nextCommutationStep = 0;
}

ISR (TIMER2_OVF_vect)
{
  if (zc_detection == 1) {

    unsigned char temp;
    unsigned char voltageMidPoint;

    ADMUX = ADMUX_MID_P;
    ADCSRA |= (1 << ADSC);
    while (ADCSRA & (1 << ADSC));
    voltageMidPoint =  ADCH;

    ADMUX = currentADMUX;
    ADCSRA |= (1 << ADSC);
    while (ADCSRA & (1 << ADSC));
    temp =  ADCH;

    if (((zcPolarity == EDGE_RISING) && (temp > voltageMidPoint)) || ((zcPolarity == EDGE_FALLING) && (temp < voltageMidPoint)))
    {
      unsigned int timeSinceCommutation;
      timeSinceCommutation = TCNT1;
      TCNT1 = COMMUTATION_CORRECTION;

      filteredTimeSinceCommutation = (COMMUTATION_TIMING_IIR_COEFF_A * timeSinceCommutation
                                      + COMMUTATION_TIMING_IIR_COEFF_B * filteredTimeSinceCommutation)
                                     / (COMMUTATION_TIMING_IIR_COEFF_A + COMMUTATION_TIMING_IIR_COEFF_B);

      OCR1A = filteredTimeSinceCommutation;

      SET_TIMER1_INT_COMMUTATION;
      CLEAR_ALL_TIMER1_INT_FLAGS;
      zc_detection = 0;
    }
  }
}

ISR(TIMER1_COMPA_vect)
{
  motorStep(nextCommutationStep);
  nextStepReadData();
  TCNT1 = 0;

  CLEAR_ALL_TIMER1_INT_FLAGS;
  SET_TIMER1_INT_HOLDOFF;
  OCR1B = ZC_DETECTION_HOLDOFF_TIME_US;
}

ISR(TIMER1_COMPB_vect)
{
  CLEAR_ALL_TIMER1_INT_FLAGS;
  DISABLE_ALL_TIMER1_INTS;
  zc_detection = 1;
}

void start_motor(void) {
  InitTimers();
  InitADC();
  InitPorts();

  nextCommutationStep = 0;
  motorStep(nextCommutationStep);
  filteredTimeSinceCommutation = 10000;

  TCNT1 = 0;
  TIMSK1 = (1 << OCIE1A);
}

void setup() {
  start_motor();
}

void loop() {

}