Continuous ADC Capture

This example shows how to setup the ADC on the ATMEGA 2560 to capture a burst of  data samples continuously. The ADC is configured to carry out conversions continuously and writes the results to an array in RAM. The array size is set to 2048 samples once the array is full the data is then transmitted over the serial connection to a host computer. The baud rate for the serial port is set to 38400baud. A matlab script that can be used to read the data from the serial port and plot the data is also provided. The ADC on the ATMEGA2560 is a 10bit ADC however to speed up the capture and serial transmission only the 8 most significant bits are stored thus the output is an 8bit value.

// Example of Free running Single Channel 8 bit ADC sampling to maximise the rate. The ADC interrupt is used to store the 8 bit samples in a RAM data buffer
// once the data buffer is full the ADC is disabled and the contents of the data buffer are sent over the USART and USB connection
// to a host computer.

#include <avr/io.h>
#include <stdint.h>
#include <avr/interrupt.h>

#define F_CPU 16000000UL
#define BAUDRATE 38400
#define BUFFER_SIZE 2048

#define FALSE 0
#define TRUE 1
volatile uint16_t bufferIndex=0; // this variable is used to store the current buffer index the volatile keyword is required as this variable is accessed within the interrupt and main code
uint16_t i=0;
volatile uint8_t data[BUFFER_SIZE];
volatile uint8_t flag=TRUE;
void adc_init(void);
void timer1_init(void);
void timer0_init(void);
void USART_init(void);
void USART_send( unsigned char data);

int main(void){
 DDRB = (1 << PB7) | (1 << PB6); //Configure led pin
 sei(); //Enable global interrupts, so our interrupt service routine can be called

 if (!(flag)){
 PORTB |= (1 << PB6);
 for (i = 0; i < BUFFER_SIZE; i++){
 USART_send(data[i]); //Send data from buffer using the USART
 flag = TRUE;
 PORTB &= ~(1 << PB6);

ISR(ADC_vect){ //This is our interrupt service routine
 data[bufferIndex++] = ADCH; // Store the 8 most significant bits in the data buffer and increment the bufferIndex
 //PORTB ^= (1<<PB7); // Turn on pin 13 LED
 if (bufferIndex == BUFFER_SIZE){
 ADCSRA &= ~(1 << ADEN);
 flag = FALSE;
 //PORTB &= ~(1<<PB7); // Turn off pin 13 LED
void timer0_init(void){
 //Configure 8 bit timer in fast PWM
 TCCR0A = (1 << COM0B1) | (1 << WGM01) | (1 << WGM00); // Enable Fast PWM mode 3, mark first
 TCCR0B = (1 << CS02) |(0 << CS01) | (0 << CS00); //set clock prescaler to 16MHz/256 = 62.5kHz (PWM frequency = 244.1Hz)
 OCR0B = 128; //sets the desired pulse width (0-255)
 DDRG |= (1 << PG5); // Set pin 4 (PG5) as output


void adc_init(void){
 ADCSRA = ((1 << ADPS2) | (1 << ADPS1) | (0 << ADPS0)); //ADC conversion clock frequency divider to 64. ADC Clock 16MHz/64 = 250kHz
 ADMUX = (1 << REFS0) | (1 << ADLAR); //Set Voltage reference to Avcc (5v), Left adjust converted value
 DIDR0 = 0xFF; //Disable digital input registers on analogue inputs A0-7
 DIDR2 = 0xFF; //Disable digital input registers on analogue inputs A8-15
 ADCSRB &= ~((0 << ADTS2) | (0 << ADTS1) | (0 << ADTS0)); //Select free running. 13 ADC clock cycles per converson ADC sample rate 250kHz/13 = 19.23kS/s
 ADCSRA |= (1 << ADEN) | (1 << ADIE) | (1 << ADATE); //Turn on ADC, Enable interrupts, enable automatic triggering
 ADCSRA |= (1 << ADSC); //start conversion

void USART_init(void){
 // Macro to determine the baud prescale rate see table 22.1 in the Mega datasheet
#define BAUD_PRESCALER (((F_CPU / (BAUDRATE * 16UL))) - 1)

UBRR0 = BAUD_PRESCALER; // Set the baud rate prescale rate register
 UCSR0C = ((0<<USBS0)|(1 << UCSZ01)|(1<<UCSZ00)); // Set frame format: 8data, 1 stop bit. See Table 22-7 for details
 UCSR0B = ((1<<RXEN0)|(1<<TXEN0)); // Enable receiver and transmitter

void USART_send( unsigned char data)
 //while the transmit buffer is not empty loop
 while(!(UCSR0A & (1<<UDRE0)));

 //when the buffer is empty write data to the transmitted
 UDR0 = data;

Matlab function for reading the data from the serial port. Note that when Matlab opens the serial port the reset on the Arduino Mega is activated and the board resets itself. To ensure the correct operation the pause(3) is used to ensure the board is ready before reading data.

function ArduinoSerialRx
N = 2048;
port = '/dev/tty.usbmodem1411'; % Edit this to match the port name as shown in the arduino editor
obj=serial(port,'BaudRate', 38400,'InputBufferSize',2*N);
finishup = onCleanup(@() fclose(obj));

data = fread(obj,N,'uint8');


Fs = 19.2115e3;
time = (0:N-1)./Fs;
freq = [0:N/2-1,-N/2:-1].*(Fs/N);
xlabel('Time (s)')

Data = fft(data);
figure(2), plot(freq(1:N/2),20*log10(abs(Data(1:N/2))))
xlabel('Frequency (Hz)')
ylabel('Spectral Magnitude (dB)')

Leave a Reply

Fill in your details below or click an icon to log in: Logo

You are commenting using your account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s