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 [Picture by Ref.8]
รูปที่ 2 บล็อกไดอะแกรมของ Analog Comparator [Picture by Ref.7]

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

รูปที่ 3 รีจิสเตอร์ควบคุมการทำงานและสถานะของ ADCSRA [Picture by Ref.7]
รูปที่ 4 ตารางแสดงการควบคุมรีจิสเตอร์ ADCSRA สัญญาณอะนาลอก [Picture by Ref.7]
รูปที่ 5 รีจิสเตอร์ควบคุมการทำงานและสถานะของ ADCSRB [Picture by Ref.7]
รูปที่ 6 รีจิสเตอร์ควบคุมการเกิดอินเตอร์รัพท์ [Picture by Ref.7]
รูปที่ 7 ตารางกำหนดควบคุมการเกิดอินเตอร์รัพท์ [Picture by Ref.7]
รูปที่ 8 รีจิสเตอร์ควบคุมสัญญาณดิจิตอลอินพุต [Picture by Ref.7]

ในรูปที่ 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 ให้ทราบนั้นเอง

Analog comparator ATMEGA328P with Library AnalogComp.h
รูปที่ 9 แสดงผลการทดลองตัวอย่างที่ 2
/* 
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) บนบอร์ดจะติดสว่างขึ้นแทนนั้นเอง

Analog comparator ATMEGA328P with Library AnalogComp.h
รูปที่ 10 แสดงผลการทดลองตัวอย่างที่ 3

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 นั้นเอง

Analog comparator ATMEGA328P with Library AnalogComp.h
รูปที่ 11 แสดงผลการทดลองตัวอย่างที่ 4
/*

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 นั้นเอง ผลการทดลองแสดงในรูปที่ ในกรอบสีเขียว

Analog comparator ATMEGA328P with Library AnalogComp.h
รูปที่ 12 แสดงผลการทดลองตัวอย่างที่ 5
/*

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

  1. https://microcontrollerslab.com/arduino-comparator-tutorial/
  2. https://github.com/leomil72/analogComp/blob/master/examples/analogComp_enableInterrupt/analogComp_enableInterrupt.ino
  3. https://forum.arduino.cc/t/analog-comparator-and-hardware-pwm/877417/7
  4. https://www.ee-diary.com/2021/07/arduino-analog-comparator-with-interrupt.html
  5. https://www.hackster.io/yeshvanth_muniraj/analog-comparator-module-in-atmega328p-migrating-to-c-7502ea
  6. https://electronics.stackexchange.com/questions/376906/amplifying-pulse-signal-0-1-0-5v-to-arduino-digital-input
  7. https://ww1.microchip.com/downloads/en/DeviceDoc/Atmel-7810-Automotive-Microcontrollers-ATmega328P_Datasheet.pdf
  8. https://forum.arduino.cc/t/arduino-uno-pinout-diagram/142856