Site icon Меандр — занимательная электроника

Управление двигателем постоянного тока

Приведенная здесь разработка была создана в целях демонстрации возможностей микроконтроллеров. Интересно было узнать, что можно сделать с маленькой микросхемкой имеющей всего 8 выводов.
Микросхема ATtiny13 является самой простейшей и самой дешевой из всего семейства 8 разрядных микроконтроллеров Atmel AVR. Тем не менее, в своем арсенале он имеет набор самых разнообразных современных периферийных устройств. В том числе два 8-разрядных таймера с возможностью работы в режиме источника сигнала с ШИМ (PWM). А так же 4 канальный 10-разрядный АЦП. Не смотря на то, что объем памяти у данной микросхемы не велик (1K памяти программ и всего 64 байта SRAM и 64 байта EEPROM), однако она прекрасно подходит для большинства задач требующих применения генератора ШИМ и АЦП. Если же для ваших задач потребуется больше памяти, вы можете легко заменить данную микросхему на другую из семейства AVR. Ниже приведена таблица, в которой перечислены микроконтроллеры, более мощные, но совместимые по всем выводам с микроконтроллером ATiny13.

Для того чтобы продемонстрировать возможности микроконтроллера ATtiny13, используя ШИМ и АЦП, я решил использовать его для управления скоростью двигателя. Я начал искать подходящий двигатель для этого проекта. В конце концов я решил, что для этого лучше всего подходит мотор-вентилятор от моего старого компьютера, используемый там для охлаждения Процессора. Я думаю, что это идеальный двигатель для данного проекта. Подключив его через ключевой каскад на транзисторе TIP120 к выходу OC0A микроконтроллера ATtiny13, мы могли бы легко управлять скоростью вентилятора, используя ШИМ. Ниже приводится полная схема для этого проекта:

Переменный резистор R4 (10K) используется в качестве источника управляющего напряжения, подаваемого на вход АЦП (вход ADC1 ATtiny13 — PIN 7). Для того, чтобы сделать схему интереснее я добавил переключатель (S1), которая служит для включения / выключения вентилятора и светодиодный индикатор (LED1), который своим миганием индицирует работу программы. Частота мигания светодиода соответствует изменениям сигнала ШИМ. Если вентилятор вращаться быстрее индикатор мигает медленно. Когда вентилятор вращаться медленнее частота мигания индикатора увеличивается. Скорость вращения вентилятора регулируется переменным резистором R4 который включен как делитель напряжения. Перемещая движок этого резистора можно изменять уровень напряжения на входе АЦП микроконтроллера.

Ниже приводится список радиоэлементов, оборудования и программ, которые я использовал в этом проекте:

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

 

Программа

При создании программы я решил использовать ассемблер вместо C, потому что я просто хотел убедиться, что максимально эффективно использую весь этот крошечный объем программной памяти в 1K. Ну и просто из любопытства, на сколько велик будет этот код; Ну да ладно, давайте разберем подробно текст программы, который вы можете видеть ниже:

;*********************************************************************
; Program		: t13pwm.asm
; Description		: Tiny13 Fast PWM and ADC Fan Controller
; Last Updated	        : 15 December 2008
; Author		: RWB
; IDE/Compiler  	: Atmel AVR Studio 4.14
; Programmer    	: AvrOspII v5.47 from Mike Henning
;               	: AVRJazz Tiny2313 Board
;*********************************************************************
.include "tn13def.inc"
; Тактовая частота микросхемы Tiny13 по умолчанию
.equ F_CPU = 9600000
.cseg
; Начинаем с нулевого адреса памяти программ
.org 0
main:   ldi R24,RAMEND         ; Установка указателя стека
        out SPL,R24            ; SP = RAMEND
; Инициализация портов ввода вывода
       ldi   R16,0b00000011   ; Устанавливаем PB0=выход, PB1=выход, PB2=вход, PB3= вход, PB4= выход
       out   DDRB,R16         ; DDRB=0x03
