Analog comparator of ATMEGA328P with Library AnalogComp.h

การทดลองนี้เป็นการใช้งานออปแอมป์ภายในตัวชิฟไมโครคอนโทรลเลอร์ ATMEGA328P ด้วยบอร์ด Arduino UNO สำหรับเปรียบเทียบค่าแรงดันระหว่างตำแหน่งขา PD6 = AIN0 และ PD7 = AIN1 ซึ่งเราสามารถใช้งานออปแอมป์ภายในชิฟได้อีกรูปแบบหนึ่งนอกจากการใช้วิธีการอ่านค่าแรงดันด้วยขา ADC0-ADC5 ซึ่งมีรูปแบบการทำงานที่แตกต่างกัน โดยในการทดลองจะนำตัวอย่างโปรแกรมแบบใช้วิธีกำหนดค่ารีจิสเตอร์ Analog comparator โดยตรง และตัวอย่างโปรแกรมด้วยการใช้ไลบารี่ AnalogComp.h ทั่วไปเพื่อให้ใช้งานได้ง่ายยิ่งขึ้นและเป็นแนวทางสำหรับนำไปใช้งานต่างๆ ต่อไป


รูปที่ 1 แสดงตำแหน่งขาใช้งาน AIN0 และ AIN1 ของบอร์ด Arduino UNO ซึ่งจะตรงกับขา PD6 = AIN0 และ PD7 = AIN1 ตามลำดับ และในรูปที่ 2 จะเป็นบล็อกไดอะแกรมภายในของ Analog Comparator รวมทั้ง ฺBit ควบคุมการทำงานที่สำคัญในแต่ละตำแหน่งให้เข้าใจการทำงานได้ง่ายขึ้น






