Temperature Controlled Box – Source Code


#include <asf.h>
#include <stdio.h>

static volatile int16_t last_temperature;
#define INPUT_PIN              ADCCH_POS_PIN1
#define OUTPUT_STR_SIZE        40
struct pwm_config mypwm[4];
int32_t destemp;
#define MY_LED0     IOPORT_CREATE_PIN(PORTA, 16)
#define MY_fan                  IOPORT_CREATE_PIN(PORTA,19)
#define MY_LED1     IOPORT_CREATE_PIN(PORTA, 17)
static void adc_handler(ADC_t *adc, uint8_t ch_mask, adc_result_t result)
{
#ifdef CONF_BOARD_OLED_UG_2832HSWEG04
gfx_mono_draw_filled_rect(0,0,128,32,GFX_PIXEL_CLR);
#endif
int32_t temperature;
char out_str[OUTPUT_STR_SIZE];

/* Compute current temperature in Celsius, based on linearization
* of the temperature sensor adc data.
*/
ioport_init();

ioport_set_pin_dir(MY_LED0, IOPORT_DIR_OUTPUT);
ioport_set_pin_dir(MY_LED1, IOPORT_DIR_OUTPUT);
if (result > 697) {
temperature = (int8_t)((-0.0295 * result) + 40.5);
} if (result > 420) {
temperature = (int8_t)((-0.0474 * result) + 53.3);
} else {
temperature = (int8_t)((-0.0777 * result) + 65.1);
}

last_temperature = temperature;

// Write temperature to display
last_temperature = last_temperature * 1.8 + 32;

if (last_temperature-2>destemp)
{

snprintf(out_str, OUTPUT_STR_SIZE, "Temperature:%1d F AC  ", last_temperature);
gpio_set_pin_low(LED0);
gpio_set_pin_high(LED1);
ioport_set_pin_level(MY_LED0, 3.3);
ioport_set_pin_level(MY_fan, 5);
ioport_set_pin_level(MY_LED1, 0);

}
else if (last_temperature+2<destemp)
{

snprintf(out_str, OUTPUT_STR_SIZE, "Temperature:%1d F Heat", last_temperature);
gpio_set_pin_low(LED1);
gpio_set_pin_high(LED0);
ioport_set_pin_level(MY_LED0, 0);
ioport_set_pin_level(MY_fan, 0);
ioport_set_pin_level(MY_LED1, 3.3);
}
else// if(last_temperature==destemp || (last_temperature-2>=destemp && last_temperature+2<=destemp))
{

snprintf(out_str, OUTPUT_STR_SIZE, "Temperature:%1d F off ", last_temperature);
gpio_set_pin_high(LED0);
gpio_set_pin_high(LED1);
ioport_set_pin_level(MY_LED0, 0);
ioport_set_pin_level(MY_fan, 0);
ioport_set_pin_level(MY_LED1, 0);

}
//            snprintf(out_str, OUTPUT_STR_SIZE, "Temperature: %2d F", last_temperature);
gfx_mono_draw_string(out_str, 0, 0, &sysfont);

// Start next conversion.
adc_start_conversion(adc, ch_mask);
}

static void adc_handler2(ADC_t *adc, uint8_t ch_mask, adc_result_t result)
{
#ifdef CONF_BOARD_OLED_UG_2832HSWEG04
gfx_mono_draw_filled_rect(0,0,128,32,GFX_PIXEL_CLR);
#endif
char out_str[OUTPUT_STR_SIZE];
if(result>= 65000){
result = 64;
}
result = (int)(result/22);
if(result < 64){
result = 64;
}
destemp=result;
snprintf(out_str, OUTPUT_STR_SIZE, "Desired Temp: %1d F   ", result);
//result = (int)result/1.2;
//gfx_mono_draw_filled_rect(0,17,result,7,GFX_PIXEL_SET);
//gfx_mono_draw_filled_rect(result,17,128-result,7,GFX_PIXEL_CLR);

gfx_mono_draw_string(out_str, 0, 20, &sysfont);
adc_start_conversion(adc, ch_mask);
}