; Инициализация АЦП
       ldi   R16,0b10000110
       out   ADCSRA,R16       ; Включаем АЦП, с коэффициентом деления 64
       ldi   R16,0b00000000
       out   ADCSRB,R16       ; Автономный режим работы
       ldi   R16,0b01100001
       out   ADMUX,R16        ; Внутренний источник опорного напряжения 1.1 В,
режим сдвига результата влево, канал: PB2 (ADC1)
       ldi   R16,0b00000100   ; Выключаем функцию цифрового входа для PB2
       out   DIDR0,R16
; Инициализация ШИМ  (PWM)
       ldi   R16,0b10000011
       out   TCCR0A,R16       ; Режим Fast PWM, сброс OC0A
       ldi   R16,0b00000100
       out   TCCR0B,R16       ; Используем коэффициент диления  fclk/256
; Инициализация флага кнопки и регистра  PORTB
       ldi   R17,0            ; Инициализация флага кнопки
       out   PORTB,R17		

lb_00:  sbic  PINB,PB3         ; проверяем условие (PB3 == 0)
        rjmp  lb_20            ; если нет, переходим к lb_20
        ldi   R19,5            ; используем подпрограмму задержки
        rcall delay_func
        sbic  PINB,PB3         ; если (PB3 == 0), читаем снова
        rjmp  lb_20            ; иначе переходим к lb_20
        cpi   R17,1            ; Обработка нажатия кнопки
        brne  lb_10            ; если (R17 != 1) переходим к lb_10
        ldi   R17,0            ; иначе R17=0
        ldi   R16,0            ; Выключаем генератор ШИМ
        out   TCCR0A,R16       ; TCCR0A = 0
        out   TCCR0B,R16       ; TCCR0B = 0
        cbi   PORTB,PB0        ; Выключаем мотор
        cbi   PORTB,PB1        ; Выключаем светодиод
        rjmp  lb_20
lb_10:	ldi   R17,1	       ; R17=1
        ldi   R16,0b10000011
        out   TCCR0A,R16       ; Режим Fast PWM, сброс OCR0A
        ldi   R16,0b00000100
        out   TCCR0B,R16       ; Используем делитель fclk/256
        sbi   PORTB,PB1        ; Включаем светодиод	

lb_20:  cpi   R17,1            ; если (R17 != 1)
        brne  lb_40            ; переходим к lb_50
				            ;
        sbi   ADCSRA,ADSC      ; Старт АЦП преобразования
lb_30:  sbic  ADCSRA,ADSC      ; для (ADCSRA & (1<<ADSC))
        rjmp  lb_30
        in    R16,ADCH         ; Читаем результат без учета 2-х последних бит из  ADCL
        out   OCR0A,R16        ; OCR0A = R16
        cbi   PORTB,PB1        ; Выключаем светодиод
        mov   R19,R16
        rcall delay_func       ; Вызываем функцию задержки (длительность в R19)
        sbi   PORTB,PB1        ; Включаем светодиод
lb_40:	mov   R19,R16
        rcall delay_func       ; Вызываем функцию задержки (длительность в R19)
        rjmp  lb_00
; Простая функция задержки
delay_func:
delay0: ldi   R20,25           ; R20 = 25
delay1: ldi   R21,255          ; R21 = 255
delay2: dec   R21              ; Уменьшаем R21 на единицу
        brne  delay2           ; если (R20 != 0) переходим к метке delay2
        dec   R20              ; Уменьшаем R20 на единицу
        brne  delay1           ; если (R20 != 0) переходим к метке delay1
        dec   R19              ; Уменьшаем  R19 на единицу
        brne  delay0           ; если (R19 != 0) переходим к метке delay0
        ret                    ; Выход их подпрограммы
.exit

 

Пояснения к программе