ในรูปที่ 3 ถึงรูปที่ 8 เป็นรีจิสเตอร์สำหรับกำหนดการทำงานของ Analog Comparator จะมีด้วยกัน 4 ตัวคือ ADCSRA, ADCSRB, ACSR และ DIDR1 โดยรายละเอียดของแต่ละบิตในรีจิสเตอร์สามารถอ่านเพิ่มเติมได้ในดาต้าชีตหน้า 202-204 และหน้า 218 และ 219 [Ref.7] สำหรับเนื้อหาถัดไปจะเป็นตัวอย่างการกำหนดการใช้งาน Analog Comparator ด้วยการเซตรีจิสเตอร์ต่างๆ และการนำไลบารี AnalogComp.h มาใช้งาน โดยใช้โปรแกรมจากเว็บไซต์อ้างอิงท้ายบทความ
/*
Arduino Code for Configuration Analog Comparator Register [Example.1]
Arduino Code Program Ref. by
https://www.hackster.io/yeshvanth_muniraj/analog-comparator-module-in-atmega328p-migrating-to-c-7502ea
PIN AIN0, D6 = Input Voltage Adj
PIN AIN1, D7 = Vref = 3.3V
PIN D13 = LED on Board
*/
#include<avr/io.h>
#define F_CPU 16000000UL
void setup() {
pinMode(13,OUTPUT);
ACSR = 0x00; // Register ACSR
ADCSRB = 0x00; // Register ADCSRB
}
void loop() {
if( (ACSR & 0x20) == 0) // Check ACO (output bit)
{
digitalWrite(13, HIGH); // turn the LED on
delay(100);
}
digitalWrite(13, LOW); // turn the LED off
delay(100);
}
ตัวอย่างที่ 1 เป็นวิธีการกำหนดค่าในรีจิสเตอร์ ACSR และ ADCSRB ให้ทุกบิตมีค่าเท่ากับ 0 ทั้งหมด จากนั้นจะใช้การตรวจสอบตำแหน่งบิที่ 5 (ACO) ของรีจิสเตอร์ ACSR ในการณีที่บิต ACO เป็นลอจิก 0 ซึ่งหมายความว่าค่าแรงดันที่ขา AIN0 น้อยกว่า AIN1 เป็นผลให้แอลอีดี L (ขา D13) บนบอร์ดกระพริบให้ทราบ และเมื่อ AIN0 มากกว่า AIN1 แอลอีดี L ก็จะดับลง ซึ่งในตัวอย่างนี้จะไม่ใช้การอินเตอร์รัพท์ใดๆ
/*
Arduino Code for Configuration Analog Comparator Register [Example.2]
Arduino Code Program Ref. by
https://www.ee-diary.com/2021/07/arduino-analog-comparator-with-interrupt.html
*** pin AIN1 = D7 Vref. 3.3V
*** pin AIN0 = D6 Input Voltage Adj
*/
void setup () {
pinMode(13, OUTPUT);
Serial.begin(9600);
DDRD |= (1<<PD4);
DIDR1 |= (1<<AIN0D); // Disable Digital Inputs at AIN0 and AIN1
ADCSRA &= ~(1<<ADEN);
ADCSRB |= (1<<ACME); //Set ACME bit in ADCSRB to use external analog input at AIN1 -ve input
ADMUX = 0x01; //select A1 as input
ACSR =
(0 << ACD) | // Analog Comparator: Enabled
(0 << ACBG) | // Clear ACBG to use external input to AIN0 +ve input
(0 << ACO) | // Analog Comparator Output: OFF
(1 << ACI) | // Analog Comparator Interrupt Flag: Clear Pending Interrupt by setting the bit
(1 << ACIE) | // Analog Comparator Interrupt: Disabled
(0 << ACIC) | // Analog Comparator Input Capture: Disabled
(0 << ACIS1) | (0 << ACIS0); // Analog Comparator Interrupt Mode: Comparator Interrupt on Output Toggle
sei();
}
void loop() {
Serial.println(" LAB Analog Comparator ");
digitalWrite(13, LOW);
delay(100);
}
ISR(ANALOG_COMP_vect){
if(ACSR & (1<<ACO))
digitalWrite(13, HIGH);
delay(100);
Serial.println(" Interrup Analog Comparator");
}
ตัวอย่างที่ 2 เป็นตัวอย่างอีกแบบหนึ่งที่จะเห็นรายละเอียดของการกำหนดบิตต่างๆ ในรีจิสเตอร์ ADCSRA, ADCSRB, ADMUX และ ACSR เป็นหลัก ซึ่งจะกำหนดใช้การอินเตอร์รัพท์เข้าช่วยโปรแกรมการงานหลักเพิ่มเติม ลักษณะการทำงานของโปรแกรมจะสังเกตเห็นว่าในกรณีที่ค่าแรงดันที่ขา AIN0 มากกว่า AIN1 เป็นผลให้แอลอีดี L (ขา D13) บนบอร์ดจะดับ แสดงข้อความ LAB Analog Comparator แต่เมื่อ AIN0 มากกว่า AIN1 เป็นผลให้เกิดการอินเตอร์รัพท์ไปยังตำแหน่ง ISR(ANALOG_COMP_vect) และแอลอีดี L ติด พร้อมทั้งแสดงข้อความ Interrup Analog Comparator ให้ทราบนั้นเอง

/*
Arduino Code for Configuration Analog Comparator Register [Example.3]
Arduino Code Program Ref. by
https://microcontrollerslab.com/arduino-comparator-tutorial/
*** pin AIN1 = D7 Input Voltage Adj
*** pin AIN0 = D6 Internal Vref =1V
*/
// Definition of interrupt names
#include <avr/io.h>
// ISR interrupt service routine
#include <avr/interrupt.h>
const int pLED = 13; // LED at Pin13
const int pAIN1 = 7; // AIN1 at Pin7
volatile boolean iflag = true;
int idx;
// Install the interrupt routine for ACOMP
ISR(ANALOG_COMP_vect)
{
if ( ACSR & (1<<ACO) ) // ACO is set?
{
iflag = false;
digitalWrite(pLED, HIGH);
}
else
{
iflag = true;
digitalWrite(pLED, LOW);
}
}
void setup()
{
Serial.begin(9600);
pinMode(pLED, OUTPUT);
digitalWrite(pLED, LOW);
pinMode(pAIN1, INPUT);
cli();
ADCSRA &= ~(1<<ADEN); // ADC disabled
ADCSRB |= ~(1<<ACME); // AMUX enabled
ACSR = (1<<ACBG) | (1<<ACIE); // ACOMP Interrupt enabled
DIDR1 = (1<<AIN1D) | (1<< AIN0D);
sei();
Serial.print("ADCSRA: "); Serial.println(ADCSRA, HEX);
Serial.print("ADCSRB: "); Serial.println(ADCSRB, HEX);
Serial.print("ACSR: "); Serial.println(ACSR, HEX);
Serial.print("DIDR1: "); Serial.println(DIDR1, HEX);
Serial.println("Setup finished.");
}
void loop()
{
if (iflag)
Serial.println(idx); // iflag controls serial output
idx++;
delay(200);
}
ตัวอย่างที่ 3 เป็นตัวอย่างการกำหนดบิตต่างๆ ในรีจิสเตอร์ ADCSRA, ADCSRB, DIDR1 และ ACSR และกำหนดใช้การอินเตอร์รัพท์เช่นกัน โดยเมื่อค่าแรงดันที่ขา AIN1 มากกว่า AIN0 แสดงการนับค่าค่าตัวแปร idx ตลอดเวลา และเมื่อ AIN1 น้อยกว่า AIN0 จะทำให้เกิดการอินเตอร์รัพท์ขึ้น ที่ตำแหน่ง ISR(ANALOG_COMP_vect) เป็นผลให้แอลอีดี L (ขา D13) บนบอร์ดจะติดสว่างขึ้นแทนนั้นเอง

