#include <Wire.h>
#include <LiquidCrystal_I2C.h>
LiquidCrystal_I2C lcd(0x27, 16, 2);
#define COIL_PIN_BIT PD3
#define LIMIT_LED_BIT PB5
#define INTERRUPT_PIN_BIT PD2
volatile int pulseCount;
float currentRPM;
unsigned long previousMillis;
unsigned long interval = 1000;
const int RPM_RANGES[] = {0, 1000, 2000, 3000, 4000, 5000, 6000, 7000, 8000, 9000, 10000};
const int DERAJAT_VALUES[] = {5, 10, 15, 20, 25, 30, 35, 40, 40, 40, 40};
int ADVANCE_VALUES[] = {0, 5, 10, 15, 20, 25, 30, 35, 40, 40, 40};
const int MAX_RPM = 10000;
const int NUM_RANGES = sizeof(RPM_RANGES) / sizeof(RPM_RANGES[0]);
void setup() {
// Konfigurasi Timer 1
TCCR1A = 0; // Mode normal
TCCR1B = (1 << CS12) | (1 << CS10); // Prescaler 1024
TIMSK1 = (1 << TOIE1); // Aktifkan interupsi overflow Timer 1
// Konfigurasi pin sebagai output
DDRD |= (1 << COIL_PIN_BIT) | (1 << LIMIT_LED_BIT);
// Konfigurasi pin sebagai input
DDRD &= ~(1 << INTERRUPT_PIN_BIT);
// Aktifkan pull-up resistor untuk pin input
PORTD |= (1 << INTERRUPT_PIN_BIT);
// Aktifkan global interupsi
sei();
pulseCount = 0;
lcd.begin(16, 2);
lcd.backlight();
lcd.setCursor(0, 0);
lcd.print("Welcomeee!");
}
void loop() {
// Main loop kosong, ringan dan dapat digunakan untuk tugas tambahan jika diperlukan.
}
ISR(TIMER1_OVF_vect) {
pulseCount++; // Tambahkan hitungan pulsa saat Timer1 overflow
}
void handleInterrupt() {
unsigned long pulseStartTime = micros();
// Tunggu sampai pin menjadi LOW
while (PIND & (1 << INTERRUPT_PIN_BIT)) {
}
unsigned long pulseDuration = micros() - pulseStartTime;
currentRPM = getRPM();
int derajatPengapian = hitungDerajatPengapian(currentRPM);
controlCoil(derajatPengapian, pulseDuration);
displayRPMBar(currentRPM);
}
void controlCoil(int derajatPengapian, unsigned long pulseDuration) {
if (pulseDuration >= 1) {
if (constrain(currentRPM, 0, MAX_RPM) <= MAX_RPM) {
PORTD |= (1 << COIL_PIN_BIT);
PORTD &= ~(1 << LIMIT_LED_BIT);
} else {
PORTD &= ~(1 << COIL_PIN_BIT);
PORTD |= (1 << LIMIT_LED_BIT);
}
} else {
PORTD &= ~(1 << COIL_PIN_BIT);
PORTD &= ~(1 << LIMIT_LED_BIT);
}
}
void displayRPMBar(float rpm) {
int barLength = map(constrain(rpm, 0, MAX_RPM), 0, MAX_RPM, 0, 16);
uint8_t registerValue = 0;
for (int i = 0; i < barLength; i++) {
registerValue |= (1 << i);
}
lcd.setCursor(0, 1);
Wire.beginTransmission(0x27);
Wire.write(0x40);
Wire.write(registerValue);
Wire.endTransmission();
for (int i = barLength; i < 16; i++) {
lcd.write(' ');
}
}
int hitungDerajatPengapian(float rpm) {
int currentRange = 0;
for (int i = 1; i < NUM_RANGES; ++i) {
if (rpm <= RPM_RANGES[i]) {
currentRange = i - 1;
break;
}
}
int derajatPengapian = min(DERAJAT_VALUES[currentRange] + ADVANCE_VALUES[currentRange], 40);
return derajatPengapian;
}
float getRPM() {
unsigned long currentMillis = millis();
if (currentMillis - previousMillis >= interval) {
previousMillis = currentMillis;
return (pulseCount * 60.0) / (interval / 1000.0);
}
return 0.0;
}