Как управлять мотором с помощью atmega 32 pwm

Некоторое время я разбирался в том, как управлять двигателем (контролировать его скорость) в быстром режиме pwm с моим atmega32. Мне нужно использовать 8-битный Timer0, потому что у меня есть другие применения для других счетчиков. Думаю, я знаю, как инициализировать таймер для этой задачи:

void initial_io (void){
    DDRC = 0xFF;
    DDRB = 0xFF;
    PORTA = (1<<PA4)|(1<<PA5);
    TCCR0 = (1<<WGM01)|(1<<WGM00); // PWM mode : Fast PWM.
    TCCR0 = (1<<CS02)|(1<<CS00); // PWM clock = CPU_Clock/1024
}

Но тут возникает проблема. Я просто не знаю, что делать дальше, что делать на моем main.

Мой конкретный проект - управлять автомобилем с дистанционным управлением с ускорением. Поэтому, когда я прошу машину ехать вперед, она должна разогнаться от остановки до максимальной скорости с фиксированным ускорением. Я не знаю ни одной сборки, поэтому, если вы можете мне помочь, сделайте это на C. Любая помощь будет очень признательна.


person user2149122    schedule 29.10.2013    source источник


Ответы (2)


Что ж, я думаю, у вас все в порядке со всеми выводами портов B и C. Кроме того, вам не нужно ничего делать при сборке на AVR. Только сборка доступна в виде макросов в avr-libc.

Настраивать

Во-первых, вы неправильно настраиваете TCCR0. Вы должны установить все биты сразу или использовать операцию чтения-изменения-записи (обычно TCCR0 |= _BV(bit_num); для установки бита или TCCR0 &= ~_BV(bit_num); для его очистки). (_BV(N) - это макрос avr-libc, который более разборчив, чем (1<<N) материал, который вы используете, но делает то же самое.) Кроме того, вам не хватает полярности вывода ШИМ, установленной битами COM00 и COM01. Прямо сейчас у вас (неявно) отключен выход PWM (OC0 отключен).

Поэтому я предполагаю, что вам нужен положительный ШИМ, то есть более высокие входные значения ШИМ приводят к большим рабочим циклам с высокой выходной мощностью. Это означает, что COM01 необходимо установить, а COM00 нужно очистить. (См. Стр. 80-81 спецификации ATmega32 (L).) В результате появится строка настройки:

TCCR0 = _BV(WGM01) | _BV(WGM00) // PWM mode: Fast PWM.
      | _BV(COM01)              // PWM polarity: active high
      | _BV(CS02) | _BV(CS00);  // PWM clock: CPU_Clock / 1024

Рабочий цикл

Теперь мы переходим к фактической генерации рабочего цикла. Таймер 0 довольно глупый, и он жестко связывает свой НИЖНИЙ с 0, а верхний с 0xFF. Это означает, что каждый период ШИМ равен PWM_Clock / 256, а поскольку вы устанавливаете PWM_Clock на CPU_Clock / 1024, период равен CPU_Clock / 262144, что составляет около 33 мс для тактовой частоты процессора 8 МГц. Таким образом, каждый такт ШИМ этот счетчик считает от 0 до 255, затем возвращается к 0 и повторяется.

Фактическая ШИМ генерируется схемой OC согласно таблице 40. Для имеющейся у нас настройки COM0* она говорит:

Очистить OC0 при сравнении совпадения, установить OC0 в НИЖНЕЕ

Это означает, что каждый раз, когда счетчик ведет счет, он сравнивает значение счетчика с регистром OCR0 и, если они совпадают, переводит выходной контакт OC0 на GND. Когда счетчик возвращается к 0, он переводит вывод на VCC.

Итак, чтобы установить рабочий цикл, вы просто записываете значение, соответствующее этому рабочему циклу, в OCR0:

OCR0 = 0;   // 0% duty cycle: always GND.
OCR0 = 64;  // 25% duty cycle
OCR0 = 128; // 50% duty cycle
OCR0 = 172; // 67% duty cycle
OCR0 = 255; // 100% duty cycle; always VCC. See below.