Arduino Library for AnalogComp.h
ตัวอย่างการใช้ไลบารี analogComp.h ข้างล่างนี้จะเป็นการใช้งานวงจร Analog Comparator ได้ง่ายขึ้น โดยเราเพียงศึกษาการใช้คำสั่งภาษา C++ เท่านั้น ซึ่งจะช่วยให้เราใช้เวลาในการพัฒนาโปรแกรมน้อยลง และจะนำตัวอย่างการใช้งานมา 3 แบบเพื่อให้เราสามารถนำไปใช้งานได้ตามที่ต้องการ
/*
Code program by : https://electronics.stackexchange.com/questions/376906/amplifying-pulse-signal-0-1-0-5v-to-arduino-digital-input
So, D7 is right (AIN0), at least it makes effect. Due that interrupt is raised when going over ~0.8V on that input. However A1 (signal input) voltage level, does not make any effect when interrupt is raised. Hmm, what could be the issue?
*** pin AIN1 = D7 Vref. 3.3V
*** pin AIN0 = D6 Input Voltage Adj
*/
#include "analogComp.h"
void setup() {
analogComparator.setOn(AIN0, AIN1); // AIN0 is on D7. As a reference voltage
analogComparator.enableInterrupt(speedSensorInterrupt);
Serial.begin(9600);
}
void loop()
{
delay (100);
Serial.println(".");
}
void speedSensorInterrupt()
{
Serial.println("analog Comparator Interrupt Active");
}
ตัวอย่างที่ 4 ข้างบนจะเป็นตัวอย่างโปรแกรมอย่างง่าย ด้วยการประกาศการใช้งาน analogComparator.setOn(AIN0, AIN1); และการกำหนดให้เกิดการอินเตอร์รัพท์ด้วยคำสั่ง analogComparator.enableInterrupt(speedSensorInterrupt); ซึ่งเมื่อแรงดันที่ขา AIN0 มากกว่า AIN1 โปรแกรมการทำงานจะเข้าไปยังโปรแกรมอินเตอร์รัพท์ speedSensorInterrupt นั้นเอง

/*
Code program by : https://github.com/leomil72/analogComp/tree/master
This is a simple sketch to demonstrate the use of analogComp, a
library to manage the analog comparator included in a wide
variety of Atmel microcontrollers
This sketch checks if the voltage on AIN0 will begin greater
than the voltage on AIN1. To test the sketch, build the following
circuit:
- connect pin D7 AIN1 to pin 3V3
- connect pin D6 AIN0 to GND using a pull-down resistor (10Kohm or greater)
- connect pin AIN0 to pin 5V to activate the comparation.
*/
#include "analogComp.h"
void setup() {
//opens the serial comm
Serial.begin(9600);
delay(2000);
}
void loop() {
//wait 5 seconds for AIN0 to begin greater than AIN1
Serial.println(analogComparator.waitComp(), DEC);
}
ตัวอย่างที่ 5 เป็นตัวอย่างโปรแกรมที่อยู่ในตัวอย่างไลบารี analogComp.h โดยการทำงานของโปรแกรมนี้จะส่งค่าตัวเลข 0 ไปยัง Serial Monitor เพื่อเป็นการรอให้แรงดันที่ขา AIN0 มากกว่า AIN1 ทุกๆ 5 วินาที และเมื่อค่าแรงดันที่ขา AIN0 มากกว่า AIN1 ก็จะทำให้โปรแกรมส่งค่าตัวเลข 1 แทนจนกว่าแรงดันที่ขา AIN0 น้อยกว่า AIN1 นั้นเอง ผลการทดลองแสดงในรูปที่ ในกรอบสีเขียว

