В предыдущем примере был подробно рассмотрен принцип работы ШИМ в AVR микроконтроллерах, на примере ATtiny13. Теперь же рассмотрим пример реализации ШИМ регулятора.
Все опыты я провожу на своей отладочной плате, соответственно код привожу применительно к ней:
/*
* tiny13_board_adc_pwm
* Демо-прошивка отладочной платы на ATtiny13.
* Демонстрация работы ШИМ-регулятора (в режиме коррекции фазы) на двух каналах:
* неинверсный сигнал на выходе OC0A, инверсный - на выходе OC0B.
* ШИМ-сигнал регулируется переменным резистором:
* напряжение с него подаётся на вход АЦП
* и, в зависимости от величины измеренного напряжения,
* изменяется время отсчёта для таймера ШИМ.
*/
#define F_CPU 1200000UL
#include <avr/io.h>
#include <avr/interrupt.h>
#define LED0 PB0 // OC0A
#define LED1 PB1 // OC0B
#define ANALOG_IN PB4 //ADC2
// Обработчик прерывания по завершению преобразования АЦП
ISR(ADC_vect)
{
// Записываем значение, полученное на выходе АЦП в регистры сравнения таймера.
// Здесь же, задаём минимальный порог свечения светодиода, после которого он будет гарантированно гаснуть
OCR0A=(ADCH < 10) ? 0 : ADCH;
OCR0B=(ADCH > 245) ? 255: ADCH;
}
int main(void)
{
// Светидиоды:
DDRB |= (1 << LED0)|(1 << LED1); // выходы = 1
PORTB &= ~((1 << LED0)|(1 << LED1)); // по умолчанию отключены = 0
// Перем. резистор:
DDRB &= ~(1 << ANALOG_IN); // вход = 0
// Таймер для ШИМ:
TCCR0A = 0xB1; // режим коррекции фазы ШИМ, неинверсный сигнал на выходе OC0A, инверсный - на выходе OC0B
TCCR0B = 0x02; // предделитель тактовой частоты CLK/8
TCNT0 = 0; // начальное значение счётчика
OCR0A = 0; // регистр сравнения A
OCR0B = 0; // регистр сравнения B
// Настройка АЦП:
ADMUX = 0x22; // опорное напряжение - VCC, левое ориентирование данных, выбран вход ADC2 (на нём висит перем. резистор)
ADCSRA = 0xEA; // АЦП включен, запуск преобразования, режим автоизмерения, прерывание по окончанию преобразования, частота CLK/4
ADCSRB = 0x00; // режим автоизмерения: постоянно запущено
DIDR0 |= (1 << ANALOG_IN); // запрещаем цифровой вход на ноге аналогового входа
sei(); //разрешаем глобально прерывания
while(1)
{
}
}
Видим, что код программы очень похож на код из предыдущего примера. Разница лишь в том, что в предыдущем примере, для демонстрации изменения ШИМ-сигналов на выходе, значение регистров сравнения плавно изменялось в бесконечном цикле, а здесь же — мы будем менять их вручную, с помощью переменного резистора, подключенного ко входу аналого-цифрового преобразователя (АЦП). На моей плате переменный резистор подключен ко входу ADC2 (PB4). АЦП будет отслеживать изменение входного напряжения и соответственно изменять значения регистров сравнения для таймера ШИМ.
Настройка АЦП
Рассмотрим подробнее, какие регистры отвечают за работу АЦП.
Регистр ADMUX (ADC Multiplexer Selection Register).
Для ATtiny13 его структура выглядит следующим образом:
Бит 6 — REFS0 (Reference Selection Bit): выбирает опорный сигнал напряжения для АЦП: *
- 0 — в качестве опорного используется напряжение Vсс;
- 1 — используется внутренний источник опорного напряжения 1.1В.
Бит 5 — ADLAR (ADC Left Adjust Result): Определяет порядок записи результатов преобразования в регистры ADCL и ADCH (см. ниже).
Биты 1-0 — MUX1-0 (Analog Channel Selection Bits): Отвечают за выбор входного канала: *
- 00 — ADC0 (PB5);
- 01 — ADC1 (PB2);
- 10 — ADC2 (PB4);
- 11 — ADC3 (PB3).
* Если какой-либо из битов REFS0, MUX0, MUX1 изменяется во время преобразования, изменение не будет действовать до тех пор, пока это преобразование не будет завершено (пока не установится бит ADIF в регистре ADCSRA).
Остальные биты не используются (зарезервированы) и доступны только для чтения (в них всегда нули).
Регистр ADCSRA (ADC Control and Status Register A):
Бит 7 — ADEN (ADC Enable): Установка этого бита активирует АЦП. Если сбросить этот бит во время преобразования, то преобразование остановится.
Бит 6 — ADSC (ADC Start Conversion): Запускает преобразование. В режиме одиночного преобразования, нужно устанавливать этот бит для каждого преобразования. В режиме автоматического преобразования — нужно установить этот бит для запуска первого преобразования.
Бит 5 — ADATE (ADC Auto Trigger Enable): Активирует автопреобразование. АЦП начнет преобразование на положительном фронте триггерного сигнала. Источник триггера выбирается установкой бита выбора триггера АЦП: ADTS регистра ADCSRB.
Бит 4 — ADIF (ADC Interrupt Flag): Флаг прерывания АЦП. Устанавливается по завершению преобразования, когда регистры данных обновились.
Бит 3 — ADIE (ADC Interrupt Enable): Разрешает прерывания АЦП.
Биты 2-0 — ADPS2-0 (ADC Prescaler Select Bits): Устанавливают предделитель тактовой частоты для АЦП:
- 000 — 2;
- 001 — 2;
- 010 — 4;
- 011 — 8;
- 100 — 16;
- 101 — 32;
- 110 — 64;
- 111 — 128.
Регистры данных ADCH и ADCL.
Результат аналого-цифрового преобразования может принимать значения от 0 до 2n-1 (где n — число разрядов АЦП). В ATtiny13 на борту десятиразрядный АЦП. Соответственно, результат АЦП может принимать 1024 значения (от 0 до 1023).
0 (0x00) — будет соответствовать входному напряжению 0В;
1023 (0x3FF) — входному напряжению, равному выбранному опорному (в зависимости от значения бита REFS0 регистра ADMUX).
По завершению преобразования, результат записывается в регистры данных ADCH и ADCL (соответственно, cтарший и младший регистры данных АЦП), в зависимости от значения бита ADLAR регистра ADMUX. Если бит ADLAR установлен — то ориентирование данных левое, если сброшен (по умолчанию) — то правое:
Когда ADCL считывается, регистр данных ADC не обновляется, пока не будет прочитан ADCH. Таким образом, если выбрано левое ориентирование данных (ADLAR=1) и нам достаточно 8-битной точности, то достаточно прочитать значение ADCH. В противном случае — сначала нужно считать значение ADCL, затем ADCH.
Регистр ADCSRB (ADC Control and Status Register B):
Биты 2-0 — ADTS2-0 (ADC Auto Trigger Source): Выбирают источник сигнала для старта преобразования в режиме автоизмерения. Преобразование будет запускаться каждый раз при установке выбранного флага прерывания:
- 000 — постоянно запущено;
- 001 — аналоговый компаратор;
- 010 — внешнее прерывание INT0;
- 011 — таймер/счётчик T0, по совпадению с регистром сравнения A;
- 100 — таймер/счётчик T0, по переполнению;
- 101 — таймер/счётчик T0, по совпадению с регистром сравнения B;
- 110 — внешнее прерывание PCINT0.
Бит 6 — ACME (Analog Comparator Multiplexer Enable): в этой статье я не буду затрагивать применение этого бита, т.к. аналоговый компаратор — это уже отдельная тема.
Остальные биты не используются (зарезервированы) и доступны только для чтения (в них всегда нули).
Регистр DIDR0 (Digital Input Disable Register 0).
Биты 5-2 — ADC3D-ADC0D (ADC3-0 Digital Input Disable): Если какой-либо из входов ADC3-0 используется как аналоговый, то в соответствующий бит ADC3D-ADC0D нужно устанавливать логическую единицу. При этом, буфер цифрового ввода на соответствующем выводе АЦП отключается для экономии энергии. Соответствующий бит PIN-регистра при этом всегда будет содержать ноль.
Возвращаемся к нашей программе
Итак, мы настроили таймер для ШИМ, и запустили АЦП: он будет постоянно отслеживать напряжение на входе ADC2, регулируемое переменным резистором, а каждый раз по окончанию преобразования будет срабатывать прерывание ISR(ADC_vect), в теле которого будут изменяться значения регистров сравнения для ШИМ-таймера, в зависимости от измеренного напряжения на входе АЦП. И не забываем, после настройки прерывания, разрешить прерывания глобально!
Полезные ссылки
ATtiny13 Datasheet (официальный)
ATtiny13 Datasheet (урезанный вариант)
AVR. Учебный курс. Использование АЦП
Модуль АЦП