int main(void)
{
struct adc_config         adc_conf;
struct adc_config adc_conf2;
struct adc_channel_config adcch_conf;
struct adc_channel_config adcch_conf2;

board_init();
sysclk_init();
sleepmgr_init();
irq_initialize_vectors();
cpu_irq_enable();
gfx_mono_init();

// Enable backlight if display type is not OLED
#ifndef CONF_BOARD_OLED_UG_2832HSWEG04
ioport_set_pin_high(LCD_BACKLIGHT_ENABLE_PIN);
#endif

// Initialize configuration structures.
adc_read_configuration(&ADCA, &adc_conf);
adcch_read_configuration(&ADCA, ADC_CH0, &adcch_conf);

adc_read_configuration(&ADCB, &adc_conf2);
adcch_read_configuration(&ADCB, ADC_CH1, &adcch_conf2);

/* Configure the ADC module:
* - unsigned, 12-bit results
* - VCC voltage reference
* - 200 kHz maximum clock rate
* - manual conversion triggering
* - temperature sensor enabled
* - callback function
*/
adc_set_conversion_parameters(&adc_conf, ADC_SIGN_ON, ADC_RES_12,
ADC_REF_VCC);
adc_set_clock_rate(&adc_conf, 200000UL);
adc_set_conversion_trigger(&adc_conf, ADC_TRIG_MANUAL, 1, 0);
adc_enable_internal_input(&adc_conf, ADC_INT_TEMPSENSE);

adc_set_conversion_parameters(&adc_conf2, ADC_SIGN_ON, ADC_RES_12,
ADC_REF_VCC);
adc_set_clock_rate(&adc_conf2, 200000UL);
adc_set_conversion_trigger(&adc_conf2, ADC_TRIG_MANUAL, 1, 0);
adc_enable_internal_input(&adc_conf2, ADC_INT_TEMPSENSE);

adc_write_configuration(&ADCA, &adc_conf);
adc_set_callback(&ADCA, &adc_handler);

adc_write_configuration(&ADCB, &adc_conf2);
adc_set_callback(&ADCB, &adc_handler2);

/* Configure ADC channel 0:
* - single-ended measurement from temperature sensor
* - interrupt flag set on completed conversion
* - interrupts disabled
*/
adcch_set_input(&adcch_conf, ADCCH_POS_PIN1, ADCCH_NEG_NONE,
1);
adcch_set_interrupt_mode(&adcch_conf, ADCCH_MODE_COMPLETE);
adcch_enable_interrupt(&adcch_conf);

adcch_write_configuration(&ADCA, ADC_CH0, &adcch_conf);

// Enable the ADC and start the first conversion.
adc_enable(&ADCA);
adc_start_conversion(&ADCA, ADC_CH0);

adcch_set_input(&adcch_conf2, ADCCH_POS_PIN1, ADCCH_NEG_NONE,
1);
adcch_set_interrupt_mode(&adcch_conf2, ADCCH_MODE_COMPLETE);
adcch_enable_interrupt(&adcch_conf2);

adcch_write_configuration(&ADCB, ADC_CH1, &adcch_conf);

// Enable the ADC and start the first conversion.
adc_enable(&ADCB);
adc_start_conversion(&ADCB, ADC_CH1);

do {
// Sleep until ADC interrupt triggers.
sleepmgr_enter_sleep();
} while (1);
}

Defect Detector – Code

The most interesting part of the code for our project was, without a doubt, the code used to filter out false positives read by the ADC. There were two main methods we utilized to do this:

  • Get a good threshold value
// in main

int baseline_reading[25];
for (int i = 0; i < 25; i++) {
    _delay_ms(100);
    baseline_reading[i] = last_result;
}
THRESHOLD = get_threshold(baseline_reading, 25);

CUTOFF = THRESHOLD + OFFSET;

/* … */

