Аналоговый компаратор сравнивает между собой два напряжения и запоминает результат сравнения в регистре. Аналоговый компаратор имеет два входа: прямой и инверсный. При необходимости опорный сигнал может быть подключен к любому из них.
В плате Arduino Nano в качестве входов аналогового компаратора используются входы AIN0 и AIN1 (D6 и D7). Ко входу AIN0 подключается внешний или внутренний опорный источник напряжения, а на вход AIN1 (инверсный вход) подается напряжения для сравнения с напряжением AIN0 (прямой вход) .
Для лучшего понимания работы компаратора соберите схему и загрузите скетч:
void setup() { ACSR |= (1 << ACBG); // подключаем ИОН ко входу AIN0 (D6) DDRB |= (1 << 5); // выход PB5 как выход (D13) (см.пример http://rcl-radio.ru/?p=80812) } void loop() { while ((ACSR & 0x20) == 0x20) { // если бит ACO регистра ACSR равен 1, то исполнять код в цикле PORTB |= (1 << 5); // PB5 HIGH } PORTB &= ~(1 << 5); // PB5 LOW }
Опорное напряжение подается на вход AIN0 от внутреннего источника опорного напряжения 1,1 В, для этого используется бит ACBG регистра ACSR.
Регистр ACSR:
7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
ACD | ACBG | ACO | ACI | ACIE | ACIC | ACIS1 | ACIS0 |
Бит ACBG (6) регистра ACSR подключение к положительному входу компаратора ИОН 1.1 В при установке 1. Если в бит ACBG установить 0, то необходимо использовать внешний источник опорного напряжения.
Бит ACO (5) регистра ACSR является выходом компаратора, в него записывается результат сравнения:
- 0 — если AIN0<AIN1
- 1 — если AIN0>AIN1
Исходя из состояния ACO в данном скетче, загорается или гаснет светодиод подключенный к выходу D13.
Так же инвертирующий выход компаратора можно подключить к выходу мультиплексора АЦП. При этом опорное напряжение (внешнее) подается на вход AIN1 (D7), а в качестве входа мультиплексора АЦП можно использовать любой вход ADC0 — ADC7 (A0-A7).
void setup() { DDRB |= (1 << 5); // выход PB5 как выход (D13) ADCSRA &= ~(1 << ADEN); // ADEN = 0 ADCSRB |= (1 << ACME); // ACME = 1 /////////////////////////// MUX2..0 = ADC0...ADC7 // ADMUX |= (1 << MUX2); // ADMUX |= (1 << MUX1); ADMUX |= (1 << MUX0); } void loop() { while ((ACSR & 0x20) == 0x20) { // если бит ACO регистра ACSR равен 1, то исполнять код в цикле PORTB |= (1 << 5); // PB5 HIGH } PORTB &= ~(1 << 5); // PB5 LOW }
В этом примере будут использованы четыре регистра:
Регистр ADCSRB:
7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
— | ACME | — | — | — | ADTS2 | ADTS1 | ADTS0 |
Регистр ADCSRA:
7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
ADEN | ADSC | ADATE | ADIF | ADIE | ADPS2 | ADPS1 | ADPS0 |
Регистр ADMUX:
7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
REFS1 | REFS0 | ADLAR | — | MUX3 | MUX2 | MUX1 | MUX0 |
Регистр ACSR:
7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
ACD | ACBG | ACO | ACI | ACIE | ACIC | ACIS1 | ACIS0 |
Когда бит ACME регистра ADCSRB установлен в 1 и модуль АЦП выключен (бит ADEN регистра ADCSRA сброшен), то отрицательный вход компаратора подключается к выходу мультиплексора АЦП. Если бит ACME сброшен, то вход компаратора подключается к выводу AIN1. С помощью регистра ADMUX можно выбирать, какой из восьми выводов ADC7 — ADC0 будет подключен к отрицательному входу компаратора.
Бит ACME (6) регистра ADCSRB управляет мультиплексором АЦП. Если установить бит в 1 (АЦП при этом должен быть выключен), то качестве отрицательного входа компаратора можно использовать один из выводом АЦП (ADC0-ADC7)
Бит ADEN (7) регистра ADCSRA включает или выключает АЦП (1-включен).
Бит ACME (6) регистра ADCSRB позволяет использовать мультиплексор АЦП в качестве входов аналогового компаратора при установке 1 ( при этом АЦП должен быть выключен).
Биты MUX2 — MUX0 (2 — 0) регистра ADMUX — управляют входом мультиплексора:
MUX2 | MUX1 | MUX0 | Неинвертирующий вход аналогового компаратора |
0 | 0 | 0 | ADC0 |
0 | 0 | 1 | ADC1 |
0 | 1 | 0 | ADC2 |
0 | 1 | 1 | ADC3 |
1 | 0 | 0 | ADC4 |
1 | 0 | 1 | ADC5 |
1 | 1 | 0 | ADC6 |
1 | 1 | 0 | ADC7 |
В следующем скетче будет показан практический пример применения аналогового компаратора: измирение длительности импульса входного сигнала.
На вход AIN1 подается импульсный сигнал, выход компаратора будет менять свое логическое состояния при прохождении импульсного сигнала, при этом активируется работа таймера Т1.
Таймер ведет непрерывный счет на максимальной частоте (8 МГц) стартуя (при помощи компаратора) на нарастании импульса и останавливает счет при спаде импульса (фактически таймер не останавливает счет, счетный регистр TCNT обнуляется).
volatile int x; float f; bool w; const float k = 1.00639058; void setup() { Serial.begin(9600); cli(); //// таймер /////////// (см.пример - http://rcl-radio.ru/?p=80820) TCCR1A = 0; TCCR1B = 0; TCCR1B |= (1 << CS10); // делитель 1 TIMSK1 |= (1 << TOIE1); //// компаратор /////// ACSR |= (1 << ACBG); // подключаем ИОН ко входу AIN0 (D6) sei(); } void loop() { while((ACSR & 0x20) == 0x20){x=0;TCNT1 = 0;} while((ACSR & 0x20) == 0x00){w=1;} if(w==1){ f = x * 65535 + TCNT1; w=0; Serial.print(f/16*k,0); Serial.println(" uS");delay(500); } } ISR (TIMER1_OVF_vect){x++;}
На вход поданы импульсы длительность 400 мс, период следования импульсов 1 секунда.
Как видно на скриншоте длительность импульса была измерена довольно с большой точностью, но стоит обратить внимание на переменную const float k = 1.00639058. Эта переменная устраняет погрешность кварцевого резонатора установленного на плате Arduino. Для получения нужного коэффициента необходимо измерить частоту которую выдает таймер Т1 в режиме генерации меандра:
void setup(){ // см. пример - http://rcl-radio.ru/?p=80812 DDRB = 0b00000010; // установить пин PB1 (D9) как выход | выход OC1A cli(); // отключить глобальные прерывания TCCR1A = 0; // установить регистр TCCR1A в 0 TCCR1B = 0; // установить регистр TCCR1B в 0 TCCR1A |= (1 << COM1A0); // COM1A1 COM1A0 = bit:01 - изменение состояния вывода OC1A на противоположное при совпадении с A TCCR1B |= (1 << WGM12); // включить CTC режим > сброс таймера по совпадению TCCR1B |= (1 << CS10); // Установить бит CS10 на коэффициент деления 1 OCR1A = 1; // 8 МГц sei(); // включить глобальные прерывания } void loop(){ }
Подключив частотомер к цифровому выходу D9 нужно измерить частоту и путем простых вычислений получить нужный поправочный коэффициент:
Как видно на скриншоте погрешность кварцевого резонатора достаточно большая, порядка 0,63%.
Следующий пример аналогичен первому, но в нем помимо длительности импульса идет измерение скважности импульса и подсчет периода следования импульсов.
volatile int x; float t,t1; bool w,w1; const float k = 1.006424; void setup() { Serial.begin(9600); cli(); //// таймер /////////// TCCR1A = 0; TCCR1B = 0; TCCR1B |= (1 << CS10); // делитель 1 TIMSK1 |= (1 << TOIE1); //// компаратор /////// ACSR |= (1 << ACBG); // подключаем ИОН ко входу AIN0 (D6) sei(); } void loop() { while((ACSR & 0x20) == 0x20){} while((ACSR & 0x20) != 0x20){x=0;TCNT1 = 0;} while((ACSR & 0x20) == 0x20){} t1 = x * 65535 + TCNT1; while((ACSR & 0x20) != 0x20){} while((ACSR & 0x20) == 0x20){x=0;TCNT1 = 0;} while((ACSR & 0x20) != 0x20){w=1;} t = x * 65535 + TCNT1; if(w==1){ w=0; Serial.print("t1 = "); Serial.print(t1/16*k,0); Serial.println(" uS"); Serial.print("t0 = "); Serial.print(t/16*k,0); Serial.println(" uS"); Serial.print("T = "); Serial.print((t/16+t1/16)*k,0); Serial.println(" uS"); delay(1000); } } ISR (TIMER1_OVF_vect){x++;}
На вход поданы импульсы длительность 450 мс, период следования импульсов 1 секунда.