// based on https://wokwi.com/projects/330112801381024338
// for https://forum.arduino.cc/t/oversampled-adc-pwm-synchronization-for-my-ball-on-beam-project-at-high-frequency-on-my-arduino-uno/1084814/18
volatile uint16_t AnalogReadings; // Wanted 10 bit value as a variable
const byte InputPin = A0; // Input 8-bit Voltage signal at A0
ISR(ADC_vect)
{
while(ADCSRA & (1<<ADSC)); // wait for conversion
ADC = (ADCL | (ADCH << 8)); // Bit shifting for 10-bit resolution from 8-bit
AnalogReadings = ADC;
}
ISR(TIMER1_COMPA_vect)
{
AnalogReadings = ADC;
ADCSRA |= (1<<ADSC); // Start the next conversion
}
ISR(Timer1_COMPB_vect)
{
AnalogReadings = ADC;
ADCSRA |= (1<<ADSC); // Start the next conversion
}
ISR(TIMER1_OVF_vect)
{
AnalogReadings = ADC;
TCNT1 = 0; // TCNT1 set to initialise again; maybe not needed as TCNT starts by itself
ADCSRA |= (1<<ADSC); // Event the conversion start
}
void setup()
{
Serial.begin(115200); // set baudrate
//CONFIGURE ADC
cli(); //disable all interrupts
ADCSRA = 0;
ADCSRB= 0; // Initialize ADC
ADMUX = 0;
// Enable Input channel A0;
ADMUX |= (1<<ADLAR); // (1<<ADLAR) for bit shifting for 8-10 bit conversion
ADMUX &= (ADMUX & 0xF8) | (InputPin); // AREF; so not bit to set
ADCSRA |= (1<<ADEN);
ADCSRA |= (1<<ADIF); // enable interrupt flag
ADCSRA |= (1<<ADIE); // enable interrupts
sei(); // enable global interrupts
ADCSRA |= (1<<ADPS2); // Prescalar of 16; 16MHz/ 16 = 1MHz/13 clock cycles= ~76Khz (ADC Sampling rate) = ~13 uS (one conversion period)
ADCSRB |= (1<<ADTS2) | (1<<ADTS0); //
ADCSRA |= (1<<ADSC); // Start the first conversion
while (ADCSRA & (1<<ADSC)); //wait for the completion of ADC conversion
cli(); // disable interrupts
// Timer Settings
TCCR1A = 0;
TCCR1B = 0; // Initialise timer1
TCNT1 = 0;
// choose phase and frequency correct PWM, TOP defined by ICR1
TCCR1A |= (1<<COM1A0) | (1<<COM1A1); // toggle outputs OC1A and OC1B
TCCR1A |= (1<<COM1B1); // OC1A should be logical negated to OC1B
TCCR1B |= (0b101<<CS10) | (1<<WGM13); // choose PWM mode (see above) and set prescaler to 1
// TCCR1B https://onlinedocs.microchip.com/pr/GUID-80B1922D-872B-40C8-A8A5-0CBE009FD908-en-US-3/index.html?GUID-07C6751D-0319-41A4-AC9F-9B0AFDA21A07
// prescaler /1024 for slower output
ICR1 = 255; // set TOP value to 255 --> resulting PWM-frequency = 16MHz / (2 * 1 * 255) = 31.372kHz = 32uS ;one pwm period
OCR1A = 128; //Duty cyycle 50%
OCR1B = 128; // for both signals
TIMSK1 |= (1<<OCIE1A)| (1<<OCIE1B) |(1<<TOIE1); // set 2 compare match interrupt & 1 overflow event masks
TIFR1 |= (1<<OCF1A)| (1<<OCF1B) |(1<<TOV1); // may not be needed as the flag is reset when the interrupt vector is initiated
sei(); // enable interrupts again
DDRB |= (1<<PB1) | (1<<PB2); // Enable output ports 9&10 digital PWM pin
ADCSRA |= (1<<ADATE);
Serial.println("Setup Complete");
// enable auto triggering mode
}
void loop()
{
while(1)
{
while(ADCSRA & ~(ADSC));
if(TIMSK1 & ~(1<<OCF1A))
{
Serial.println(AnalogReadings);
Serial.flush();
}
else if(TIMSK1 & ~(1<<OCF1B))
{
Serial.println(AnalogReadings);
Serial.flush();
}
else if(TIMSK1 & ~(1<<TOV1))
{
Serial.println(AnalogReadings);
Serial.flush();
}
}
}