Oscilloscope Example Code

To demonstrate how to use the Oscilloscope module a light sensor target application will be used. The light sensor example is a rather generic data source where an Analog-to-Digital Converter is used to sample the sensor, and the example applies to a wide range of other data sources.

The example requires the following equipment and software:
To run the example, the following hardware setup is required: A picture of the setup is shown below.

To be able to view the light sensor data in the Oscilloscope module, the ATmega256RFR2 target has to be programmed with code that samples the light sensor and sends the data to the Embedded Debugger (EDBG) on the ATmega256RFR2 Xplained Pro over a serial interface. The EDBG then uses the Data Gateway Interface (DGI) to send the data to the host computer.

First, a new project for the target application code has to be set up in Atmel Studio.

Todo:
#include <avr/io.h>
#include <avr/interrupt.h>
uint16_t adc_value = 0;
volatile uint8_t send_data = 0;
void adc_init(void){
	// Internal 1.5V reference, ADC0 as single ended input
	ADMUX = (1 << REFS1);
	// Enable the ADC, auto triggered mode, interrupt on conversion finished
	ADCSRA |= (1<<ADEN) | (1<<ADATE) | (1<<ADIE);
	// Timer/Counter compare match A as trigger of ADC conversion
	ADCSRB |= (1<<ADTS1) | (1<<ADTS0);
	// Check that the reference is OK
	while (0x00 == (ADCSRB & (1 << REFOK)));
}
void spi_init(void){
	// Slave select (PB0), MOSI (PB2) and SCK (PB1) as output
	DDRB |= (1<<PINB0) | (1<<PINB2) | (1<<PINB1);
	//Slave select high (inactive)
	PORTB |= (1<<PINB0);
	// 2X mode, 4MHz SPI clock when CPU clock is 8MHz
	SPSR |= (1<<SPI2X);
	// Master mode, enable SPI module. 
	// Clock polarity and phase is kept at default (Sample on rising edge)
	SPCR = (1<<SPE) | (1<<MSTR);
}
void spi_send(uint8_t data){
	// Slave select low
	PORTB &= ~(1<<PINB0);
	// Write data to shift register
	SPDR = data;
	// Wait for the transmission to complete
	while (0x00 == (SPSR & (1<<SPIF)));
	// Slave select high
	PORTB |= (1<<PINB0);
}
void timer_init(){
	// Set TOP value for timer (output compare A value)
	OCR0A = 80;
	// Clear timer on compare match mode
	TCCR0A = (1<<WGM01);
	// Timer clocked by CPU clock, no prescaler
	TCCR0B = (1<<CS00);
}
ISR (ADC_vect){	
	// Store the light sensor sample
	adc_value = (ADC);
	// Clear timer interrupt flag to enable the next sample
	TIFR0 |= (1<<OCF0A);
	// Flag sending of data to host
	send_data = 1;
}
int main(void){
	timer_init();
	adc_init();
	spi_init();
	// Interrupts on
	sei();
	while (1){
		if (1 == send_data){
			send_data = 0;
			// Send the ADC value over SPI to the host
			// Only the 8 lower bits contain useful data
			spi_send(adc_value & 0xFF);
		}
	}
}

The code configures the ADC to take a new sample every 10th μs giving a sample rate of 100 kHz. This is achieved by using a timer that counts up to 80 before resetting. The code is based on the target CPU running on the internal 16 MHz clock with a clock prescaler of 2 (default) and the CKDIV8 fuse not set. The data samples are sent to the EDBG over the DGI SPI interface. The SPI interface is running at 4 MHz. The ATmega256RFR2 ADC is 10-bit but only the lower 8 bits contain useful data in this example.

Todo: Build the project/solution (F7).
Todo:
  • Open the project properties (right click the project in the Solution Explorer and select Properties)
  • On the Tool tab, select the appropriate tool and interface
Todo: Program the application into the target and start the debugging by selecting Continue (F5).
Todo: Open the Data Visualizer as an extension inside Atmel Studio by selecting it in the Tools menu.