int get_threshold(int readings[], int read_count)
{
    float xbar = 0.0, s = 0.0;
    for (int i = 0; i < read_count; i++) {
        xbar += readings[i];
    }
    xbar /= read_count;
    for (int i = 0; i < read_count; i++) {
        s += squared(readings[i] - xbar);
    }
    s /= read_count - 1;
    s = sqrt(s);
    return (int) (xbar + 0.75 * s);
}

The first method employed is simply setting a good threshold that the readings must surpass in order for the program to act upon them. To obtain this threshold a number of readings are taken at the start of the program. These preliminary readings are then used to get a good baseline for the threshold by finding the arithmetic mean of them and adding a fraction of a standard deviation. The precise number of standard deviations we added was arrived at through trial and error.

  • Ensure that the reading is consistent
do {
     // busy loops 200 ms at a time until a sufficiently large weight is placed on the sensor
     int streak = 0;
     do {
         _delay_ms(100);
         if (last_result < THRESHOLD)
             streak = 0;
         else
             streak++;
     } while (streak < STREAK_THRESHOLD);
     streak = 0;
     // sorts the object appropriately
     sort_object(&mypwm, last_result, CUTOFF);
} while (1);

The other method employed is to ensure that the readings we get from the ADC are consistently significant. To do this, instead of calling the sort routine whenever a result is significant, we only call the sort routine when each of the last N results have been significant. This helps to filter out fleeting spikes of voltage read by the ADC whether they are from simple noise or from the spike that results when an object is dropped onto the sensor rather than placed on top of it.

Anagram Solver Code Post and Schematic (Interesting Aspect) – Team SilverCharm

Our final project was the Anagram Solver we have many interesting aspects to our project. Some of these high level aspects include:

  • Controlling the LCD on the A3BU
  • Asking the USER for Input
  • Flashing LED’s for correct/incorrect answers
  • Flashing LED for inputs
  • Generating results based on C programming

Although we have so many interesting features, one of our best features is the crazy light show! After exiting the program, the program flashes both the blue and red LED’s and the LCD backlight in a spectacular fashion. It is truly an amazing aspect of our project!

Below is our schematic made from our board.

MIDI Harp: ADC Settings

One challenging aspect of our project was configuration of the analog-to-digital converter (ADC) for maximum stability and playability. Taking inspiration from Atmel Software Framework example projects, various Atmel publications, and a vast chorus of AVR-forum participants, we included the following ADC initialization function in our final project:

//initialize free running ADC sweep
void adc_init(void)
{
	// Initialize configuration structures.
	struct adc_config         adc_conf;
	struct adc_channel_config adcch_conf;
	
	adc_read_configuration(&amp;ADCB, &amp;adc_conf);
	adcch_read_configuration(&amp;ADCB, ADC_CH0, &amp;adcch_conf);

	adc_set_conversion_parameters(&amp;adc_conf, ADC_SIGN_ON, ADC_RES_12, ADC_REF_AREFB) ;
	adc_set_clock_rate(&amp;adc_conf, 5000UL);
	adc_set_conversion_trigger(&amp;adc_conf, ADC_TRIG_FREERUN_SWEEP, 3, 0);
	adc_set_callback(&amp;ADCB, &amp;adc_handler);
	adc_write_configuration(&amp;ADCB, &amp;adc_conf);

	adcch_enable_interrupt(&amp;adcch_conf);

	adcch_set_input(&amp;adcch_conf, ADCCH_POS_PIN1, ADCCH_NEG_NONE, 1);
	adcch_write_configuration(&amp;ADCB, ADC_CH0, &amp;adcch_conf);
	
	adcch_set_input(&amp;adcch_conf, ADCCH_POS_PIN2, ADCCH_NEG_NONE, 1);
	adcch_write_configuration(&amp;ADCB, ADC_CH1, &amp;adcch_conf);

	adcch_set_input(&amp;adcch_conf, ADCCH_POS_PIN3, ADCCH_NEG_NONE, 1);
	adcch_write_configuration(&amp;ADCB, ADC_CH2, &amp;adcch_conf);
}

This code utilizes ASF to allow for human readable (and Intellisense-aided!) modification, without requiring the bit by bit setting of appropriate AVR registers. Our project was configured for a free-running, single-ended, 12-bit, interrupt-triggering sweep of 3 ADC channels, referenced against an external 2.5V source, at the modest frequency of 5 kHz. Once the ADC is enabled, each finished conversion calls a function that averages the most recent 15 values, and translates this result into a “state” (from which is derived a tone) and volume (from height within the trigger range).

MIDI Harp: Incorporating LUFA into an Atmel Project

LUFA is a USB communication library used extensively in the MIDI Harp project. We found incorporating LUFA with ASF libraries to be a bit of a chore, so below we have detailed the steps that will get you through modifying your project so you can get on our level.

The first step is to download the LUFA software pack for Atmel Studio 7, which can be found here: https://gallery.atmel.com/Products/Details/d1057c19-3815-487e-baa7-0001985c6901.

Once you have this fully installed, create a new temperature sensor example project for the A3BU (We started here as we knew this included everything for utilizing ADC).  Import the required LUFA modules, shown below, via ASF Wizard.

Final ASF Wizard

Now, ASF and LUFA employ conflicting numerical definitions to indicate the feature-set of various models of Atmel boards.  To correct this, you first need to go to your project’s properties by right clicking the orange project folder in the Solution Explorer, clicking properties, and going to the toolchain tab. Under AVR/GNU C Compiler->Symbols you will discover contradicting BOARD definitions from ASF and LUFA respectively.  We found it easiest to keep the definition used by Atmel and update the LUFA files accordingly.

Toolchain Symbols fixed

Two additional symbols must be created with the green + button as follows: F_USB=48000000UL and F_CPU=32000000UL

With BOARD=BOARD_A3BU_XPLAINED deleted and BOARD=XMEGA_A3BU_XPLAINED remaining, you will need to make a series of changes to the files associated with LUFA.  First, in the Solution Explorer, go to src/LUFA/LUFA/Common/ and open BoardTypes.h.  Two lines have to be commented here to correct the conflicting definitions.  First, comment line 160, “#define BOARD_A3BU_XPLAINED 31”.  This removes LUFA’s numerical definition for the board definition we are no longer using.  Then, comment line 148, “#define BOARD_EVK1101 27”.  This conflicts with Atmel because XMEGA_A3BU_XPLAINED is defined as 27, and gets mixed up with this other board definition from LUFA.  LUFA files then attempt to set up the project for the EVK1101 board instead of A3BU.

BoardTypes Fixed

Next, go into Solution Explorer src/LUFA/LUFA/Drivers/Board/ and open Buttons.h, Dataflash.h, and LEDs.h. (There should also be a folder called XMEGA. If this is not in the folder with the above files, create the LUFA example project “MIDI Device Demo (Class Driver APIs) – XMEGA Architecture”, and copy this folder into this location.)  Within each of these files, find a line that looks similar to #elif (BOARD == BOARD_A3BU_XPLAINED) and change BOARD_A3BU_XPLAINED to XMEGA_A3BU_XPLAINED as shown below.

Buttons fixed

Once each of these files have been changed, you may close these files.  Now go to src/config/LUFAConfig.h and make the changes shown below to the XMEGA section starting at line 91:

LUFAConfig Fixed

These will initially all be commented out.  Uncomment the lines shown and add the values shown to the right (lines 106, 113,116, 118, and 120).

Finally, you will need to copy the files Descriptors.h, Descriptors.c, and MIDI.h from the src folder of the MIDI example project mentioned above, and put them into the src folder for your project. There are several functions which can be found in MIDI.c from this example project that must be used to make the USB connection and send MIDI signals.  Copying these files allows the necessary functions to work properly.  Then make sure to include these files in the Solution Explorer so your project knows where to find them.

src Folder with added files

You should copy code sections into your project from the MIDI demo project’s MIDI.c file that initialize and facilitate USB communication, including one to manage clock sources and speeds, while removing a conflicting initialization of ASF’s system clock management module.  After that, you’re good to go!  We’ll leave the rest of the coding to you.  Have fun with your MIDI harp!