Первая группа операций программы (см. текст программы выше) производит установку вершины стека. Стек использует память SRAM микроконтроллера. Мы устанавливаем указатель стека на конец этой памяти (память имеет 64 байта). Системная переменная RAMEND всегда содержит адрес последней ячейки памяти. То есть в нашем случае RAMEND = 64

   ldi R24,RAMEND         ; Установка указателя стека
   out SPL,R24            ; SP = RAMEND

Следующая группа операций устанавливает режим работы порта PORTB. Режимы каждой линии порта устанавливаются так, как показано в следующей таблице:

Инициализация Аналого-цифрового преобразователя

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

A. Выбрать коэффициент предварительного деления и активировать АЦП

Предварительный делитель используется для деления тактовой частоты (внутренего или внешнего тактового генератора). Тактовая частота используется в схеме последовательных приближений АЦП для преобразования аналогового сигнала. Чтобы получить максимальное разрешение нужно выбирать частоту в пределах от 50 кГц до 200 кГц. В данной конструкции я использую деление на 64, поэтому тактовая частота будет составлять примерно 150 кГц (9600000 Гц / 64).

Благодаря тому, что мы не используем автозапуск преобразования (ADATE) и прерывание от АЦП (ADIF и ADIE), то мы можем установить эти биты таким образом чтобы, установить коэффициент деления 64 (ADPS2, ADPS1 и ADPS0) и включить АЦП (ADEN). Запись значения в регистр состояния АЦП производится следующим образом:

ldi   R16,0b10000110
out   ADCSRA,R16       ; Включить АЦП, с коэффициентом предделения 64


B. Выбрать источник запуска процесса преобразования АЦП

Мы будем использовать автономный режим запуска, значит, встроенный АЦП будет сам периодически запускать процесс преобразования, как только мы программно разрешим выполнять этот процесс:

Таким образом, записав во все три бита управления режимами запуска АЦП (ADTS2, ADT21 и ADTS0) уровень логического нуля, мы переводим АЦП в автономный режим работы:

ldi   R16,0b00000000
out   ADCSRB,R16       ; Автономный режим работы

C. Выбор канала АЦП

Мы должны указать, какой из каналов АЦП мы будем использовать, выбрав значения битов управления мультиплексором АЦП (MUX1 и MUX0) в регистре управления мультиплексором (ADMUX):

Из таблицы видно, что для того, что бы выбрать канал ADC1 (PB2), используемый в данном проекте необходимо присвоить биту MUX1 = 0 и биту MUX0 = 1. Далее мы выберем значение внутреннего опорного напряжения равным 1,1 вольт, записав в соответствующий бит (REFS0) значение логической «1»:

Когда встроенный АЦП закончит процесс преобразования, результат измерения запишется в пару регистров ADCH и ADCL. Однако в этом проекте мы используем 8-разрядное значение. Поэтому мы выбираем режим сдвига влево результата измерения. Для этого мы устанавливаем бит ADLAR в регистре ADMUX в единицу и будем читать результат только из реестра ADCH (младшие биты в регистре ADCL мы просто игнорируем).

ldi   R16,0b01100001
out   ADMUX,R16     ; Reference 1.1 Volt, Left Adjust, Channel: PB2

D. Отключение функции цифрового входа для аналогового канала

И последнее, что необходимо выполнить для завершения процесса инициализации АЦП, это отключение для канала (ADC1) функции цифрового входа. Каждый разряд порта ввода-вывода в обычном режиме работает как вход для цифрового сигнала. Если данный вывод микросхемы мы используем как вход АЦП, то рекомендуется отключить функцию цифрового входа. Это позволяет уменьшить ток потребления и увеличить точность преобразования.

Записав в бит ADC1D значение логической «1», мы отключаем функцию цифрового входа для линии порта PB2:

ldi   R16,0b00000100   ; Отключение цифрового входа для PB2
out   DIDR0,R16

Инициализация режима широтно-импульсной модуляции

