/* Soil Sensor Firmware * Light Measurement & LED Status Indication * Sensiplicity Systems * * Copyright 2015 * * Department of Botany and Plant Pathology * Center for Genome Research and Biocomputing * Oregon State University * Corvallis, OR 97331 * * This program is not free software; you can not redistribute it and/or * modify it at all. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. */ #include "light.h" #include #include #define F_CPU 2000000 #include "util/delay.h" #ifndef BREV #error "No board revision specified. Please provide -DBREV=2 or -DBREV=4 on the command line." #endif #if BREV != 2 && BREV != 4 && BREV!=5 #error "Incorect board revision specified. Please provide -DBREV=2, -DBREV=4, or -DBREV=5 on the command line." #endif static volatile enum led_state_t LedState, RequestedState; #if BREV == 2 /* Set the led to a visible state. */ void set_led_visible(enum led_state_t state){ init_led(); /* Turn the anode on. */ PORTD |= _BV(PD4); /* Turn LED off, then selectively turn on the required bits. */ PORTD |= _BV(PD3); PORTE |= _BV(PE0); switch (state){ case(LED_OFF): break; case(LED_GREEN): PORTE &= ~_BV(PE0); break; case(LED_RED): PORTD &= ~_BV(PD3); break; case(LED_MEASURING): break; } } #elif BREV == 4 || BREV == 5 /* Set the led to a visible state. */ void set_led_visible(enum led_state_t state){ init_led(); /* Turn the anode on. */ PORTD |= _BV(PD4); /* Make the photodiode high impedance. */ DDRD &= ~_BV(PD3); if(state == LED_GREEN || state == LED_RED){ PORTE &= ~_BV(PE0); } else { PORTE |= _BV(PE0); } } #endif /* Set the LED state. This function will only set * LED_OFF, LED_RED, and LED_GREEN. * This function can be safely called while a measurement is * in progress; the state will be set when the measurement is * complete. */ void set_led(enum led_state_t state){ if(LED_MEASURING == state)return; RequestedState = state; if(LED_MEASURING != LedState){ set_led_visible(state); } } #if BREV == 2 /* Setup pins for the LED. */ void init_led(void){ DDRD |= _BV(PD4); DDRD |= _BV(PD3); DDRE |= _BV(PE0); } #elif BREV == 4 || BREV == 5 /* Setup pins for the LED. */ void init_led(void){ DDRD |= _BV(PD4); DDRD |= _BV(PD3); } #endif #if BREV == 2 #elif BREV == 4 || BREV == 5 /* Seconds per measure() iteration numerator and denominator. */ #define SPI_N 912ULL #define SPI_D 130170000ULL /* Amount of charge moved by photodiode in draining capacitor * from 5V to the threshold voltage. */ #define dQ_N 315ULL #define dQ_D 10000000000ULL /* Maximum count */ #define MAXC 2000000ULL /* nA to nW/cm^2 conversion factor. */ #define nA_to_nW 100 /* Measure the time taken for the anode to reach the logic 1 threshold, * up to maximum count. */ uint32_t measure(void){ uint32_t count = 0; while(count < MAXC && (PINE & _BV(PE0))){ count++; } return count; } /* Perform a light measurement. */ uint32_t measure_light(void){ /* Setup pins: led = hiz, anode = low */ DDRD &= ~_BV(PD4); PORTD &= ~_BV(PD4); DDRD |= ~_BV(PD3); PORTD &= ~_BV(PD3); /* Charge cathode to 5V, measure discharge time to 2.5V. */ PORTE |= _BV(PE0); DDRE |= _BV(PE0); __builtin_avr_delay_cycles(20); DDRE &= ~_BV(PE0); PORTE &= ~_BV(PE0); uint32_t count = measure(); PORTE |= _BV(PE0); DDRE |= _BV(PE0); float A = (3.15e-8/((float)count * (.0912/13017.0))); return (uint32_t)(A * 1e9 * 100); /* The simple approach in r5 is working pretty well, but it can be improved. * Sources of error (after factory calibration): * -drift in photodiode sensitivity (unfixable, but unlikely) * -change (especially increase) in leakage (pot in epoxy, use minimum amount of flux) * -change in capacitor value (unlikely based on NP0 spec) * -change in threshold voltage (totally uncontrolled) * Any changes in the threshold voltage cannot be assumed to be consistent among pins. * Therefore, the change in threshold voltage on that pin must be measured. Do this by adding * an extra resistor between a pin and the measurement node (PE0). Usually, the pin is * hi-z, so the resistor doesn't affect the measurement. During a threshold measurement, * the diode is disconnected on its other side with a high-z pin. Then the pin connected * to the resistor is pulled low, and its discharge time is measured. The resistor should * be around 500k. */ } #endif