Skip to content

Instantly share code, notes, and snippets.

@edgar-bonet
Created June 21, 2017 20:03
Show Gist options
  • Save edgar-bonet/dbe7ce3be4fa8a8d04b93e70d5f7e552 to your computer and use it in GitHub Desktop.
Save edgar-bonet/dbe7ce3be4fa8a8d04b93e70d5f7e552 to your computer and use it in GitHub Desktop.
Dual channel Arduino/AVR oscilloscope
/*
* dual-scope.c: Dual channel Arduino/AVR oscilloscope.
*
* This program alternatively samples analog inputs 0 and 1, with an
* overall sampling rate of about 9615 samp/s (one sample every 104 us),
* i.e. 4808 samp/s per channel. The samples are output as 8-bit binary
* through the serial port at 115200/8N1. A fixed 16-byte ASCII prologue
* is sent at program startup, before the actual binary data, for
* synchronization purposes. The first byte sent after the prologue is a
* sample from channel 0.
*
* Program internals: The analog-to-digital converter is set to "free
* running mode". The ADC ISR transfers the samples through the serial
* port and switches the ADC multiplexer in order to sample both
* channels.
*
* Target platform: This program is intended for an Arduino Uno, but it
* does not use the Arduino libraries. It can be compiled either as a
* .ino "sketch" or as a plain C file. It is likely to work on most
* ATmega microcontrollers having an ADC and clocked at 16 MHz.
*
* Copyright (c) 2017 Edgar Bonet Orozco.
* Released under the terms of the MIT license:
* https://opensource.org/licenses/MIT
*/
#include <avr/io.h>
#include <avr/interrupt.h>
#include <avr/sleep.h>
#include <util/delay.h>
#define BAUD 115200 // serial port baud rate
#define BAUD_TOL 3 // increase the baud rate tolerance to 3%
#include <util/setbaud.h> // compute USE_2X and UBRR_VALUE
#define PROLOGUE "== STARTING ==\r\n"
// ADC interrupt service routine.
ISR(ADC_vect)
{
// Output the 8-bit ADC reading through the serial port.
UDR0 = ADCH;
// Switch between ADC channels 0 and 1.
ADMUX ^= _BV(MUX0);
}
int main(void)
{
// Configure the serial port.
UCSR0A = USE_2X << U2X0; // double speed if needed
UCSR0B = _BV(TXEN0); // enable only the transmitter
UCSR0C = _BV(UCSZ00) // character size = 8 bits
| _BV(UCSZ01); // ...ditto
UBRR0 = UBRR_VALUE; // set baud rate
// Send the prologue.
for (const char *p = PROLOGUE; *p; p++) {
UDR0 = *p; // output
loop_until_bit_is_set(UCSR0A, UDRE0); // flush
}
// Configure the ADC in "free running mode".
DIDR0 = _BV(ADC0D) // disable the digital inputs ADC0...
| _BV(ADC1D); // ...and ADC1
ADMUX = _BV(REFS0) // voltage reference = AVCC
| _BV(ADLAR) // left adjust result
| 0; // initial input channel = ADC0
ADCSRB = 0; // auto trigger source = self
ADCSRA = _BV(ADEN) // enable ADC
| _BV(ADSC) // start first conversion
| _BV(ADATE) // enable auto trigger
| _BV(ADIF) // clear interrupt flag
| _BV(ADIE) // enable interrupt
| 7; // prescaler = 128
_delay_us(9); // let the conversion start
ADMUX |= _BV(MUX0); // first channel switch
// Sleep while waiting for interrupts.
sei();
sleep_enable();
for (;;)
sleep_cpu();
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment