6.3.3 Using Horizontal Cursor Code

So far, the Graph module of the Data Visualizer has been used to show the data generated by the light sensor and to show when the Night mode switch toggles between the two modes. The Graph module can also be used to interact with the target application while it is running. In this example, the Night mode threshold can be adjusted dynamically by using a horizontal cursor.

First, the code must be extended to accept incoming data on the CDC USART. The output of the horizontal cursor is a 4-byte float value and will be sent over the CDC interface to the target application. This float value will be used as the threshold for the Night mode switch.

#include <avr/io.h>
#include <avr/interrupt.h>
const char* message_on = "NIGHT MODE ON";
const char* message_off = "NIGHT MODE OFF";
union u_float{
	float flt;
	char data[4];
	};
uint16_t adc_value = 0;
uint8_t nightmode_threshold;
uint8_t nightmode_active = 0;
union u_float cdc_received_data;
uint8_t cdc_read_index=0;
ISR (USART1_RX_vect){
	// A byte is received on the CDC UART, MSB first
	cdc_received_data.data[cdc_read_index] = UDR1 & 0xFF;
	if (3 == cdc_read_index){
		// A complete float value is received
		nightmode_threshold = (uint8_t) cdc_received_data.flt;
		cdc_read_index = 0;
	}
	else {
		cdc_read_index++;
	}
}
void adc_init(void){
	// Internal 1.5V reference, ADC0 as single ended input
	ADMUX = (1 << REFS1);	
	// Enable the ADC, 
	ADCSRA |= (1<<ADEN);
	// Check that the reference is OK
	while (0x00 == (ADCSRB & (1 << REFOK)));	
}
uint16_t adc_sample(void){	
	// Trigger an ADC conversion
	ADCSRA |= (1<<ADSC);	
	// Wait for conversion to finish
	while (0x00 == (ADCSRA & (1 << ADIF)));	
	// Clear the interrupt flag
	ADCSRA |= (1 << ADIF);
	return (ADC);
}
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);
	// 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 cdc_init(void){
	// Baud rate 9600 based on 8 MHz CPU clock
	UBRR1 = 51;
	// Enable the transmitter and receiver, 8 bit character size,
	// receive interrupts enabled
	UCSR1B = (1<<RXEN1) | (1<<TXEN1) | (1<<RXCIE1);
}
void cdc_send(const char data){
	// Wait for transmitter to be ready for more data
	while (0x00 == (UCSR1A & (1<<UDRE1)));
	// Send the data
	UDR1 = data;
}
void send_message(const char* message){
	while (*message)
		cdc_send(*message++);
	// String markers requires Null-termination
	cdc_send(0);
}
int main(void){
	adc_init();
	spi_init();
	cdc_init();		
	// Interrupts on
	sei();			
       while (1){
		adc_value = adc_sample();	
		// Send the ADC value over SPI to the host
		// Only the 8 lower bits contain useful data		
		spi_send(adc_value & 0xFF);		
		//  higher adc value == less light 
		if (adc_value > nightmode_threshold){		
			if (0x00 == nightmode_active){
				// Changing from nightmode inactive to active
				nightmode_active = 0x01;
				send_message(message_on);
			}
		} else {
			if (0x01 == nightmode_active){
				// Changing from nightmode active to inactive
				nightmode_active = 0x00;
				send_message(message_off);
			}
		}
	}
}
Todo:
  • Build the project, program and run the application by simply selecting Continue (F5) in the Debug menu of Atmel Studio