/* 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 /* Shut down power to TIM1. */ static void shutdown_timer(void){ PRR |= _BV(PRTIM1); } /* Turn on power to the TIM1. */ static void startup_timer(void){ PRR &= ~_BV(PRTIM1); } 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); shutdown_timer(); } #elif BREV == 4 || BREV == 5 /* Setup pins for the LED. */ void init_led(void){ DDRD |= _BV(PD4); DDRD |= _BV(PD3); shutdown_timer(); } #endif static volatile uint16_t LightLevel; static volatile uint16_t *LightResult; #if BREV == 2 /* Perform a light measurement. This function returns * immediately, but the measurement can take up to * 260 ms to complete. * If result is not NULL, it will be set to the result of the measurement. */ void measure_light(uint16_t volatile *result){ if(LED_MEASURING == LedState)return; RequestedState = LedState; LedState = LED_MEASURING; /* Setup pins. */ DDRE &= ~_BV(PE0); /* Set green led pin to Hi-Z. */ PORTE &= ~_BV(PE0); DDRD |= _BV(PD3); /* Set red led pin high. */ PORTD |= _BV(PD3); DDRD |= _BV(PD4); /* Set anode pin low. */ PORTD &= ~_BV(PD4); _delay_us(10); DDRD &= ~_BV(PD3); /* Set red led pin high-impedance. */ PORTD &= ~_BV(PD3); /* Setup timer and overflow interrupt. */ startup_timer(); TCNT1H = 0; TCNT1L = 0; TIMSK1 = _BV(TOIE1); TCCR1B = _BV(CS11); /* Start timer with Clk/8 */ /* Setup pin change interrupt PCINT19. */ PCICR |= _BV(PCIE2); PCMSK2 |= _BV(PCINT19); LightResult = result; } #elif BREV == 4 || BREV == 5 /* Perform a light measurement. This function returns * immediately, but the measurement can take up to * 260 ms to complete. * If result is not NULL, it will be set to the result of the measurement. */ void measure_light(uint16_t volatile *result){ if(LED_MEASURING == LedState)return; RequestedState = LedState; LedState = LED_MEASURING; /* Setup pins. */ DDRD |= _BV(PD4); /* Set led anode and photodiode cathode high. */ PORTD |= _BV(PD4); DDRE |= _BV(PE0); PORTE |= _BV(PE0); DDRD |= _BV(PD3); /* Set photodiode anode low. */ PORTD &= ~_BV(PD3); _delay_us(10); /* Setup timer and overflow interrupt. */ startup_timer(); TCNT1H = 0; TCNT1L = 0; TIMSK1 = _BV(TOIE1); TCCR1B = _BV(CS10); /* Start timer with Clk/1 */ DDRD &= ~_BV(PD3); /* Set photodiode anode high-impedance. */ PORTD &= ~_BV(PD3); /* Setup pin change interrupt PCINT19 (PD3). */ PCICR |= _BV(PCIE2); PCMSK2 |= _BV(PCINT19); LightResult = result; } #endif /* Pin changed; copy the time from the timer. */ ISR(PCINT2_vect){ TCCR1B = 0; /* Stop timer. */ LightLevel = TCNT1L; LightLevel |= (uint16_t) TCNT1H << 8; ATOMIC_BLOCK(ATOMIC_RESTORESTATE){ if(LightResult != NULL)*LightResult = LightLevel; } PCMSK2 &= ~_BV(PCINT19); /* Disable pin-change interrupt. */ /* Restore led state. */ LedState = RequestedState; set_led_visible(LedState); shutdown_timer(); } /* Timer overflowed before the pin changed; it must be really dim in here. */ ISR(TIMER1_OVF_vect){ TCCR1B = 0; /* Stop timer. */ LightLevel = 65535; ATOMIC_BLOCK(ATOMIC_RESTORESTATE){ if(LightResult != NULL)*LightResult = LightLevel; } PCMSK2 &= ~_BV(PCINT19); /* Disable pin-change interrupt. */ /* Restore led state. */ LedState = RequestedState; set_led_visible(LedState); shutdown_timer(); } /* Get the result of a previous light measurement. * A low value indicates bright light. * If no previous measurement is available, or if a measurement * is in progress, 0 is returned. */ uint16_t read_light(void){ uint16_t light_local; light_local = LightLevel; LightLevel = 0; return light_local; }