#define F_CPU 16000000UL
#include <avr/io.h>
#include <util/delay.h>
#include <avr/interrupt.h>
#include <stdint.h>
#include <stdbool.h>
volatile uint8_t pulser_angle = 65; // Pulser angle default, bisa diubah runtime jika diperlukan
// Advance map (derajat sebelum TMA)
const uint8_t map1[30] = {
0, 8, 15, 20, 22, 26, 28, 30, 32, 32,
32, 32, 32, 32, 32, 32, 32, 32, 32, 34,
32, 30, 28, 25, 23, 19, 17, 15, 15, 15
};
// Dwell time (dalam mikrodetik) sesuai koil 2.5 ohm, disesuaikan per RPM
const uint16_t dwell_time_us[30] = {
5500, 4989, 4528, 4113, 3738, 3400, 3095, 2821, 2573, 2350,
2148, 1967, 1803, 1655, 1522, 1402, 1294, 1196, 1108, 1028,
957, 892, 834, 782, 734, 692, 653, 618, 587, 559
};
int main() {
uint8_t led = 0;
uint32_t pulse_delay, curve_select;
uint32_t time_per_degree;
uint32_t spark_time;
int16_t spark_deg;
DDRB = 0x22; // PB5 dan PB1 output
PORTB = 0x00;
TCCR1B = 0x03; // Prescaler 64 (timer clock = 250kHz -> 4us per tick)
while (1) {
// Tunggu rising edge pulser
while ((PINB & 0x01) == 0x00);
while ((PINB & 0x01) == 0x01);
pulse_delay = TCNT1;
TCNT1 = 0;
if (pulse_delay < 10) continue; // Hindari divide by zero
curve_select = 30000 / pulse_delay; // Semakin cepat putaran, semakin besar curve
if (curve_select >= 30) curve_select = 29;
time_per_degree = pulse_delay / 360; // dalam satuan timer tick (4us/tick)
spark_deg = pulser_angle - map1[curve_select];
if (spark_deg < 1) spark_deg = 1;
spark_time = time_per_degree * spark_deg; // timer tick sampai spark
uint32_t dwell_ticks = dwell_time_us[curve_select] / 4; // konversi us ke tick (4us/tick)
uint32_t dwell_start = (spark_time > dwell_ticks) ? (spark_time - dwell_ticks) : 0;
// LED indikator tiap 8 siklus
if (++led == 8) {
PORTB |= 0x20;
_delay_ms(10);
PORTB &= ~0x20;
led = 0;
}
// TCI control
while (TCNT1 < dwell_start); // Tunggu sampai saat mulai charging koil
PORTB |= 0x02; // PB1 HIGH (charge koil)
while (TCNT1 < spark_time); // Tunggu sampai saat spark
PORTB &= ~0x02; // PB1 LOW (fire)
}
}
Are you using Arduino UNO to develop your project?
no i use arduino nano
Arduino NANO and Arduino UNO are the same things except that the NANO is small size and breadboard version.
As you are using Arduino NANO and of course the Arduino IDE, you can use setup() and loop() functions to write Arduino codes instead of register-level codes to attract the attentions of more readers.
Remember, your NANO only has an 8-bit access to memory and only has a 16 bit register width. So your use of 32 bit data requires MANY processor cycles to do the math. Using a processor with real 32 bit registers and a wider memory path would really ease your programming!
#define F_CPU 16000000UL // Mengatur frekuensi CPU ke 16 MHz
#include <avr/io.h> // Untuk register I/O AVR
#include <util/delay.h> // Untuk fungsi delay
#include <avr/interrupt.h> // Termasuk jika di masa depan ingin menggunakan interrupt
#include <stdint.h> // Untuk tipe integer tetap (uint8_t, uint16_t, dll.)
#include <stdbool.h> // Untuk tipe boolean
// Variabel global yang dapat diubah saat runtime jika diperlukan
volatile uint8_t pulser_angle = 65; // Sudut pulser default, dalam derajat sebelum TMA
// Advance map (derajat sebelum TMA)
// Indeks array ini menentukan tingkat 'advance' berdasarkan RPM
const uint8_t map1[30] = {
0, 8, 15, 20, 22, 26, 28, 30, 32, 32,
32, 32, 32, 32, 32, 32, 32, 32, 32, 34,
32, 30, 28, 25, 23, 19, 17, 15, 15, 15
};
// Dwell time (dalam mikrodetik) sesuai koil 2.5 ohm, disesuaikan per RPM
// Indeks array ini menentukan durasi 'dwell' berdasarkan RPM
const uint16_t dwell_time_us[30] = {
5500, 4989, 4528, 4113, 3738, 3400, 3095, 2821, 2573, 2350,
2148, 1967, 1803, 1655, 1522, 1402, 1294, 1196, 1108, 1028,
957, 892, 834, 782, 734, 692, 653, 618, 587, 559
};
// Variabel yang akan digunakan di dalam loop, dideklarasikan secara global
// atau lokal di dalam loop, tergantung kebutuhan
uint8_t led_counter = 0; // Menghitung siklus untuk indikator LED
void setup() {
// Konfigurasi Pin I/O
// PB5 (LED indikator) dan PB1 (Kontrol Koil) sebagai output
DDRB |= (1 << PB5) | (1 << PB1); // Set PB5 dan PB1 sebagai output
// PB0 (Input Pulser) secara default adalah input jika tidak diatur sebagai output
// Pastikan pull-up internal tidak aktif jika pulser memiliki pull-up eksternal
// PORTB &= ~((1 << PB5) | (1 << PB1)); // Pastikan pin output LOW saat inisialisasi
// Konfigurasi Timer/Counter1
// Prescaler 64: clock timer = F_CPU / 64 = 16,000,000 / 64 = 250,000 Hz
// Setiap tick timer = 1 / 250,000 Hz = 4 mikrodetik
TCCR1B = (1 << CS11) | (1 << CS10); // Set prescaler ke 64
TCNT1 = 0; // Reset Timer/Counter1
}
void loop() {
uint32_t pulse_duration_ticks;
uint32_t curve_index;
uint32_t time_per_degree_ticks;
uint32_t spark_time_ticks;
int16_t spark_advance_deg;
uint32_t dwell_ticks;
uint32_t dwell_start_ticks;
// 1. Tunggu sinyal pulser (deteksi falling edge)
// Asumsi pulser input ada di PB0
while ((PINB & (1 << PB0)) == 0x00); // Tunggu rising edge (pulser HIGH)
while ((PINB & (1 << PB0)) == 0x01); // Tunggu falling edge (pulser LOW)
// 2. Baca durasi putaran dari timer dan reset timer
pulse_duration_ticks = TCNT1; // Ambil nilai timer (durasi satu putaran pulser)
TCNT1 = 0; // Reset timer untuk pengukuran berikutnya
// 3. Hindari pembagian dengan nol atau nilai terlalu kecil
if (pulse_duration_ticks < 10) { // Jika durasi terlalu pendek (RPM terlalu tinggi / noise)
return; // Lewati siklus ini dan tunggu pulser berikutnya
}
// 4. Hitung indeks kurva berdasarkan RPM
// Nilai 30000 / pulse_duration_ticks ini adalah proxy untuk RPM
// Semakin cepat putaran, pulse_duration_ticks semakin kecil, curve_index semakin besar
curve_index = 30000 / pulse_duration_ticks;
if (curve_index >= 30) {
curve_index = 29; // Batasi indeks agar tidak melebihi ukuran array (0-29)
}
// 5. Hitung waktu per derajat (dalam tick timer)
time_per_degree_ticks = pulse_duration_ticks / 360; // Asumsi pulser 1 pulse/revolution
// 6. Hitung sudut spark yang diinginkan
// pulser_angle (sudut pulser relatif terhadap TMA) - advance_map (advance yang diinginkan)
spark_advance_deg = pulser_angle - map1[curve_index];
if (spark_advance_deg < 1) {
spark_advance_deg = 1; // Pastikan sudut spark minimal 1 derajat
}
// 7. Hitung waktu spark (dalam tick timer) dari titik pulser
spark_time_ticks = time_per_degree_ticks * spark_advance_deg;
// 8. Konversi dwell time dari mikrodetik ke tick timer
dwell_ticks = dwell_time_us[curve_index] / 4; // 1 tick = 4 mikrodetik
// 9. Hitung kapan harus memulai charging koil (dwell_start)
// Dwell start = spark_time - dwell_time
dwell_start_ticks = (spark_time_ticks > dwell_ticks) ? (spark_time_ticks - dwell_ticks) : 0;
// 10. Indikator LED (nyala tiap 8 siklus)
if (++led_counter == 8) {
PORTB |= (1 << PB5); // Nyalakan LED (PB5 HIGH)
_delay_ms(10); // Tunggu sebentar
PORTB &= ~(1 << PB5); // Matikan LED (PB5 LOW)
led_counter = 0; // Reset counter
}
// 11. Kontrol TCI (Transistor Controlled Ignition)
// Tunggu sampai waktu memulai charging koil
while (TCNT1 < dwell_start_ticks);
PORTB |= (1 << PB1); // Nyalakan koil (PB1 HIGH)
// Tunggu sampai waktu spark
while (TCNT1 < spark_time_ticks);
PORTB &= ~(1 << PB1); // Matikan koil (PB1 LOW) -> SPARK!
}
// Fungsi main, ini adalah titik masuk program
int main() {
setup(); // Panggil fungsi setup untuk inisialisasi
while (1) {
loop(); // Panggil fungsi loop secara terus-menerus
}
return 0; // Program tidak akan mencapai sini dalam operasi normal
}
Is this what you mean by code that uses setup() and loop() like the code above?
The ATmega328P MCU of the Arduino NANO Board has 32 general purpose registers (r0 - r31) whic are all 8-bit wide though r26-r27, r28-r29, r30-r31 can be cascaded to form 16-bit pointer registers.
Thank you for your question. Honestly, I don't fully understand the difference between 8-bit data and 32-bit data. Can you explain further, or perhaps provide an example that differentiates the two?
You don't need to include the above codes in your sketch as the main() function is automatically executed and then the control goes to setup() function and then to an endless for() loop from where the user loop() function is recurrently called upon.
#include <Arduino.h>
int main(void) {
init(); // Initialize hardware (e.g., timers, I/O, etc.)
#if defined(USBCON)
USBDevice.attach(); // If using USB-capable board, attach USB device
#endif
setup(); // Call the user-defined setup() function
for (;;) {
loop(); // Repeatedly call the user-defined loop() function
// Optionally allow background tasks or power saving
if (serialEventRun) serialEventRun();
}
return 0; // This is never reached
}
Codes of init function:
void init(void)
{
#if defined(__AVR_ATmega8__)
// Disable analog comparator
ACSR = _BV(ACD);
#endif
// Set up timer 0 for normal operation
TCCR0A = 0;
TCCR0B = _BV(CS01) | _BV(CS00); // prescaler = 64
// Enable Timer 0 overflow interrupt
TIMSK0 = _BV(TOIE0);
#if defined(__AVR_ATmega8__)
// Timer 1 is used for measuring pulse widths
TCCR1B = _BV(CS11); // prescaler = 8
// Timer 2 is for delay()
TCCR2 = _BV(CS22); // prescaler = 64
ASSR = 0;
TIMSK = _BV(TOIE2);
#else
// Timer 1 is used for PWM (analogWrite on pins 9 and 10)
TCCR1A = 0;
TCCR1B = _BV(CS11); // prescaler = 8
// Timer 2 is used for PWM (analogWrite on pins 3 and 11)
TCCR2A = _BV(WGM21) | _BV(WGM20); // Fast PWM mode
TCCR2B = _BV(CS22); // prescaler = 64
TIMSK2 = _BV(TOIE2); // Enable Timer2 overflow interrupt
#endif
// Set the prescaler for the ADC
sbi(ADCSRA, ADPS2);
sbi(ADCSRA, ADPS1);
sbi(ADCSRA, ADPS0);
// Enable the ADC
sbi(ADCSRA, ADEN);
// Initialize the standard GPIO states
sei(); // Enable global interrupts
}
Have you migrated from Assembly Language to Arduino Language?
Thanks for your question. Honestly, I don't fully understand [concept/difference]. Can you explain further, or perhaps provide an example to help me understand it better?
A memory chip contains many small slots called memory locations. A memory location usually contains 8-bit data (Fig-1).
Figure-1:
To hold 32-bit data, you need four consecutive memory locations.
Therefore, to acquire 32-bit data from a memory chip, the MCU is required to perform 4 memory-read operations/cycles.
The flash, EEPROM, and RAM memory blocks of the microcontroller of Arduino NANO Board are of 8-bit wide (Fig-1).
No, I have not migrated from Assembly Language to Arduino Language. Actually, I am focusing on learning and deepening Arduino Language first. I know that Assembly Language is a low-level language that is very close to the hardware, while Arduino Language (which is based on C/C++) is an abstraction that makes it easier to program microcontrollers.
Maybe in the future, after I master Arduino well, I will try to understand how Arduino code is translated to Assembly, or even try Assembly programming directly for very specific tasks that require high optimization. However, for now, my focus is on Arduino Language
Then you should write the following Arduino codes in place of DDRB = 0x22;
//22 = 0010 0010
pinMode(8, INPUT);
pinMode(9, OUTPUT);
pinMode(10, INPUT);
pinMode(11, INPUT);
pinMode(12, INPUT);
pinMode(13, OUTPUT);
Can you write Arduino codes for the following instruction taken from your post #1?
while(PINB & 0x01) == 0x00);
In the above, which input port-pin are you testing? Is it DPin-8 (PB0)? If yes, then the Arduino codes are:
while(digitalRead(8) != HIGH)
{
; //wait
}
If I change my code to 8-bit data, will it work fine?
Leave the data sizes of your identifiers/variables as they are.
Just re-format your sketch of post #1 using Arduino codes.
You may read the attached file to get some idea on the Arcitecture of ATmega328P MCU and the Arduino UNO/NANO Board.
Ch-2 ArchitectureLec.pdf (738.0 KB)
#define PULSER_INPUT_PIN 8 // Pin D8 pada Arduino Nano/Uno (terhubung ke PB0)
#define COIL_OUTPUT_PIN 9 // Pin D9 pada Arduino Nano/Uno (terhubung ke PB1)
#define LED_INDICATOR_PIN 13 // Pin D13 pada Arduino Nano/Uno (terhubung ke PB5, LED bawaan)
Is this what you mean?
Exactly!
thanks, so i have to use arduino code language in my code
Yes1
And yet, you might be required to write register level codes like
TCNT1 = 0x00 as there is no Arduino equivalent.
Thank you Professor Mostafa! Much better said that I could ever do. Seriously, the OP needs to take some introductory computer science classes at a community college, or a similar place. It is really hard to know how to give advice without using that sort of knowledge to base it on.