/*
Code program by : https://github.com/leomil72/analogComp/tree/master
This is a simple sketch to demonstrate the use of analogComp, a
library to manage the analog comparator included in a wide
variety of Atmel microcontrollers
This sketch enables an interrupt to be raised when on the analog input 0
(pin AIN0) there is a voltage greater than the voltage on the
analog input 1 (pin AIN1). To test the sketch, build the following
circuit:
- connect pin D7 AIN1 to pin 3V3
- connect pin D6 AIN0 to GND using a pull-down resistor (10Kohm or greater)
- connect pin AIN0 to pin 5V to activate the interrupt
*/
#include "analogComp.h"
const byte LED13 = 13; //set the output LED
volatile boolean enableLed = false; //used to check if the interrupt has raised
void setup() {
pinMode(LED13, OUTPUT); //LED pin as output
analogComparator.setOn(AIN0, AIN1); //we instruct the lib to use voltages on the pins
analogComparator.enableInterrupt(changeStatus, CHANGE); //we set the interrupt and when it has to be raised
}
void loop() {
if (enableLed) { //let's check if the analog comparator has raised the interrupt
//yes, so we do a little blink of the LED
digitalWrite(LED13, HIGH);
delay(200);
digitalWrite(LED13, LOW);
enableLed = false;
}
}
//interrupt to be raised by the analog comparator
void changeStatus() {
enableLed = true; //let's inform the main loop that the condition has been reached by the analog comparator
}
ตัวอย่างที่ 6 เป็นตัวอย่างโปรแกรมที่อยู่ในไลบารี analogComp.h เช่นกัน โดยในช่วงเวลาปกติโปรแกรมจะทำงานในชุดคำสั่ง void loop() ตลอดเวลาโดยจะสังเกต LED ที่ขา D13 บนบอร์ด Arduino UNO จะดับตลอดเวลา ด้วยการกำหนดให้ค่าตัวแปร enableLed = false; และเมื่อแรงดันที่ขา AIN0 มากกว่า AIN1 ทำให้เกิดการอินเตอร์รัพท์ขึ้น เป็นผลให้โปรแกรมกระโดดมายังโปรแกรมอินเตอร์รัพท์ void changeStatus() เพื่อกำหนดค่าตัวแปร enableLed = true; โดยในขณะนี้ LED ที่ขา D13 ก็จะกระพริบให้ทราบทันที
สำหรับการทดลองใช้งานออปแอมป์ภายในตัวชิฟไมโครคอนโทรลเลอร์ ATMEGA328P นี้ เป็นการใช้งานฮาร์ดแวร์ภายในตัวชิฟที่มีมาให้อีกส่วนหนึ่ง ซึ่งเราสามารถเลือกนำมาใช้งานต่างๆ ได้อีกรูปแบบหนึ่ง ทั้งนี้ในงานที่ไม่ต้องการประมวลผลค่าแรงดันอินพุตอะนาลอกตลอดเวลา (ตำแหน่งขา A0-A5) ก็จะช่วยให้ตัวไมโครคอนโทรลเลอร์ทำงานในโปรแกรมหลัก (void loop()) ได้มีประสิทธิภาพมากขึ้น
Reference
- https://microcontrollerslab.com/arduino-comparator-tutorial/
- https://github.com/leomil72/analogComp/blob/master/examples/analogComp_enableInterrupt/analogComp_enableInterrupt.ino
- https://forum.arduino.cc/t/analog-comparator-and-hardware-pwm/877417/7
- https://www.ee-diary.com/2021/07/arduino-analog-comparator-with-interrupt.html
- https://www.hackster.io/yeshvanth_muniraj/analog-comparator-module-in-atmega328p-migrating-to-c-7502ea
- https://electronics.stackexchange.com/questions/376906/amplifying-pulse-signal-0-1-0-5v-to-arduino-digital-input
- https://ww1.microchip.com/downloads/en/DeviceDoc/Atmel-7810-Automotive-Microcontrollers-ATmega328P_Datasheet.pdf
- https://forum.arduino.cc/t/arduino-uno-pinout-diagram/142856