Микроконтроллер ATtiny13 имеет два независимых канала ШИМ. Выход канала, который мы используем в этом проекте, называется OC0A (выходит на PB0). А другой канал называется OC0B (выходит на PB1). Для включения встроенного ШИМ микроконтроллера ATtiny13 мы используем следующие шаги:

A. Выбор режима генерации сигнала

Микроконтроллер ATtiny13 поддерживает режима ШИМ. Первый из них называется «Fast PWM» (быстрый ШИМ). Он обеспечивает самую высокую частоту выходного сигнала. Второй режим называется «Phase correct PWM» (ШИМ с корректной фазой). Этот режим обеспечивает симметричное изменение, как переднего, так и заднего фронтов выходного сигнала. Этот режим работает на более низких частотах по сравнению с «Fast PWM». В рамках этого проекта мы будем использовать «Fast PWM».

Установив COM0A1 = 1 и COM0A0 = 0, мы выбираем режим сброса OC0A при совпадении, и установки OC0A при достижении конца. Это означает, что в тот момент, когда значение счетного регистра TCNT0 таймера окажется равным значению регистра OCR0A, выход OC0A (PB0) будет сброшен в «0». Когда же таймер досчитает до своего максимального значения (TCNT0 = 255), на выходе OC0A установится логическая «1». Изменяя значение OC0A, можно менять ширину выходного импульса. Режим «Fast PWM» можно выбрать, установив в регистре TCCR0B бит WGM2 = 0, а в регистре TCCR0A биты WGM1 = 1 и WGM2 = 1.

ldi   R16,0b10000011
out   TCCR0A,R16       ; Установка режима Fast PWM, Сброс OC0A

B. Выбор тактовой частоты на входе таймера

Сердце встроенного ШИМ — 8-разрядный таймер/счетчик. Для формирования выходного сигнала существует специальная схема сравнения. она непрерывно сравнивает значение счетного регистра таймера TCNT0 со значением регистра OCR0A и использует результат сравнения для формирования выходного сигнала. Для того чтобы вся эта схема заработала, мы должны подать на вход таймера тактовый сигнал. В качестве тактового сигнала выбирается один из выходов со специального встроенного делителя тактовой частоты. Для выбора одного из этих сигналов служит специальный регистр TCCR0B:

В этом проекте я использую сигнал с выхода делителя с коэффициентом деления 256. Частоту выходного сигнала для режима Fast PWM можно рассчитать по следующей формуле:

Учитывая, что N — это коэффициент предварительного деления, при тактовой частоте внутреннего генератора 9,6 МГц выходная частота на OC0A будет равна

fOC0A=9600000 / (256 x 256) = 146.48 Hz

Вы должны поэкспериментировать и выбрать для себя самый подходящий коэффициент деления предварительного делителя, в зависимости от типа двигателя постоянного тока который вы используете. Попробуйте начать с большого коэффициента предварительного деления и постепенно уменьшать его наблюдая, как ведет себя двигатель. При этом необходимо контролировать, что бы биты FOC0A и FOC0B в регистре TCCR0B были установлены в «0» в режиме ШИМ.

ldi   R16,0b00000100
out   TCCR0B,R16       ; Используем предварительный делитель  fclk/256

 

Описание алгоритма

Работу программы я довольно подробно уже объяснил (думаю, что в самой программе достаточно комментариев). Однако мне хотелось бы пояснить работу всего алгоритма используя псевдокод в стиле языка C. Надеюсь, что это позволит лучше понять все детали. И так, вот этот псевдокод:

Initial_PORTB();
Initial_ADC();
Initial_PWM();
R17_SwicthFlag = 0;
for(;;) {
    if (switch_PB3_is_pressed()) {
    if (R17_SwicthFlag == 0) {
    R17_SwicthFlag = 1;
    Enable_PWM();
      Turn_On_LED();
    } else {
      R17_SwicthFlag = 0;
      Disable_PWM();
      Turn_Off_LED();
    }
  }
  if (R17_SwicthFlag == 1) {
    Enable_ADC();
    Wait_ADC_Conversion();
    PWM_OC0A = ADC_Result_in_ADCH;
    Turn_Off_LED();
    Delay(ADC_Result_in_ADCH);
    Turn_On_LED()
  }
  Delay(ADC_Result_in_ADCH);
}