Этот последний случай предназначен для решения общей проблемы с ШИМ: количество возможных настроек рабочего цикла всегда на единицу больше, чем количество шагов счета. В этом случае имеется 256 шагов, и если на выходе может быть VCC для 0, 1, 2,… 256 из этих шагов, это дает 257 вариантов. Таким образом, вместо того, чтобы предотвращать случай 0% или 100%, они заставляют исчезнуть случай, когда меньше 100%. В примечании 1 к таблице 40 говорится:

Особый случай возникает, когда OCR0 равен TOP и установлен COM01. В этом случае сравнение игнорируется, но установка или очистка выполняется ВНИЗ.

И еще одно: если вы пишете в OCR0 в середине цикла ШИМ, он просто ждет следующего цикла.

Моделирование ускорения

Теперь, чтобы получить желаемое «постоянное ускорение», вам нужна какая-то стандартная временная база. Прерывание TOV0 (переполнение таймера 0) может сработать, или вы можете использовать другой таймер или какую-то внешнюю ссылку. Вы будете использовать эту стандартную временную базу, чтобы знать, когда обновлять OCR0.

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

#define kAccelStep 4
void accelerate_step() {
    uint8_t x = OCR0;
    if(x < (255 - kAccelStep))
        OCR0 = x + kAccelStep;
    else
        OCR0 = 255;
}

Просто делайте что-то подобное для каждого временного шага, и вы получите постоянное ускорение. Аналогичный алгоритм можно использовать для замедления, и вы даже можете использовать более изящные алгоритмы для моделирования нелинейных функций или для компенсации того факта, что двигатель не сразу переходит на скорость, заданную ШИМ.

person Mike DeSimone    schedule 29.10.2013
comment
Это было именно то, что я искал. Большое тебе спасибо. Еще одна вещь, которую я хочу спросить. Не могли бы вы объяснить мне значение рабочего цикла? Также есть способ сделать мое выходное напряжение от OC0 по желанию. Я имею в виду, есть ли способ с if (...) OC0 = 0? - person user2149122; 05.11.2013
comment
Для прямоугольной волны с заданным периодом T рабочий цикл - это то, какой процент от T тратится на высокий (для положительного рабочего цикла) или низкий (для отрицательного рабочего цикла). Таким образом, 75% положительный сигнал рабочего цикла с периодом 10 мс будет иметь максимальную мощность 7,5 мс и минимальную частоту 2,5 мс. Что касается вашего второго вопроса, я не уверен, что вы имеете в виду, говоря, что мое выходное напряжение с OC0 произвольно. 1) Выходные напряжения от OC0 равны VDD или GND. 2) Если вы хотите, чтобы OC0 был постоянным VDD или GND, либо установите рабочий цикл на 0% или 100%, либо используйте бит FOC0 в TCCR0. - person Mike DeSimone; 06.11.2013
comment
Извините моя ошибка. Я имел в виду от GND к VDD. Итак, если я хочу начать с остановки и линейно ускоряться, мой OCR0 должен быть 0, верно? - person user2149122; 07.11.2013
comment
Изначально. Принятие 0 (GND) соответствует остановке двигателя. - person Mike DeSimone; 07.11.2013

Поскольку вы, кажется, новичок в программировании AVR, я предлагаю вам пойти простым путем: начните с Arduino.

Среда Arduino предлагает простые функции, поэтому вам не нужно напрямую манипулировать регистрами процессора. Например, чтобы управлять выводом PWM, вам просто нужно вызвать analogWrite() (здесь)

Вот руководство по подключению двигателя к Arduino.

Вот руководство по программированию ATMega32 из Arduino IDE

person Benoit Blanchon    schedule 29.10.2013
comment
Если они используют другие счетчики, они, вероятно, не могут использовать Arduino. - person Mike DeSimone; 29.10.2013