Приведенная здесь разработка была создана в целях демонстрации возможностей микроконтроллеров. Интересно было узнать, что можно сделать с маленькой микросхемкой имеющей всего 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 который включен как делитель напряжения. Перемещая движок этого резистора можно изменять уровень напряжения на входе АЦП микроконтроллера.
Ниже приводится список радиоэлементов, оборудования и программ, которые я использовал в этом проекте:
- 4 резистора: 2,7К, 10K (2 шт), 300 Ом
- 1 переменный резистор: 10K
- 2 Конденсатора 100нФ, 1 конденсатор: 10нФ
- 1 диод: 1N4001
- 1 светодиод (любой маломощный)
- микроконтроллер ATtiny13
- транзистор: пара Дарлингтона TIP120
- Источник постоянного тока 12 Вольт
- Отладочная плата AVRJazz с микроконтроллером Tiny2313 на борту
- Программатор OvrOspII разработки Микки Хеннинга
- Atmel AVR Studio 4 для ввода, компиляции и отладки программы
Примечание переводчика: Вы можете применять любой программатор. А от отладочной платы можно и вовсе отказаться, так как программа уже отдажена.
Программа
При создании программы я решил использовать ассемблер вместо 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