В тексте псевдокода можно увидеть бесконечный цикл for(;;), аналог которого на ассемблере выглядит следующим образом:

lb_00:  sbic PINB,PB3
        ...строка программы
        ...строка программы
        ...строка программы
        rjmp lb_00

Внутри этого бесконечного цикла мы производим чтение положения переключателя, включаем и выключаем ШИМ, производим АЦП преобразование, включаем и выключаем индикатор.

При установке бита ADSC регистра ADCSRA в единицу мы конфигурируем встроенное АЦП микросхемы ATtiny13 для чтения канала (PB2) и начала процесса преобразования. Далее мы должны просто подождать, пока этот бит будет очищен. Это произойдет в тот момент, когда процесс преобразования будет закончен.

        sbi   ADCSRA,ADSC      ; Start ADC conversion
lb_30:  sbic  ADCSRA,ADSC      ; while (ADCSRA & (1<<ADSC))
	rjmp  lb_30
        in    R16,ADCH         ; Read the result Ignore the last 2 bits in ADCL
        out   OCR0A,R16        ; OCR0A = R16

Как я уже объяснял ранее, я использую светодиодный индикатор, как индикатор правильной работы программы. Но для того, чтобы сделать эту индикацию более интересной, я использую выходное значение АЦП в качестве коэффициента задержки для мерцания светодиода. Это делает задержку переменной и зависящей от положения движка переменного резистора.

        mov   R19,R16
        rcall delay_func       ; Вызываем функцию задержки с параметром в R19

Если значение ADCH увеличивается, ширина выходного импульса на выходе OC0A (PB0) будет шире. Это приведет к тому, что скорость вращения двигателя постоянного тока станет быстрее. При этом время задержки мерцания светодиода будет больше. Если же значение ADCH уменьшается, то это означает, что длительность выходного импульса сигнала ШИМ уменьшается, и двигатель постоянного тока будет вращаться медленнее, а значение задержки мигания индикатора уменьшится. Вы можете изменить алгоритм регулировки, изменяя значение битов COM0A1 и COM0A0 в регистре TCCR0A. Это приведет к инвертированию сигнала ШИМ на выходе OC0A.

 

Загрузка и запуск кода

После компиляции и отладки (вы всегда должны пройти через эту процедуру) вы должны получить такой результат:

Собственно кодом занято около 128 байт из общего (1024 байт) объема программной памяти. Этот очень хороший результат, так как остается еще место на усовершенствование программы на будущее. Как вы видите 1K программной памяти микроконтроллера ATtiny13 вполне достаточно для несложного устройства использующего ШИМ и АЦП. Теперь можно прошивать код в микроконтроллер. Но прежде чем сделать это необходимо проверить микросхему ATtiny13 правильно ли установлены биты защиты и другие FUSE переключатели. Для правильной работы описываемого устройства в программе OvrOspII биты FUSE переключателей должны быть установлены следующим образом:

 

Убедитесь, что вы выбрали режим синхронизации от внутреннего RC генератора, частота 9.6МГц; время запуска: 14 CK + 64ms. Если все выставлено правильно, вы можете прошивать программу микроконтроллер.

 

Ниже вы можете посмотреть видеоролик, где показана работа описанного выше устройства.

 

Примечание: когда вентилятор крутится медленнее, индикатор мигает быстрее. И наоборот, когда вентилятор крутится быстрее, светодиод мигает медленнее. Однако на видео вращение вентилятора искажается. Вентилятор ускоряется, а кажется, что он вращается медленнее. Это происходит от того, что частота вращения вентилятора близка и даже превышает частоту кадров при съемке.

 

Отладочная плата AVRJazz

Адрес оригинала статьи: http://www.ermicro.com

Exit mobile version