diff --git a/build-number.txt b/build-number.txt index 065fd3e..964480f 100644 --- a/build-number.txt +++ b/build-number.txt @@ -1 +1 @@ -137 +213 diff --git a/src/light_sensor.c b/src/light_sensor.c new file mode 100644 index 0000000..2d79442 --- /dev/null +++ b/src/light_sensor.c @@ -0,0 +1,119 @@ +#include "light_sensor.h" + +volatile unsigned int LightSensor_Measurement; +volatile bool LightSensor_NewMeasurement = false; + +// Rolling average of the brightness measurement +float LightSensor_AbsoluteBrightness = 0.5f; + +// Maximum and minimum encountered so far +float LightSensor_MinimumBrightness = 1.0f; +float LightSensor_MaximumBrightness = 0.0f; + +float LightSensor_RelativeBrightness; + +static void LightSensor_Measure(void) +{ + // Use light sensor pin in output mode, drive high + GPIOB->MODER = (GPIOB->MODER & ~(0x03 << PIN_LIGHT_SENSOR * 2)) + | (0x01 << PIN_LIGHT_SENSOR * 2); + + // Disable timer + TIM14->SR = 0x0000; + TIM14->CR1 = 0x0000; + TIM14->CNT = 0; + + // Delay a bit to ensure capacitor is charged + for(unsigned int i = 0; i < 100; i++) + { + __asm__ volatile("nop"); + } + + // Re-enable timer + TIM14->CR1 = TIM_CR1_CEN; + + // Switch to alternate function mode + GPIOB->MODER = (GPIOB->MODER & ~(0x03 << PIN_LIGHT_SENSOR * 2)) + | (0x02 << PIN_LIGHT_SENSOR * 2); +} + +void LightSensor_Init(void) +{ + RCC->AHBENR |= RCC_AHBENR_GPIOBEN; + RCC->APB1ENR |= RCC_APB1ENR_TIM14EN; + + GPIOB->ODR |= (1 << PIN_LIGHT_SENSOR); + GPIOB->AFR[0] &= ~(0x0f << (PIN_LIGHT_SENSOR * 4)); + + // Enable input capture for light sensor pin, trigger on falling edge + TIM14->CCMR1 = TIM_CCMR1_CC1S_0; + TIM14->CCER = TIM_CCER_CC1E | TIM_CCER_CC1P; + // Set up TIM14 for an oveflow interrupt at the configured interval + TIM14->PSC = 48000 * LIGHTSENSOR_INTERVAL / 65535; + TIM14->ARR = 65535; + TIM14->DIER = TIM_DIER_UIE; + NVIC_EnableIRQ(TIM14_IRQn); + + LightSensor_Measure(); +} + +void LightSensor_Poll(void) +{ + if(LightSensor_NewMeasurement) + { + unsigned int measurement = LightSensor_Measurement; + LightSensor_NewMeasurement = false; + float brightness = 1.0f - measurement / 65535.0f; + + if(measurement != 65535 && brightness < LightSensor_MinimumBrightness) + { + LightSensor_MinimumBrightness = brightness; + } + if(brightness > LightSensor_MaximumBrightness) + { + LightSensor_MaximumBrightness = brightness; + } + + LightSensor_AbsoluteBrightness = LIGHTSENSOR_LAMBDA * LightSensor_AbsoluteBrightness + + (1.0f - LIGHTSENSOR_LAMBDA) * brightness; + + // Slowly move maximum and minimum back to 0.0 and 1.0, respectively + LightSensor_MaximumBrightness *= LIGHTSENSOR_ALPHA; + LightSensor_MinimumBrightness = 1.0f - LightSensor_MinimumBrightness; + LightSensor_MinimumBrightness *= LIGHTSENSOR_ALPHA; + LightSensor_MinimumBrightness = 1.0f - LightSensor_MinimumBrightness; + + // Scale and saturate to get relative brightness value + float range = LightSensor_MaximumBrightness + - LightSensor_MinimumBrightness; + float low = LightSensor_MinimumBrightness + + range * LIGHTSENSOR_LOW_BOUND; + float high = LightSensor_MinimumBrightness + + range * LIGHTSENSOR_HIGH_BOUND; + LightSensor_RelativeBrightness = (LightSensor_AbsoluteBrightness - low) + / (high - low); + if(LightSensor_RelativeBrightness < 0.0f) + { + LightSensor_RelativeBrightness = 0.0f; + } + if(LightSensor_RelativeBrightness > 1.0f) + { + LightSensor_RelativeBrightness = 1.0f; + } + } +} + +void TIM14_IRQHandler(void) +{ + if(TIM14->SR & TIM_SR_CC1IF) + { + LightSensor_Measurement = TIM14->CCR1; + } + else + { + LightSensor_Measurement = 65535; + } + LightSensor_NewMeasurement = true; + + LightSensor_Measure(); +} \ No newline at end of file diff --git a/src/light_sensor.h b/src/light_sensor.h new file mode 100644 index 0000000..033b53f --- /dev/null +++ b/src/light_sensor.h @@ -0,0 +1,25 @@ +#pragma once + +#include + +#include "stm32f030x6.h" +#include "pinning.h" + +// ADC polling interval in milliseconds +#define LIGHTSENSOR_INTERVAL 2000 + +// 'Forgetting factor' of the rolling brightness average +#define LIGHTSENSOR_LAMBDA 0.95f + +// 'Forgetting factor' for maximum and minimum brightness +#define LIGHTSENSOR_ALPHA 0.999f + +// Bounds for converting absolute to relative brightness: Consider everything +// near the minimum or maximum 0.0 or 1.0, respectively +#define LIGHTSENSOR_LOW_BOUND 0.1f +#define LIGHTSENSOR_HIGH_BOUND 0.9f + +extern float LightSensor_RelativeBrightness; + +void LightSensor_Init(void); +void LightSensor_Poll(void); diff --git a/src/main.c b/src/main.c index 6a2ef80..5d21560 100644 --- a/src/main.c +++ b/src/main.c @@ -8,25 +8,26 @@ int main(void) __asm__ volatile("nop"); } + LightSensor_Init(); LED_Init(); int ct = 0; uint8_t *data = (uint8_t*)LED_PixelData; while(1) { + LightSensor_Poll(); + uint8_t brightness = LightSensor_RelativeBrightness * 255; if(LED_FrameFlag) { LED_FrameFlag = false; - data[ct]++; + data[ct] = brightness; LED_Commit(); - if(data[ct] == 255) + + ct += 3; + if(ct >= LED_COLUMNS * LED_ROWS) { - ct += 3; - if(ct >= LED_COLUMNS * LED_ROWS) - { - ct -= LED_COLUMNS * LED_ROWS; - ct += 1; - } + ct -= LED_COLUMNS * LED_ROWS; + ct += 1; } } } diff --git a/src/main.h b/src/main.h index d51f46d..3f1bfc6 100644 --- a/src/main.h +++ b/src/main.h @@ -3,6 +3,7 @@ #include "stm32f030x6.h" #include "buildid.h" #include "led.h" +#include "light_sensor.h" int main(void); diff --git a/src/pinning.h b/src/pinning.h index c10a259..7527e61 100644 --- a/src/pinning.h +++ b/src/pinning.h @@ -14,6 +14,9 @@ #define PIN_LED_G_3 13 #define PIN_LED_B_3 14 +// Port B +#define PIN_LIGHT_SENSOR 1 // ADC_IN9 + // Port F #define PIN_ROW_DATA 0 // Shift register data in #define PIN_ROW_SCK 1 // Shift register clock