From 659fdb9004f6303c3b371ebb7d54cf97dff862c4 Mon Sep 17 00:00:00 2001 From: fruchti Date: Thu, 16 Jul 2020 20:44:57 +0200 Subject: [PATCH 01/31] Add LED order --- build-number.txt | 2 +- src/animation.c | 16 +++------------- src/animation.h | 6 +++++- 3 files changed, 9 insertions(+), 15 deletions(-) diff --git a/build-number.txt b/build-number.txt index a1f7f63..54ea97e 100644 --- a/build-number.txt +++ b/build-number.txt @@ -1 +1 @@ -298 +310 diff --git a/src/animation.c b/src/animation.c index fc3657f..a97e30d 100644 --- a/src/animation.c +++ b/src/animation.c @@ -1,18 +1,8 @@ #include "animation.h" -#include "led.h" const unsigned int Animation_LEDOrder[LED_COUNT] = { - // Red - 0, 1, 2, 3, 4, 5, - // Green - 6, 7, 8, 9, 10, 11, - // Blue - 12, 13, 14, 15, 16, 17, - // Yellow - 18, 19, 20, 21, 22, 23, - // Cyan - 24, 25, 26, 27, 28, 29, - // Fuchsia - 30, 31 + 19, 27, 21, 13, 0, 4, 24, 8, 12, 15, 6, 5, 28, + 29, 17, 3, 18, 26, 22, 10, 16, 20, 30, 1, + 25, 2, 14, 31, 7, 11, 9, 23 }; \ No newline at end of file diff --git a/src/animation.h b/src/animation.h index 7b9637e..e3f28fc 100644 --- a/src/animation.h +++ b/src/animation.h @@ -1 +1,5 @@ -#pragma once \ No newline at end of file +#pragma once + +#include "led.h" + +extern const unsigned int Animation_LEDOrder[LED_COUNT]; From 3121557afdca48363cef0a732d541726f8ae9eea Mon Sep 17 00:00:00 2001 From: fruchti Date: Thu, 16 Jul 2020 22:04:54 +0200 Subject: [PATCH 02/31] Add basic gradient output --- build-number.txt | 2 +- src/animation.c | 305 ++++++++++++++++++++++++++++++++++++++++++++++- src/animation.h | 8 ++ src/main.c | 55 +-------- src/main.h | 1 + 5 files changed, 315 insertions(+), 56 deletions(-) diff --git a/build-number.txt b/build-number.txt index 54ea97e..55bd0ac 100644 --- a/build-number.txt +++ b/build-number.txt @@ -1 +1 @@ -310 +333 diff --git a/src/animation.c b/src/animation.c index a97e30d..377fe82 100644 --- a/src/animation.c +++ b/src/animation.c @@ -1,8 +1,311 @@ #include "animation.h" +#include "light_sensor.h" const unsigned int Animation_LEDOrder[LED_COUNT] = { 19, 27, 21, 13, 0, 4, 24, 8, 12, 15, 6, 5, 28, 29, 17, 3, 18, 26, 22, 10, 16, 20, 30, 1, 25, 2, 14, 31, 7, 11, 9, 23 -}; \ No newline at end of file +}; + +const LED_Colour_t Animation_ColourLUT[] = +{ + { .r = 255, .g = 51, .b = 51 }, + { .r = 255, .g = 51, .b = 56 }, + { .r = 255, .g = 51, .b = 61 }, + { .r = 255, .g = 51, .b = 65 }, + { .r = 255, .g = 51, .b = 70 }, + { .r = 255, .g = 51, .b = 75 }, + { .r = 255, .g = 51, .b = 80 }, + { .r = 255, .g = 51, .b = 85 }, + { .r = 255, .g = 51, .b = 89 }, + { .r = 255, .g = 51, .b = 94 }, + { .r = 255, .g = 51, .b = 99 }, + { .r = 255, .g = 51, .b = 104 }, + { .r = 255, .g = 51, .b = 109 }, + { .r = 255, .g = 51, .b = 113 }, + { .r = 255, .g = 51, .b = 118 }, + { .r = 255, .g = 51, .b = 123 }, + { .r = 255, .g = 51, .b = 128 }, + { .r = 255, .g = 51, .b = 133 }, + { .r = 255, .g = 51, .b = 137 }, + { .r = 255, .g = 51, .b = 142 }, + { .r = 255, .g = 51, .b = 147 }, + { .r = 255, .g = 51, .b = 152 }, + { .r = 255, .g = 51, .b = 157 }, + { .r = 255, .g = 51, .b = 161 }, + { .r = 255, .g = 51, .b = 166 }, + { .r = 255, .g = 51, .b = 171 }, + { .r = 255, .g = 51, .b = 176 }, + { .r = 255, .g = 51, .b = 181 }, + { .r = 255, .g = 51, .b = 185 }, + { .r = 255, .g = 51, .b = 190 }, + { .r = 255, .g = 51, .b = 195 }, + { .r = 255, .g = 51, .b = 200 }, + { .r = 255, .g = 51, .b = 205 }, + { .r = 255, .g = 51, .b = 209 }, + { .r = 255, .g = 51, .b = 214 }, + { .r = 255, .g = 51, .b = 219 }, + { .r = 255, .g = 51, .b = 224 }, + { .r = 255, .g = 51, .b = 229 }, + { .r = 255, .g = 51, .b = 233 }, + { .r = 255, .g = 51, .b = 238 }, + { .r = 255, .g = 51, .b = 243 }, + { .r = 255, .g = 51, .b = 248 }, + { .r = 255, .g = 51, .b = 253 }, + { .r = 253, .g = 51, .b = 255 }, + { .r = 248, .g = 51, .b = 255 }, + { .r = 243, .g = 51, .b = 255 }, + { .r = 238, .g = 51, .b = 255 }, + { .r = 233, .g = 51, .b = 255 }, + { .r = 229, .g = 51, .b = 255 }, + { .r = 224, .g = 51, .b = 255 }, + { .r = 219, .g = 51, .b = 255 }, + { .r = 214, .g = 51, .b = 255 }, + { .r = 209, .g = 51, .b = 255 }, + { .r = 205, .g = 51, .b = 255 }, + { .r = 200, .g = 51, .b = 255 }, + { .r = 195, .g = 51, .b = 255 }, + { .r = 190, .g = 51, .b = 255 }, + { .r = 185, .g = 51, .b = 255 }, + { .r = 181, .g = 51, .b = 255 }, + { .r = 176, .g = 51, .b = 255 }, + { .r = 171, .g = 51, .b = 255 }, + { .r = 166, .g = 51, .b = 255 }, + { .r = 161, .g = 51, .b = 255 }, + { .r = 157, .g = 51, .b = 255 }, + { .r = 152, .g = 51, .b = 255 }, + { .r = 147, .g = 51, .b = 255 }, + { .r = 142, .g = 51, .b = 255 }, + { .r = 137, .g = 51, .b = 255 }, + { .r = 133, .g = 51, .b = 255 }, + { .r = 128, .g = 51, .b = 255 }, + { .r = 123, .g = 51, .b = 255 }, + { .r = 118, .g = 51, .b = 255 }, + { .r = 113, .g = 51, .b = 255 }, + { .r = 109, .g = 51, .b = 255 }, + { .r = 104, .g = 51, .b = 255 }, + { .r = 99, .g = 51, .b = 255 }, + { .r = 94, .g = 51, .b = 255 }, + { .r = 89, .g = 51, .b = 255 }, + { .r = 85, .g = 51, .b = 255 }, + { .r = 80, .g = 51, .b = 255 }, + { .r = 75, .g = 51, .b = 255 }, + { .r = 70, .g = 51, .b = 255 }, + { .r = 65, .g = 51, .b = 255 }, + { .r = 61, .g = 51, .b = 255 }, + { .r = 56, .g = 51, .b = 255 }, + { .r = 51, .g = 51, .b = 255 }, + { .r = 51, .g = 56, .b = 255 }, + { .r = 51, .g = 61, .b = 255 }, + { .r = 51, .g = 65, .b = 255 }, + { .r = 51, .g = 70, .b = 255 }, + { .r = 51, .g = 75, .b = 255 }, + { .r = 51, .g = 80, .b = 255 }, + { .r = 51, .g = 85, .b = 255 }, + { .r = 51, .g = 89, .b = 255 }, + { .r = 51, .g = 94, .b = 255 }, + { .r = 51, .g = 99, .b = 255 }, + { .r = 51, .g = 104, .b = 255 }, + { .r = 51, .g = 109, .b = 255 }, + { .r = 51, .g = 113, .b = 255 }, + { .r = 51, .g = 118, .b = 255 }, + { .r = 51, .g = 123, .b = 255 }, + { .r = 51, .g = 128, .b = 255 }, + { .r = 51, .g = 133, .b = 255 }, + { .r = 51, .g = 137, .b = 255 }, + { .r = 51, .g = 142, .b = 255 }, + { .r = 51, .g = 147, .b = 255 }, + { .r = 51, .g = 152, .b = 255 }, + { .r = 51, .g = 157, .b = 255 }, + { .r = 51, .g = 161, .b = 255 }, + { .r = 51, .g = 166, .b = 255 }, + { .r = 51, .g = 171, .b = 255 }, + { .r = 51, .g = 176, .b = 255 }, + { .r = 51, .g = 181, .b = 255 }, + { .r = 51, .g = 185, .b = 255 }, + { .r = 51, .g = 190, .b = 255 }, + { .r = 51, .g = 195, .b = 255 }, + { .r = 51, .g = 200, .b = 255 }, + { .r = 51, .g = 205, .b = 255 }, + { .r = 51, .g = 209, .b = 255 }, + { .r = 51, .g = 214, .b = 255 }, + { .r = 51, .g = 219, .b = 255 }, + { .r = 51, .g = 224, .b = 255 }, + { .r = 51, .g = 229, .b = 255 }, + { .r = 51, .g = 233, .b = 255 }, + { .r = 51, .g = 238, .b = 255 }, + { .r = 51, .g = 243, .b = 255 }, + { .r = 51, .g = 248, .b = 255 }, + { .r = 51, .g = 253, .b = 255 }, + { .r = 51, .g = 255, .b = 253 }, + { .r = 51, .g = 255, .b = 248 }, + { .r = 51, .g = 255, .b = 243 }, + { .r = 51, .g = 255, .b = 238 }, + { .r = 51, .g = 255, .b = 233 }, + { .r = 51, .g = 255, .b = 229 }, + { .r = 51, .g = 255, .b = 224 }, + { .r = 51, .g = 255, .b = 219 }, + { .r = 51, .g = 255, .b = 214 }, + { .r = 51, .g = 255, .b = 209 }, + { .r = 51, .g = 255, .b = 205 }, + { .r = 51, .g = 255, .b = 200 }, + { .r = 51, .g = 255, .b = 195 }, + { .r = 51, .g = 255, .b = 190 }, + { .r = 51, .g = 255, .b = 185 }, + { .r = 51, .g = 255, .b = 181 }, + { .r = 51, .g = 255, .b = 176 }, + { .r = 51, .g = 255, .b = 171 }, + { .r = 51, .g = 255, .b = 166 }, + { .r = 51, .g = 255, .b = 161 }, + { .r = 51, .g = 255, .b = 157 }, + { .r = 51, .g = 255, .b = 152 }, + { .r = 51, .g = 255, .b = 147 }, + { .r = 51, .g = 255, .b = 142 }, + { .r = 51, .g = 255, .b = 137 }, + { .r = 51, .g = 255, .b = 133 }, + { .r = 51, .g = 255, .b = 128 }, + { .r = 51, .g = 255, .b = 123 }, + { .r = 51, .g = 255, .b = 118 }, + { .r = 51, .g = 255, .b = 113 }, + { .r = 51, .g = 255, .b = 109 }, + { .r = 51, .g = 255, .b = 104 }, + { .r = 51, .g = 255, .b = 99 }, + { .r = 51, .g = 255, .b = 94 }, + { .r = 51, .g = 255, .b = 89 }, + { .r = 51, .g = 255, .b = 85 }, + { .r = 51, .g = 255, .b = 80 }, + { .r = 51, .g = 255, .b = 75 }, + { .r = 51, .g = 255, .b = 70 }, + { .r = 51, .g = 255, .b = 65 }, + { .r = 51, .g = 255, .b = 61 }, + { .r = 51, .g = 255, .b = 56 }, + { .r = 51, .g = 255, .b = 51 }, + { .r = 56, .g = 255, .b = 51 }, + { .r = 61, .g = 255, .b = 51 }, + { .r = 65, .g = 255, .b = 51 }, + { .r = 70, .g = 255, .b = 51 }, + { .r = 75, .g = 255, .b = 51 }, + { .r = 80, .g = 255, .b = 51 }, + { .r = 85, .g = 255, .b = 51 }, + { .r = 89, .g = 255, .b = 51 }, + { .r = 94, .g = 255, .b = 51 }, + { .r = 99, .g = 255, .b = 51 }, + { .r = 104, .g = 255, .b = 51 }, + { .r = 109, .g = 255, .b = 51 }, + { .r = 113, .g = 255, .b = 51 }, + { .r = 118, .g = 255, .b = 51 }, + { .r = 123, .g = 255, .b = 51 }, + { .r = 128, .g = 255, .b = 51 }, + { .r = 133, .g = 255, .b = 51 }, + { .r = 137, .g = 255, .b = 51 }, + { .r = 142, .g = 255, .b = 51 }, + { .r = 147, .g = 255, .b = 51 }, + { .r = 152, .g = 255, .b = 51 }, + { .r = 157, .g = 255, .b = 51 }, + { .r = 161, .g = 255, .b = 51 }, + { .r = 166, .g = 255, .b = 51 }, + { .r = 171, .g = 255, .b = 51 }, + { .r = 176, .g = 255, .b = 51 }, + { .r = 181, .g = 255, .b = 51 }, + { .r = 185, .g = 255, .b = 51 }, + { .r = 190, .g = 255, .b = 51 }, + { .r = 195, .g = 255, .b = 51 }, + { .r = 200, .g = 255, .b = 51 }, + { .r = 205, .g = 255, .b = 51 }, + { .r = 209, .g = 255, .b = 51 }, + { .r = 214, .g = 255, .b = 51 }, + { .r = 219, .g = 255, .b = 51 }, + { .r = 224, .g = 255, .b = 51 }, + { .r = 229, .g = 255, .b = 51 }, + { .r = 233, .g = 255, .b = 51 }, + { .r = 238, .g = 255, .b = 51 }, + { .r = 243, .g = 255, .b = 51 }, + { .r = 248, .g = 255, .b = 51 }, + { .r = 253, .g = 255, .b = 51 }, + { .r = 255, .g = 253, .b = 51 }, + { .r = 255, .g = 248, .b = 51 }, + { .r = 255, .g = 243, .b = 51 }, + { .r = 255, .g = 238, .b = 51 }, + { .r = 255, .g = 233, .b = 51 }, + { .r = 255, .g = 229, .b = 51 }, + { .r = 255, .g = 224, .b = 51 }, + { .r = 255, .g = 219, .b = 51 }, + { .r = 255, .g = 214, .b = 51 }, + { .r = 255, .g = 209, .b = 51 }, + { .r = 255, .g = 205, .b = 51 }, + { .r = 255, .g = 200, .b = 51 }, + { .r = 255, .g = 195, .b = 51 }, + { .r = 255, .g = 190, .b = 51 }, + { .r = 255, .g = 185, .b = 51 }, + { .r = 255, .g = 181, .b = 51 }, + { .r = 255, .g = 176, .b = 51 }, + { .r = 255, .g = 171, .b = 51 }, + { .r = 255, .g = 166, .b = 51 }, + { .r = 255, .g = 161, .b = 51 }, + { .r = 255, .g = 157, .b = 51 }, + { .r = 255, .g = 152, .b = 51 }, + { .r = 255, .g = 147, .b = 51 }, + { .r = 255, .g = 142, .b = 51 }, + { .r = 255, .g = 137, .b = 51 }, + { .r = 255, .g = 133, .b = 51 }, + { .r = 255, .g = 128, .b = 51 }, + { .r = 255, .g = 123, .b = 51 }, + { .r = 255, .g = 118, .b = 51 }, + { .r = 255, .g = 113, .b = 51 }, + { .r = 255, .g = 109, .b = 51 }, + { .r = 255, .g = 104, .b = 51 }, + { .r = 255, .g = 99, .b = 51 }, + { .r = 255, .g = 94, .b = 51 }, + { .r = 255, .g = 89, .b = 51 }, + { .r = 255, .g = 85, .b = 51 }, + { .r = 255, .g = 80, .b = 51 }, + { .r = 255, .g = 75, .b = 51 }, + { .r = 255, .g = 70, .b = 51 }, + { .r = 255, .g = 65, .b = 51 }, + { .r = 255, .g = 61, .b = 51 }, + { .r = 255, .g = 56, .b = 51 }, +}; + +LED_Colour_t Animation_GetColour(unsigned int step, unsigned int brightness) +{ + const unsigned int lut_size = sizeof(Animation_ColourLUT) + / sizeof(LED_Colour_t); + + unsigned int index = step * lut_size / ANIMATION_STEPS; + + LED_Colour_t colour = Animation_ColourLUT[index]; + + colour.r = ((unsigned int)colour.r * brightness) + >> ANIMATION_BRIGHTNESS_BITS; + colour.g = ((unsigned int)colour.g * brightness) + >> ANIMATION_BRIGHTNESS_BITS; + colour.b = ((unsigned int)colour.b * brightness) + >> ANIMATION_BRIGHTNESS_BITS; + + return colour; +} + +void Animation_DrawGradient(unsigned int step_bottom, unsigned int step_top, + unsigned int brightness) +{ + for(int i = 0; i < LED_COUNT; i++) + { + unsigned int step = ((LED_COUNT - 1 - i) * step_bottom + i * step_top) + / (LED_COUNT - 1); + LED_PixelData[Animation_LEDOrder[i]] = Animation_GetColour(step, + brightness); + } +} + +void Animation_Poll(void) +{ + if(!LED_FrameFlag) + { + return; + } + LED_FrameFlag = false; + Animation_DrawGradient(60000, 50000, + ANIMATION_MAX_BRIGHTNESS * LightSensor_RelativeBrightness); + LED_Commit(); +} \ No newline at end of file diff --git a/src/animation.h b/src/animation.h index e3f28fc..7b28abb 100644 --- a/src/animation.h +++ b/src/animation.h @@ -2,4 +2,12 @@ #include "led.h" +#define ANIMATION_STEPS 65535 +#define ANIMATION_BRIGHTNESS_BITS \ + 16 +#define ANIMATION_MAX_BRIGHTNESS \ + (1 << ANIMATION_BRIGHTNESS_BITS) + extern const unsigned int Animation_LEDOrder[LED_COUNT]; + +void Animation_Poll(void); \ No newline at end of file diff --git a/src/main.c b/src/main.c index 781c397..9c10a4a 100644 --- a/src/main.c +++ b/src/main.c @@ -13,64 +13,11 @@ int main(void) LED_Init(); - unsigned int counter = 0; - const unsigned int blink_period = 1000; - const unsigned int group_size = 6; while(1) { __WFI(); LightSensor_Poll(); - - if(LED_FrameFlag) - { - memset(LED_PixelData, 0, sizeof(LED_PixelData)); - - for(unsigned int i = 0; i < LED_COUNT; i++) - { - uint8_t brightness = 0; - if((counter / blink_period) <= (i % group_size)) - { - if(counter / (blink_period / 2) % 2) - { - brightness = 30; - } - } - - switch(i / group_size) - { - case 0: - LED_PixelData[i].r = brightness; - break; - case 1: - LED_PixelData[i].g = brightness; - break; - case 2: - LED_PixelData[i].b = brightness; - break; - case 3: - LED_PixelData[i].r = brightness; - LED_PixelData[i].g = brightness; - break; - case 4: - LED_PixelData[i].g = brightness; - LED_PixelData[i].b = brightness; - break; - case 5: - LED_PixelData[i].b = brightness; - LED_PixelData[i].r = brightness; - break; - } - - counter++; - if(counter > blink_period * (group_size + 2)) - { - counter = 0; - } - } - - LED_FrameFlag = false; - LED_Commit(); - } + Animation_Poll(); } return 0; diff --git a/src/main.h b/src/main.h index 48ce191..3b978d4 100644 --- a/src/main.h +++ b/src/main.h @@ -6,6 +6,7 @@ #include "buildid.h" #include "led.h" #include "light_sensor.h" +#include "animation.h" int main(void); From 187c9157990ece22395e5c31d1822e1815fc9691 Mon Sep 17 00:00:00 2001 From: fruchti Date: Thu, 16 Jul 2020 22:43:39 +0200 Subject: [PATCH 03/31] Use integer arithmetic for light sensor --- build-number.txt | 2 +- src/animation.c | 12 ++++-------- src/animation.h | 4 ---- src/light_sensor.c | 45 ++++++++++++++++++++++++--------------------- src/light_sensor.h | 12 ++++++++---- 5 files changed, 37 insertions(+), 38 deletions(-) diff --git a/build-number.txt b/build-number.txt index 55bd0ac..35329ed 100644 --- a/build-number.txt +++ b/build-number.txt @@ -1 +1 @@ -333 +361 diff --git a/src/animation.c b/src/animation.c index 377fe82..5d5e147 100644 --- a/src/animation.c +++ b/src/animation.c @@ -276,12 +276,9 @@ LED_Colour_t Animation_GetColour(unsigned int step, unsigned int brightness) LED_Colour_t colour = Animation_ColourLUT[index]; - colour.r = ((unsigned int)colour.r * brightness) - >> ANIMATION_BRIGHTNESS_BITS; - colour.g = ((unsigned int)colour.g * brightness) - >> ANIMATION_BRIGHTNESS_BITS; - colour.b = ((unsigned int)colour.b * brightness) - >> ANIMATION_BRIGHTNESS_BITS; + colour.r = ((unsigned int)colour.r * brightness) >> LIGHTSENSOR_BITS; + colour.g = ((unsigned int)colour.g * brightness) >> LIGHTSENSOR_BITS; + colour.b = ((unsigned int)colour.b * brightness) >> LIGHTSENSOR_BITS; return colour; } @@ -305,7 +302,6 @@ void Animation_Poll(void) return; } LED_FrameFlag = false; - Animation_DrawGradient(60000, 50000, - ANIMATION_MAX_BRIGHTNESS * LightSensor_RelativeBrightness); + Animation_DrawGradient(60000, 50000, LightSensor_RelativeBrightness); LED_Commit(); } \ No newline at end of file diff --git a/src/animation.h b/src/animation.h index 7b28abb..036115d 100644 --- a/src/animation.h +++ b/src/animation.h @@ -3,10 +3,6 @@ #include "led.h" #define ANIMATION_STEPS 65535 -#define ANIMATION_BRIGHTNESS_BITS \ - 16 -#define ANIMATION_MAX_BRIGHTNESS \ - (1 << ANIMATION_BRIGHTNESS_BITS) extern const unsigned int Animation_LEDOrder[LED_COUNT]; diff --git a/src/light_sensor.c b/src/light_sensor.c index 6f0d8e1..0b10abe 100644 --- a/src/light_sensor.c +++ b/src/light_sensor.c @@ -4,13 +4,13 @@ volatile unsigned int LightSensor_Measurement; volatile bool LightSensor_NewMeasurement = false; // Rolling average of the brightness measurement -float LightSensor_AbsoluteBrightness = 0.5f; +unsigned int LightSensor_AbsoluteBrightness = LIGHTSENSOR_MAX / 2; // Maximum and minimum encountered so far -float LightSensor_MinimumBrightness = 1.0f; -float LightSensor_MaximumBrightness = 0.0f; +unsigned int LightSensor_MinimumBrightness = LIGHTSENSOR_MAX; +unsigned int LightSensor_MaximumBrightness = 0; -float LightSensor_RelativeBrightness; +int LightSensor_RelativeBrightness; static void LightSensor_Measure(void) { @@ -66,35 +66,38 @@ void LightSensor_Poll(void) { unsigned int measurement = LightSensor_Measurement; LightSensor_NewMeasurement = false; - float brightness = 65535.0f / measurement; - LightSensor_AbsoluteBrightness = LIGHTSENSOR_LAMBDA * LightSensor_AbsoluteBrightness - + (1.0f - LIGHTSENSOR_LAMBDA) * brightness; + unsigned int brightness = ((1 << 31) + / (measurement + 1)) >> (31 - 16); + // LightSensor_AbsoluteBrightness = LIGHTSENSOR_LAMBDA * LightSensor_AbsoluteBrightness + // + (1.0f - LIGHTSENSOR_LAMBDA) * brightness; + LightSensor_AbsoluteBrightness -= LightSensor_AbsoluteBrightness >> LIGHTSENSOR_LAMBDA_BITS; + LightSensor_AbsoluteBrightness += brightness >> LIGHTSENSOR_LAMBDA_BITS; - if(LightSensor_AbsoluteBrightness < LightSensor_MinimumBrightness) + if(brightness < LightSensor_MinimumBrightness) { - LightSensor_MinimumBrightness = LightSensor_AbsoluteBrightness; + LightSensor_MinimumBrightness = brightness; } - if(LightSensor_AbsoluteBrightness > LightSensor_MaximumBrightness) + if(brightness > LightSensor_MaximumBrightness) { - LightSensor_MaximumBrightness = LightSensor_AbsoluteBrightness; + LightSensor_MaximumBrightness = brightness; } // Scale and saturate to get relative brightness value - float range = LightSensor_MaximumBrightness + int range = LightSensor_MaximumBrightness - LightSensor_MinimumBrightness; - float low = LightSensor_MinimumBrightness - + range * LIGHTSENSOR_LOW_BOUND; - float high = LightSensor_MinimumBrightness - + range * LIGHTSENSOR_HIGH_BOUND; + int low = LightSensor_MinimumBrightness + + LIGHTSENSOR_LOW_BOUND * range / LIGHTSENSOR_MAX; + int high = LightSensor_MinimumBrightness + + LIGHTSENSOR_HIGH_BOUND * range / LIGHTSENSOR_MAX; LightSensor_RelativeBrightness = (LightSensor_AbsoluteBrightness - low) - / (high - low); - if(LightSensor_RelativeBrightness < 0.0f) + * LIGHTSENSOR_MAX / (high - low); + if(LightSensor_RelativeBrightness < 0) { - LightSensor_RelativeBrightness = 0.0f; + LightSensor_RelativeBrightness = 0; } - if(LightSensor_RelativeBrightness > 1.0f) + if(LightSensor_RelativeBrightness > LIGHTSENSOR_MAX) { - LightSensor_RelativeBrightness = 1.0f; + LightSensor_RelativeBrightness = LIGHTSENSOR_MAX; } } } diff --git a/src/light_sensor.h b/src/light_sensor.h index 4ae31cc..bc99ee2 100644 --- a/src/light_sensor.h +++ b/src/light_sensor.h @@ -8,15 +8,19 @@ // ADC polling interval in milliseconds #define LIGHTSENSOR_INTERVAL 250 +// Resolution of the brightness output +#define LIGHTSENSOR_BITS 12 +#define LIGHTSENSOR_MAX (1 << LIGHTSENSOR_BITS) + // 'Forgetting factor' of the rolling brightness average -#define LIGHTSENSOR_LAMBDA 0.9f +#define LIGHTSENSOR_LAMBDA_BITS 4 // 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.005f -#define LIGHTSENSOR_HIGH_BOUND 0.9f +#define LIGHTSENSOR_LOW_BOUND ((unsigned int)(0.005 * LIGHTSENSOR_MAX)) +#define LIGHTSENSOR_HIGH_BOUND ((unsigned int)(0.9 * LIGHTSENSOR_MAX)) -extern float LightSensor_RelativeBrightness; +extern int LightSensor_RelativeBrightness; void LightSensor_Init(void); void LightSensor_Poll(void); From 5a45524af33ffbce5dae96554ed095a8efd944b8 Mon Sep 17 00:00:00 2001 From: fruchti Date: Thu, 16 Jul 2020 23:11:09 +0200 Subject: [PATCH 04/31] Add simple smooth animation --- build-number.txt | 2 +- src/animation.c | 22 ++++++++++++++++++++-- src/animation.h | 5 +++++ 3 files changed, 26 insertions(+), 3 deletions(-) diff --git a/build-number.txt b/build-number.txt index 35329ed..a5c3fde 100644 --- a/build-number.txt +++ b/build-number.txt @@ -1 +1 @@ -361 +373 diff --git a/src/animation.c b/src/animation.c index 5d5e147..a89b36c 100644 --- a/src/animation.c +++ b/src/animation.c @@ -286,6 +286,14 @@ LED_Colour_t Animation_GetColour(unsigned int step, unsigned int brightness) void Animation_DrawGradient(unsigned int step_bottom, unsigned int step_top, unsigned int brightness) { + if(step_bottom >= ANIMATION_STEPS) + { + step_bottom = 2 * ANIMATION_STEPS - step_bottom; + } + if(step_top >= ANIMATION_STEPS) + { + step_top = 2 * ANIMATION_STEPS - step_top; + } for(int i = 0; i < LED_COUNT; i++) { unsigned int step = ((LED_COUNT - 1 - i) * step_bottom + i * step_top) @@ -301,7 +309,17 @@ void Animation_Poll(void) { return; } - LED_FrameFlag = false; - Animation_DrawGradient(60000, 50000, LightSensor_RelativeBrightness); + + static unsigned int bottom = 0; + static unsigned int top = 0; + + bottom += ANIMATION_STEPS / ANIMATION_DURATION_BOTTOM; + top += ANIMATION_STEPS / ANIMATION_DURATION_TOP; + + bottom %= 2 * ANIMATION_STEPS; + top %= 2 * ANIMATION_STEPS; + + Animation_DrawGradient(bottom, top, LightSensor_RelativeBrightness); LED_Commit(); + LED_FrameFlag = false; } \ No newline at end of file diff --git a/src/animation.h b/src/animation.h index 036115d..f9abe60 100644 --- a/src/animation.h +++ b/src/animation.h @@ -4,6 +4,11 @@ #define ANIMATION_STEPS 65535 +#define ANIMATION_DURATION_BOTTOM \ + 1000 + +#define ANIMATION_DURATION_TOP 5000 + extern const unsigned int Animation_LEDOrder[LED_COUNT]; void Animation_Poll(void); \ No newline at end of file From ee5a5b9f6c7aadd4302e6452ee38f072b67009dd Mon Sep 17 00:00:00 2001 From: fruchti Date: Thu, 16 Jul 2020 23:19:28 +0200 Subject: [PATCH 05/31] Fix light sensor underflow --- build-number.txt | 2 +- src/light_sensor.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/build-number.txt b/build-number.txt index a5c3fde..d9061d9 100644 --- a/build-number.txt +++ b/build-number.txt @@ -1 +1 @@ -373 +375 diff --git a/src/light_sensor.c b/src/light_sensor.c index 0b10abe..4ec450f 100644 --- a/src/light_sensor.c +++ b/src/light_sensor.c @@ -89,7 +89,7 @@ void LightSensor_Poll(void) + LIGHTSENSOR_LOW_BOUND * range / LIGHTSENSOR_MAX; int high = LightSensor_MinimumBrightness + LIGHTSENSOR_HIGH_BOUND * range / LIGHTSENSOR_MAX; - LightSensor_RelativeBrightness = (LightSensor_AbsoluteBrightness - low) + LightSensor_RelativeBrightness = ((int)LightSensor_AbsoluteBrightness - low) * LIGHTSENSOR_MAX / (high - low); if(LightSensor_RelativeBrightness < 0) { From 38d3a3846bd6f57cba933ff13b2e7269a93247bd Mon Sep 17 00:00:00 2001 From: fruchti Date: Thu, 16 Jul 2020 23:48:48 +0200 Subject: [PATCH 06/31] Add power-down mode for LED driver --- build-number.txt | 2 +- src/led.c | 41 +++++++++++++++++++++++++++++++++++++++++ src/led.h | 6 ++++++ src/main.c | 13 +++++++++++++ src/main.h | 1 + 5 files changed, 62 insertions(+), 1 deletion(-) diff --git a/build-number.txt b/build-number.txt index d9061d9..6bb2f4e 100644 --- a/build-number.txt +++ b/build-number.txt @@ -1 +1 @@ -375 +389 diff --git a/src/led.c b/src/led.c index bad20ff..537093e 100644 --- a/src/led.c +++ b/src/led.c @@ -3,6 +3,7 @@ LED_Colour_t LED_PixelData[LED_COUNT] = {{0}}; volatile bool LED_FrameFlag = false; +volatile bool LED_SuspendFlag = false; #define LED_ODR_MASK ((1 << PIN_LED_R_0) | (1 << PIN_LED_G_0) \ | (1 << PIN_LED_B_0) | (1 << PIN_LED_R_1) \ @@ -215,6 +216,7 @@ void LED_Init(void) RCC->APB1ENR |= RCC_APB1ENR_TIM3EN; // Fill both DMA buffers + LED_QueuePageFlip = false; LED_Commit(); LED_PageFlip(); LED_Commit(); @@ -244,6 +246,39 @@ void LED_Init(void) LED_StartBCM(0); } +void LED_Suspend(void) +{ + LED_SuspendFlag = true; + while(LED_SuspendFlag); + + // Disable timer and DMA channels + TIM3->CR1 = 0x0000; + TIM3->DIER = 0x0000; + TIM3->CNT = 0; + DMA1->IFCR = DMA_IFCR_CTCIF3; + DMA1_Channel3->CCR = 0x0000; + DMA1_Channel4->CCR = 0x0000; + NVIC_DisableIRQ(DMA1_Channel2_3_IRQn); + NVIC_ClearPendingIRQ(DMA1_Channel2_3_IRQn); + + // Deactivate all rows and columns + GPIOA->BSRR = LED_ODR_MASK; + GPIOF->BSRR = (1 << PIN_ROW_DATA); + for(int i = 0; i < LED_ROWS + 1; i++) + { + LED_PulseRowClock(); + } + + // Disable timer and DMA clocks + RCC->AHBENR &= ~RCC_AHBENR_DMA1EN; + RCC->APB1ENR &= ~RCC_APB1ENR_TIM3EN; +} + +void LED_WakeUp(void) +{ + LED_Init(); +} + void DMA1_Channel2_3_IRQHandler(void) { // Interrupt when all bits have been sent @@ -268,6 +303,12 @@ void DMA1_Channel2_3_IRQHandler(void) LED_PulseRowClock(); } + if(LED_SuspendFlag) + { + LED_SuspendFlag = false; + return; + } + // Start sending bits out again. The row offset caused by the shift // register "lagging" one clock cycle behind because RCK and SCK are // connected to the same signal doesn't matter here: we're not paying much diff --git a/src/led.h b/src/led.h index 83833c2..2416ffd 100644 --- a/src/led.h +++ b/src/led.h @@ -27,6 +27,12 @@ extern volatile bool LED_FrameFlag; void LED_InitShiftRegister(void); void LED_Init(void); +// Enter power-saving mode (LEDs off) +void LED_Suspend(void); + +// Leave power-saving mode +void LED_WakeUp(void); + // Display LED_PixelData, starting with the next frame void LED_Commit(void); diff --git a/src/main.c b/src/main.c index 9c10a4a..57c9d0e 100644 --- a/src/main.c +++ b/src/main.c @@ -5,6 +5,8 @@ int main(void) LED_InitShiftRegister(); LightSensor_Init(); + bool powered_down = false; + // Delay a bit to make programming easier for(unsigned int i = 0; i < 1000; i++) { @@ -18,6 +20,17 @@ int main(void) __WFI(); LightSensor_Poll(); Animation_Poll(); + + if(LightSensor_RelativeBrightness == 0 && !powered_down) + { + LED_Suspend(); + powered_down = true; + } + if(powered_down && LightSensor_RelativeBrightness > 0) + { + LED_WakeUp(); + powered_down = false; + } } return 0; diff --git a/src/main.h b/src/main.h index 3b978d4..20c8393 100644 --- a/src/main.h +++ b/src/main.h @@ -1,6 +1,7 @@ #pragma once #include +#include #include "stm32f030x6.h" #include "buildid.h" From bf50402ce265f33a40487172ff5348d379154c44 Mon Sep 17 00:00:00 2001 From: fruchti Date: Fri, 17 Jul 2020 00:17:35 +0200 Subject: [PATCH 07/31] Track suspend status in LED driver --- src/led.c | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/src/led.c b/src/led.c index 537093e..365545b 100644 --- a/src/led.c +++ b/src/led.c @@ -4,6 +4,7 @@ LED_Colour_t LED_PixelData[LED_COUNT] = {{0}}; volatile bool LED_FrameFlag = false; volatile bool LED_SuspendFlag = false; +bool LED_Suspended = false; #define LED_ODR_MASK ((1 << PIN_LED_R_0) | (1 << PIN_LED_G_0) \ | (1 << PIN_LED_B_0) | (1 << PIN_LED_R_1) \ @@ -59,6 +60,11 @@ static volatile bool LED_QueuePageFlip = false; void LED_Commit(void) { + if(LED_Suspended) + { + return; + } + // Wait for the current data to be displayed in case LED_Commit was called // more than one time during a single frame. Otherwise, a race condition // might occur. @@ -248,6 +254,11 @@ void LED_Init(void) void LED_Suspend(void) { + if(LED_Suspended) + { + return; + } + LED_SuspendFlag = true; while(LED_SuspendFlag); @@ -272,11 +283,20 @@ void LED_Suspend(void) // Disable timer and DMA clocks RCC->AHBENR &= ~RCC_AHBENR_DMA1EN; RCC->APB1ENR &= ~RCC_APB1ENR_TIM3EN; + + LED_Suspended = true; } void LED_WakeUp(void) { + if(!LED_Suspended) + { + return; + } + LED_Init(); + + LED_Suspended = false; } void DMA1_Channel2_3_IRQHandler(void) From e81daf59fe8188da895428e889c4e1931353ce37 Mon Sep 17 00:00:00 2001 From: fruchti Date: Fri, 17 Jul 2020 00:17:51 +0200 Subject: [PATCH 08/31] Use dedicated timer for animation --- build-number.txt | 2 +- src/animation.c | 23 +++++++++++++++++++++-- src/animation.h | 11 ++++++++--- src/main.c | 1 + 4 files changed, 31 insertions(+), 6 deletions(-) diff --git a/build-number.txt b/build-number.txt index 6bb2f4e..102c15d 100644 --- a/build-number.txt +++ b/build-number.txt @@ -1 +1 @@ -389 +409 diff --git a/src/animation.c b/src/animation.c index a89b36c..44f219f 100644 --- a/src/animation.c +++ b/src/animation.c @@ -1,6 +1,8 @@ #include "animation.h" #include "light_sensor.h" +volatile bool Animation_FrameFlag = false; + const unsigned int Animation_LEDOrder[LED_COUNT] = { 19, 27, 21, 13, 0, 4, 24, 8, 12, 15, 6, 5, 28, @@ -303,12 +305,24 @@ void Animation_DrawGradient(unsigned int step_bottom, unsigned int step_top, } } +void Animation_Init(void) +{ + RCC->APB2ENR |= RCC_APB2ENR_TIM17EN; + + TIM17->PSC = 8000 - 1; + TIM17->ARR = 1000 / ANIMATION_REFRESH_RATE; + TIM17->DIER = TIM_DIER_UIE; + TIM17->CR1 = TIM_CR1_CEN; + NVIC_EnableIRQ(TIM17_IRQn); +} + void Animation_Poll(void) { - if(!LED_FrameFlag) + if(!Animation_FrameFlag) { return; } + Animation_FrameFlag = false; static unsigned int bottom = 0; static unsigned int top = 0; @@ -321,5 +335,10 @@ void Animation_Poll(void) Animation_DrawGradient(bottom, top, LightSensor_RelativeBrightness); LED_Commit(); - LED_FrameFlag = false; +} + +void TIM17_IRQHandler(void) +{ + Animation_FrameFlag = true; + TIM17->SR &= ~TIM_SR_UIF; } \ No newline at end of file diff --git a/src/animation.h b/src/animation.h index f9abe60..eecdd54 100644 --- a/src/animation.h +++ b/src/animation.h @@ -1,14 +1,19 @@ #pragma once +#include + #include "led.h" -#define ANIMATION_STEPS 65535 +#define ANIMATION_STEPS (1 << 24) + +#define ANIMATION_REFRESH_RATE 10 #define ANIMATION_DURATION_BOTTOM \ - 1000 + 6048000 -#define ANIMATION_DURATION_TOP 5000 +#define ANIMATION_DURATION_TOP 6652800 extern const unsigned int Animation_LEDOrder[LED_COUNT]; +void Animation_Init(void); void Animation_Poll(void); \ No newline at end of file diff --git a/src/main.c b/src/main.c index 57c9d0e..bb229ca 100644 --- a/src/main.c +++ b/src/main.c @@ -14,6 +14,7 @@ int main(void) } LED_Init(); + Animation_Init(); while(1) { From 53aa19d91f3a38e2f99d255a25f6b5b7cf84dd9c Mon Sep 17 00:00:00 2001 From: fruchti Date: Fri, 17 Jul 2020 16:38:09 +0200 Subject: [PATCH 09/31] Externalise colour LUT --- build-number.txt | 2 +- src/animation.c | 260 +------------------------------------------- src/animation_lut.c | 260 ++++++++++++++++++++++++++++++++++++++++++++ src/animation_lut.h | 5 + 4 files changed, 267 insertions(+), 260 deletions(-) create mode 100644 src/animation_lut.c create mode 100644 src/animation_lut.h diff --git a/build-number.txt b/build-number.txt index 102c15d..53c86ff 100644 --- a/build-number.txt +++ b/build-number.txt @@ -1 +1 @@ -409 +417 diff --git a/src/animation.c b/src/animation.c index 44f219f..7b075f1 100644 --- a/src/animation.c +++ b/src/animation.c @@ -1,4 +1,5 @@ #include "animation.h" +#include "animation_lut.h" #include "light_sensor.h" volatile bool Animation_FrameFlag = false; @@ -10,265 +11,6 @@ const unsigned int Animation_LEDOrder[LED_COUNT] = 25, 2, 14, 31, 7, 11, 9, 23 }; -const LED_Colour_t Animation_ColourLUT[] = -{ - { .r = 255, .g = 51, .b = 51 }, - { .r = 255, .g = 51, .b = 56 }, - { .r = 255, .g = 51, .b = 61 }, - { .r = 255, .g = 51, .b = 65 }, - { .r = 255, .g = 51, .b = 70 }, - { .r = 255, .g = 51, .b = 75 }, - { .r = 255, .g = 51, .b = 80 }, - { .r = 255, .g = 51, .b = 85 }, - { .r = 255, .g = 51, .b = 89 }, - { .r = 255, .g = 51, .b = 94 }, - { .r = 255, .g = 51, .b = 99 }, - { .r = 255, .g = 51, .b = 104 }, - { .r = 255, .g = 51, .b = 109 }, - { .r = 255, .g = 51, .b = 113 }, - { .r = 255, .g = 51, .b = 118 }, - { .r = 255, .g = 51, .b = 123 }, - { .r = 255, .g = 51, .b = 128 }, - { .r = 255, .g = 51, .b = 133 }, - { .r = 255, .g = 51, .b = 137 }, - { .r = 255, .g = 51, .b = 142 }, - { .r = 255, .g = 51, .b = 147 }, - { .r = 255, .g = 51, .b = 152 }, - { .r = 255, .g = 51, .b = 157 }, - { .r = 255, .g = 51, .b = 161 }, - { .r = 255, .g = 51, .b = 166 }, - { .r = 255, .g = 51, .b = 171 }, - { .r = 255, .g = 51, .b = 176 }, - { .r = 255, .g = 51, .b = 181 }, - { .r = 255, .g = 51, .b = 185 }, - { .r = 255, .g = 51, .b = 190 }, - { .r = 255, .g = 51, .b = 195 }, - { .r = 255, .g = 51, .b = 200 }, - { .r = 255, .g = 51, .b = 205 }, - { .r = 255, .g = 51, .b = 209 }, - { .r = 255, .g = 51, .b = 214 }, - { .r = 255, .g = 51, .b = 219 }, - { .r = 255, .g = 51, .b = 224 }, - { .r = 255, .g = 51, .b = 229 }, - { .r = 255, .g = 51, .b = 233 }, - { .r = 255, .g = 51, .b = 238 }, - { .r = 255, .g = 51, .b = 243 }, - { .r = 255, .g = 51, .b = 248 }, - { .r = 255, .g = 51, .b = 253 }, - { .r = 253, .g = 51, .b = 255 }, - { .r = 248, .g = 51, .b = 255 }, - { .r = 243, .g = 51, .b = 255 }, - { .r = 238, .g = 51, .b = 255 }, - { .r = 233, .g = 51, .b = 255 }, - { .r = 229, .g = 51, .b = 255 }, - { .r = 224, .g = 51, .b = 255 }, - { .r = 219, .g = 51, .b = 255 }, - { .r = 214, .g = 51, .b = 255 }, - { .r = 209, .g = 51, .b = 255 }, - { .r = 205, .g = 51, .b = 255 }, - { .r = 200, .g = 51, .b = 255 }, - { .r = 195, .g = 51, .b = 255 }, - { .r = 190, .g = 51, .b = 255 }, - { .r = 185, .g = 51, .b = 255 }, - { .r = 181, .g = 51, .b = 255 }, - { .r = 176, .g = 51, .b = 255 }, - { .r = 171, .g = 51, .b = 255 }, - { .r = 166, .g = 51, .b = 255 }, - { .r = 161, .g = 51, .b = 255 }, - { .r = 157, .g = 51, .b = 255 }, - { .r = 152, .g = 51, .b = 255 }, - { .r = 147, .g = 51, .b = 255 }, - { .r = 142, .g = 51, .b = 255 }, - { .r = 137, .g = 51, .b = 255 }, - { .r = 133, .g = 51, .b = 255 }, - { .r = 128, .g = 51, .b = 255 }, - { .r = 123, .g = 51, .b = 255 }, - { .r = 118, .g = 51, .b = 255 }, - { .r = 113, .g = 51, .b = 255 }, - { .r = 109, .g = 51, .b = 255 }, - { .r = 104, .g = 51, .b = 255 }, - { .r = 99, .g = 51, .b = 255 }, - { .r = 94, .g = 51, .b = 255 }, - { .r = 89, .g = 51, .b = 255 }, - { .r = 85, .g = 51, .b = 255 }, - { .r = 80, .g = 51, .b = 255 }, - { .r = 75, .g = 51, .b = 255 }, - { .r = 70, .g = 51, .b = 255 }, - { .r = 65, .g = 51, .b = 255 }, - { .r = 61, .g = 51, .b = 255 }, - { .r = 56, .g = 51, .b = 255 }, - { .r = 51, .g = 51, .b = 255 }, - { .r = 51, .g = 56, .b = 255 }, - { .r = 51, .g = 61, .b = 255 }, - { .r = 51, .g = 65, .b = 255 }, - { .r = 51, .g = 70, .b = 255 }, - { .r = 51, .g = 75, .b = 255 }, - { .r = 51, .g = 80, .b = 255 }, - { .r = 51, .g = 85, .b = 255 }, - { .r = 51, .g = 89, .b = 255 }, - { .r = 51, .g = 94, .b = 255 }, - { .r = 51, .g = 99, .b = 255 }, - { .r = 51, .g = 104, .b = 255 }, - { .r = 51, .g = 109, .b = 255 }, - { .r = 51, .g = 113, .b = 255 }, - { .r = 51, .g = 118, .b = 255 }, - { .r = 51, .g = 123, .b = 255 }, - { .r = 51, .g = 128, .b = 255 }, - { .r = 51, .g = 133, .b = 255 }, - { .r = 51, .g = 137, .b = 255 }, - { .r = 51, .g = 142, .b = 255 }, - { .r = 51, .g = 147, .b = 255 }, - { .r = 51, .g = 152, .b = 255 }, - { .r = 51, .g = 157, .b = 255 }, - { .r = 51, .g = 161, .b = 255 }, - { .r = 51, .g = 166, .b = 255 }, - { .r = 51, .g = 171, .b = 255 }, - { .r = 51, .g = 176, .b = 255 }, - { .r = 51, .g = 181, .b = 255 }, - { .r = 51, .g = 185, .b = 255 }, - { .r = 51, .g = 190, .b = 255 }, - { .r = 51, .g = 195, .b = 255 }, - { .r = 51, .g = 200, .b = 255 }, - { .r = 51, .g = 205, .b = 255 }, - { .r = 51, .g = 209, .b = 255 }, - { .r = 51, .g = 214, .b = 255 }, - { .r = 51, .g = 219, .b = 255 }, - { .r = 51, .g = 224, .b = 255 }, - { .r = 51, .g = 229, .b = 255 }, - { .r = 51, .g = 233, .b = 255 }, - { .r = 51, .g = 238, .b = 255 }, - { .r = 51, .g = 243, .b = 255 }, - { .r = 51, .g = 248, .b = 255 }, - { .r = 51, .g = 253, .b = 255 }, - { .r = 51, .g = 255, .b = 253 }, - { .r = 51, .g = 255, .b = 248 }, - { .r = 51, .g = 255, .b = 243 }, - { .r = 51, .g = 255, .b = 238 }, - { .r = 51, .g = 255, .b = 233 }, - { .r = 51, .g = 255, .b = 229 }, - { .r = 51, .g = 255, .b = 224 }, - { .r = 51, .g = 255, .b = 219 }, - { .r = 51, .g = 255, .b = 214 }, - { .r = 51, .g = 255, .b = 209 }, - { .r = 51, .g = 255, .b = 205 }, - { .r = 51, .g = 255, .b = 200 }, - { .r = 51, .g = 255, .b = 195 }, - { .r = 51, .g = 255, .b = 190 }, - { .r = 51, .g = 255, .b = 185 }, - { .r = 51, .g = 255, .b = 181 }, - { .r = 51, .g = 255, .b = 176 }, - { .r = 51, .g = 255, .b = 171 }, - { .r = 51, .g = 255, .b = 166 }, - { .r = 51, .g = 255, .b = 161 }, - { .r = 51, .g = 255, .b = 157 }, - { .r = 51, .g = 255, .b = 152 }, - { .r = 51, .g = 255, .b = 147 }, - { .r = 51, .g = 255, .b = 142 }, - { .r = 51, .g = 255, .b = 137 }, - { .r = 51, .g = 255, .b = 133 }, - { .r = 51, .g = 255, .b = 128 }, - { .r = 51, .g = 255, .b = 123 }, - { .r = 51, .g = 255, .b = 118 }, - { .r = 51, .g = 255, .b = 113 }, - { .r = 51, .g = 255, .b = 109 }, - { .r = 51, .g = 255, .b = 104 }, - { .r = 51, .g = 255, .b = 99 }, - { .r = 51, .g = 255, .b = 94 }, - { .r = 51, .g = 255, .b = 89 }, - { .r = 51, .g = 255, .b = 85 }, - { .r = 51, .g = 255, .b = 80 }, - { .r = 51, .g = 255, .b = 75 }, - { .r = 51, .g = 255, .b = 70 }, - { .r = 51, .g = 255, .b = 65 }, - { .r = 51, .g = 255, .b = 61 }, - { .r = 51, .g = 255, .b = 56 }, - { .r = 51, .g = 255, .b = 51 }, - { .r = 56, .g = 255, .b = 51 }, - { .r = 61, .g = 255, .b = 51 }, - { .r = 65, .g = 255, .b = 51 }, - { .r = 70, .g = 255, .b = 51 }, - { .r = 75, .g = 255, .b = 51 }, - { .r = 80, .g = 255, .b = 51 }, - { .r = 85, .g = 255, .b = 51 }, - { .r = 89, .g = 255, .b = 51 }, - { .r = 94, .g = 255, .b = 51 }, - { .r = 99, .g = 255, .b = 51 }, - { .r = 104, .g = 255, .b = 51 }, - { .r = 109, .g = 255, .b = 51 }, - { .r = 113, .g = 255, .b = 51 }, - { .r = 118, .g = 255, .b = 51 }, - { .r = 123, .g = 255, .b = 51 }, - { .r = 128, .g = 255, .b = 51 }, - { .r = 133, .g = 255, .b = 51 }, - { .r = 137, .g = 255, .b = 51 }, - { .r = 142, .g = 255, .b = 51 }, - { .r = 147, .g = 255, .b = 51 }, - { .r = 152, .g = 255, .b = 51 }, - { .r = 157, .g = 255, .b = 51 }, - { .r = 161, .g = 255, .b = 51 }, - { .r = 166, .g = 255, .b = 51 }, - { .r = 171, .g = 255, .b = 51 }, - { .r = 176, .g = 255, .b = 51 }, - { .r = 181, .g = 255, .b = 51 }, - { .r = 185, .g = 255, .b = 51 }, - { .r = 190, .g = 255, .b = 51 }, - { .r = 195, .g = 255, .b = 51 }, - { .r = 200, .g = 255, .b = 51 }, - { .r = 205, .g = 255, .b = 51 }, - { .r = 209, .g = 255, .b = 51 }, - { .r = 214, .g = 255, .b = 51 }, - { .r = 219, .g = 255, .b = 51 }, - { .r = 224, .g = 255, .b = 51 }, - { .r = 229, .g = 255, .b = 51 }, - { .r = 233, .g = 255, .b = 51 }, - { .r = 238, .g = 255, .b = 51 }, - { .r = 243, .g = 255, .b = 51 }, - { .r = 248, .g = 255, .b = 51 }, - { .r = 253, .g = 255, .b = 51 }, - { .r = 255, .g = 253, .b = 51 }, - { .r = 255, .g = 248, .b = 51 }, - { .r = 255, .g = 243, .b = 51 }, - { .r = 255, .g = 238, .b = 51 }, - { .r = 255, .g = 233, .b = 51 }, - { .r = 255, .g = 229, .b = 51 }, - { .r = 255, .g = 224, .b = 51 }, - { .r = 255, .g = 219, .b = 51 }, - { .r = 255, .g = 214, .b = 51 }, - { .r = 255, .g = 209, .b = 51 }, - { .r = 255, .g = 205, .b = 51 }, - { .r = 255, .g = 200, .b = 51 }, - { .r = 255, .g = 195, .b = 51 }, - { .r = 255, .g = 190, .b = 51 }, - { .r = 255, .g = 185, .b = 51 }, - { .r = 255, .g = 181, .b = 51 }, - { .r = 255, .g = 176, .b = 51 }, - { .r = 255, .g = 171, .b = 51 }, - { .r = 255, .g = 166, .b = 51 }, - { .r = 255, .g = 161, .b = 51 }, - { .r = 255, .g = 157, .b = 51 }, - { .r = 255, .g = 152, .b = 51 }, - { .r = 255, .g = 147, .b = 51 }, - { .r = 255, .g = 142, .b = 51 }, - { .r = 255, .g = 137, .b = 51 }, - { .r = 255, .g = 133, .b = 51 }, - { .r = 255, .g = 128, .b = 51 }, - { .r = 255, .g = 123, .b = 51 }, - { .r = 255, .g = 118, .b = 51 }, - { .r = 255, .g = 113, .b = 51 }, - { .r = 255, .g = 109, .b = 51 }, - { .r = 255, .g = 104, .b = 51 }, - { .r = 255, .g = 99, .b = 51 }, - { .r = 255, .g = 94, .b = 51 }, - { .r = 255, .g = 89, .b = 51 }, - { .r = 255, .g = 85, .b = 51 }, - { .r = 255, .g = 80, .b = 51 }, - { .r = 255, .g = 75, .b = 51 }, - { .r = 255, .g = 70, .b = 51 }, - { .r = 255, .g = 65, .b = 51 }, - { .r = 255, .g = 61, .b = 51 }, - { .r = 255, .g = 56, .b = 51 }, -}; - LED_Colour_t Animation_GetColour(unsigned int step, unsigned int brightness) { const unsigned int lut_size = sizeof(Animation_ColourLUT) diff --git a/src/animation_lut.c b/src/animation_lut.c new file mode 100644 index 0000000..8ef4e12 --- /dev/null +++ b/src/animation_lut.c @@ -0,0 +1,260 @@ +#include "animation_lut.h" + +const LED_Colour_t Animation_ColourLUT[] = +{ + { .r = 255, .g = 51, .b = 51 }, + { .r = 255, .g = 51, .b = 56 }, + { .r = 255, .g = 51, .b = 61 }, + { .r = 255, .g = 51, .b = 65 }, + { .r = 255, .g = 51, .b = 70 }, + { .r = 255, .g = 51, .b = 75 }, + { .r = 255, .g = 51, .b = 80 }, + { .r = 255, .g = 51, .b = 85 }, + { .r = 255, .g = 51, .b = 89 }, + { .r = 255, .g = 51, .b = 94 }, + { .r = 255, .g = 51, .b = 99 }, + { .r = 255, .g = 51, .b = 104 }, + { .r = 255, .g = 51, .b = 109 }, + { .r = 255, .g = 51, .b = 113 }, + { .r = 255, .g = 51, .b = 118 }, + { .r = 255, .g = 51, .b = 123 }, + { .r = 255, .g = 51, .b = 128 }, + { .r = 255, .g = 51, .b = 133 }, + { .r = 255, .g = 51, .b = 137 }, + { .r = 255, .g = 51, .b = 142 }, + { .r = 255, .g = 51, .b = 147 }, + { .r = 255, .g = 51, .b = 152 }, + { .r = 255, .g = 51, .b = 157 }, + { .r = 255, .g = 51, .b = 161 }, + { .r = 255, .g = 51, .b = 166 }, + { .r = 255, .g = 51, .b = 171 }, + { .r = 255, .g = 51, .b = 176 }, + { .r = 255, .g = 51, .b = 181 }, + { .r = 255, .g = 51, .b = 185 }, + { .r = 255, .g = 51, .b = 190 }, + { .r = 255, .g = 51, .b = 195 }, + { .r = 255, .g = 51, .b = 200 }, + { .r = 255, .g = 51, .b = 205 }, + { .r = 255, .g = 51, .b = 209 }, + { .r = 255, .g = 51, .b = 214 }, + { .r = 255, .g = 51, .b = 219 }, + { .r = 255, .g = 51, .b = 224 }, + { .r = 255, .g = 51, .b = 229 }, + { .r = 255, .g = 51, .b = 233 }, + { .r = 255, .g = 51, .b = 238 }, + { .r = 255, .g = 51, .b = 243 }, + { .r = 255, .g = 51, .b = 248 }, + { .r = 255, .g = 51, .b = 253 }, + { .r = 253, .g = 51, .b = 255 }, + { .r = 248, .g = 51, .b = 255 }, + { .r = 243, .g = 51, .b = 255 }, + { .r = 238, .g = 51, .b = 255 }, + { .r = 233, .g = 51, .b = 255 }, + { .r = 229, .g = 51, .b = 255 }, + { .r = 224, .g = 51, .b = 255 }, + { .r = 219, .g = 51, .b = 255 }, + { .r = 214, .g = 51, .b = 255 }, + { .r = 209, .g = 51, .b = 255 }, + { .r = 205, .g = 51, .b = 255 }, + { .r = 200, .g = 51, .b = 255 }, + { .r = 195, .g = 51, .b = 255 }, + { .r = 190, .g = 51, .b = 255 }, + { .r = 185, .g = 51, .b = 255 }, + { .r = 181, .g = 51, .b = 255 }, + { .r = 176, .g = 51, .b = 255 }, + { .r = 171, .g = 51, .b = 255 }, + { .r = 166, .g = 51, .b = 255 }, + { .r = 161, .g = 51, .b = 255 }, + { .r = 157, .g = 51, .b = 255 }, + { .r = 152, .g = 51, .b = 255 }, + { .r = 147, .g = 51, .b = 255 }, + { .r = 142, .g = 51, .b = 255 }, + { .r = 137, .g = 51, .b = 255 }, + { .r = 133, .g = 51, .b = 255 }, + { .r = 128, .g = 51, .b = 255 }, + { .r = 123, .g = 51, .b = 255 }, + { .r = 118, .g = 51, .b = 255 }, + { .r = 113, .g = 51, .b = 255 }, + { .r = 109, .g = 51, .b = 255 }, + { .r = 104, .g = 51, .b = 255 }, + { .r = 99, .g = 51, .b = 255 }, + { .r = 94, .g = 51, .b = 255 }, + { .r = 89, .g = 51, .b = 255 }, + { .r = 85, .g = 51, .b = 255 }, + { .r = 80, .g = 51, .b = 255 }, + { .r = 75, .g = 51, .b = 255 }, + { .r = 70, .g = 51, .b = 255 }, + { .r = 65, .g = 51, .b = 255 }, + { .r = 61, .g = 51, .b = 255 }, + { .r = 56, .g = 51, .b = 255 }, + { .r = 51, .g = 51, .b = 255 }, + { .r = 51, .g = 56, .b = 255 }, + { .r = 51, .g = 61, .b = 255 }, + { .r = 51, .g = 65, .b = 255 }, + { .r = 51, .g = 70, .b = 255 }, + { .r = 51, .g = 75, .b = 255 }, + { .r = 51, .g = 80, .b = 255 }, + { .r = 51, .g = 85, .b = 255 }, + { .r = 51, .g = 89, .b = 255 }, + { .r = 51, .g = 94, .b = 255 }, + { .r = 51, .g = 99, .b = 255 }, + { .r = 51, .g = 104, .b = 255 }, + { .r = 51, .g = 109, .b = 255 }, + { .r = 51, .g = 113, .b = 255 }, + { .r = 51, .g = 118, .b = 255 }, + { .r = 51, .g = 123, .b = 255 }, + { .r = 51, .g = 128, .b = 255 }, + { .r = 51, .g = 133, .b = 255 }, + { .r = 51, .g = 137, .b = 255 }, + { .r = 51, .g = 142, .b = 255 }, + { .r = 51, .g = 147, .b = 255 }, + { .r = 51, .g = 152, .b = 255 }, + { .r = 51, .g = 157, .b = 255 }, + { .r = 51, .g = 161, .b = 255 }, + { .r = 51, .g = 166, .b = 255 }, + { .r = 51, .g = 171, .b = 255 }, + { .r = 51, .g = 176, .b = 255 }, + { .r = 51, .g = 181, .b = 255 }, + { .r = 51, .g = 185, .b = 255 }, + { .r = 51, .g = 190, .b = 255 }, + { .r = 51, .g = 195, .b = 255 }, + { .r = 51, .g = 200, .b = 255 }, + { .r = 51, .g = 205, .b = 255 }, + { .r = 51, .g = 209, .b = 255 }, + { .r = 51, .g = 214, .b = 255 }, + { .r = 51, .g = 219, .b = 255 }, + { .r = 51, .g = 224, .b = 255 }, + { .r = 51, .g = 229, .b = 255 }, + { .r = 51, .g = 233, .b = 255 }, + { .r = 51, .g = 238, .b = 255 }, + { .r = 51, .g = 243, .b = 255 }, + { .r = 51, .g = 248, .b = 255 }, + { .r = 51, .g = 253, .b = 255 }, + { .r = 51, .g = 255, .b = 253 }, + { .r = 51, .g = 255, .b = 248 }, + { .r = 51, .g = 255, .b = 243 }, + { .r = 51, .g = 255, .b = 238 }, + { .r = 51, .g = 255, .b = 233 }, + { .r = 51, .g = 255, .b = 229 }, + { .r = 51, .g = 255, .b = 224 }, + { .r = 51, .g = 255, .b = 219 }, + { .r = 51, .g = 255, .b = 214 }, + { .r = 51, .g = 255, .b = 209 }, + { .r = 51, .g = 255, .b = 205 }, + { .r = 51, .g = 255, .b = 200 }, + { .r = 51, .g = 255, .b = 195 }, + { .r = 51, .g = 255, .b = 190 }, + { .r = 51, .g = 255, .b = 185 }, + { .r = 51, .g = 255, .b = 181 }, + { .r = 51, .g = 255, .b = 176 }, + { .r = 51, .g = 255, .b = 171 }, + { .r = 51, .g = 255, .b = 166 }, + { .r = 51, .g = 255, .b = 161 }, + { .r = 51, .g = 255, .b = 157 }, + { .r = 51, .g = 255, .b = 152 }, + { .r = 51, .g = 255, .b = 147 }, + { .r = 51, .g = 255, .b = 142 }, + { .r = 51, .g = 255, .b = 137 }, + { .r = 51, .g = 255, .b = 133 }, + { .r = 51, .g = 255, .b = 128 }, + { .r = 51, .g = 255, .b = 123 }, + { .r = 51, .g = 255, .b = 118 }, + { .r = 51, .g = 255, .b = 113 }, + { .r = 51, .g = 255, .b = 109 }, + { .r = 51, .g = 255, .b = 104 }, + { .r = 51, .g = 255, .b = 99 }, + { .r = 51, .g = 255, .b = 94 }, + { .r = 51, .g = 255, .b = 89 }, + { .r = 51, .g = 255, .b = 85 }, + { .r = 51, .g = 255, .b = 80 }, + { .r = 51, .g = 255, .b = 75 }, + { .r = 51, .g = 255, .b = 70 }, + { .r = 51, .g = 255, .b = 65 }, + { .r = 51, .g = 255, .b = 61 }, + { .r = 51, .g = 255, .b = 56 }, + { .r = 51, .g = 255, .b = 51 }, + { .r = 56, .g = 255, .b = 51 }, + { .r = 61, .g = 255, .b = 51 }, + { .r = 65, .g = 255, .b = 51 }, + { .r = 70, .g = 255, .b = 51 }, + { .r = 75, .g = 255, .b = 51 }, + { .r = 80, .g = 255, .b = 51 }, + { .r = 85, .g = 255, .b = 51 }, + { .r = 89, .g = 255, .b = 51 }, + { .r = 94, .g = 255, .b = 51 }, + { .r = 99, .g = 255, .b = 51 }, + { .r = 104, .g = 255, .b = 51 }, + { .r = 109, .g = 255, .b = 51 }, + { .r = 113, .g = 255, .b = 51 }, + { .r = 118, .g = 255, .b = 51 }, + { .r = 123, .g = 255, .b = 51 }, + { .r = 128, .g = 255, .b = 51 }, + { .r = 133, .g = 255, .b = 51 }, + { .r = 137, .g = 255, .b = 51 }, + { .r = 142, .g = 255, .b = 51 }, + { .r = 147, .g = 255, .b = 51 }, + { .r = 152, .g = 255, .b = 51 }, + { .r = 157, .g = 255, .b = 51 }, + { .r = 161, .g = 255, .b = 51 }, + { .r = 166, .g = 255, .b = 51 }, + { .r = 171, .g = 255, .b = 51 }, + { .r = 176, .g = 255, .b = 51 }, + { .r = 181, .g = 255, .b = 51 }, + { .r = 185, .g = 255, .b = 51 }, + { .r = 190, .g = 255, .b = 51 }, + { .r = 195, .g = 255, .b = 51 }, + { .r = 200, .g = 255, .b = 51 }, + { .r = 205, .g = 255, .b = 51 }, + { .r = 209, .g = 255, .b = 51 }, + { .r = 214, .g = 255, .b = 51 }, + { .r = 219, .g = 255, .b = 51 }, + { .r = 224, .g = 255, .b = 51 }, + { .r = 229, .g = 255, .b = 51 }, + { .r = 233, .g = 255, .b = 51 }, + { .r = 238, .g = 255, .b = 51 }, + { .r = 243, .g = 255, .b = 51 }, + { .r = 248, .g = 255, .b = 51 }, + { .r = 253, .g = 255, .b = 51 }, + { .r = 255, .g = 253, .b = 51 }, + { .r = 255, .g = 248, .b = 51 }, + { .r = 255, .g = 243, .b = 51 }, + { .r = 255, .g = 238, .b = 51 }, + { .r = 255, .g = 233, .b = 51 }, + { .r = 255, .g = 229, .b = 51 }, + { .r = 255, .g = 224, .b = 51 }, + { .r = 255, .g = 219, .b = 51 }, + { .r = 255, .g = 214, .b = 51 }, + { .r = 255, .g = 209, .b = 51 }, + { .r = 255, .g = 205, .b = 51 }, + { .r = 255, .g = 200, .b = 51 }, + { .r = 255, .g = 195, .b = 51 }, + { .r = 255, .g = 190, .b = 51 }, + { .r = 255, .g = 185, .b = 51 }, + { .r = 255, .g = 181, .b = 51 }, + { .r = 255, .g = 176, .b = 51 }, + { .r = 255, .g = 171, .b = 51 }, + { .r = 255, .g = 166, .b = 51 }, + { .r = 255, .g = 161, .b = 51 }, + { .r = 255, .g = 157, .b = 51 }, + { .r = 255, .g = 152, .b = 51 }, + { .r = 255, .g = 147, .b = 51 }, + { .r = 255, .g = 142, .b = 51 }, + { .r = 255, .g = 137, .b = 51 }, + { .r = 255, .g = 133, .b = 51 }, + { .r = 255, .g = 128, .b = 51 }, + { .r = 255, .g = 123, .b = 51 }, + { .r = 255, .g = 118, .b = 51 }, + { .r = 255, .g = 113, .b = 51 }, + { .r = 255, .g = 109, .b = 51 }, + { .r = 255, .g = 104, .b = 51 }, + { .r = 255, .g = 99, .b = 51 }, + { .r = 255, .g = 94, .b = 51 }, + { .r = 255, .g = 89, .b = 51 }, + { .r = 255, .g = 85, .b = 51 }, + { .r = 255, .g = 80, .b = 51 }, + { .r = 255, .g = 75, .b = 51 }, + { .r = 255, .g = 70, .b = 51 }, + { .r = 255, .g = 65, .b = 51 }, + { .r = 255, .g = 61, .b = 51 }, + { .r = 255, .g = 56, .b = 51 }, +}; diff --git a/src/animation_lut.h b/src/animation_lut.h new file mode 100644 index 0000000..09dcff0 --- /dev/null +++ b/src/animation_lut.h @@ -0,0 +1,5 @@ +#pragma once + +#include "led.h" + +extern const LED_Colour_t Animation_ColourLUT[255]; From 5d1ef2263af6705a16a6c886533abe6673fbff9a Mon Sep 17 00:00:00 2001 From: fruchti Date: Fri, 17 Jul 2020 23:31:57 +0200 Subject: [PATCH 10/31] Gradually reset maximum/minimum brightness --- build-number.txt | 2 +- src/light_sensor.c | 11 +++++++++++ src/light_sensor.h | 4 ++++ 3 files changed, 16 insertions(+), 1 deletion(-) diff --git a/build-number.txt b/build-number.txt index 53c86ff..9524ef4 100644 --- a/build-number.txt +++ b/build-number.txt @@ -1 +1 @@ -417 +424 diff --git a/src/light_sensor.c b/src/light_sensor.c index 4ec450f..8a87dbd 100644 --- a/src/light_sensor.c +++ b/src/light_sensor.c @@ -99,6 +99,17 @@ void LightSensor_Poll(void) { LightSensor_RelativeBrightness = LIGHTSENSOR_MAX; } + + // Slowly reset limit values + static int decay_counter = 0; + decay_counter++; + if(decay_counter == LIGHTSENSOR_LIMIT_RESET_TIME * 1000 + / LIGHTSENSOR_INTERVAL / LIGHTSENSOR_MAX) + { + decay_counter = 0; + LightSensor_MaximumBrightness -= 1; + LightSensor_MinimumBrightness += 1; + } } } diff --git a/src/light_sensor.h b/src/light_sensor.h index bc99ee2..1315b28 100644 --- a/src/light_sensor.h +++ b/src/light_sensor.h @@ -15,6 +15,10 @@ // 'Forgetting factor' of the rolling brightness average #define LIGHTSENSOR_LAMBDA_BITS 4 +// Time until minimum and maximum value are completely reset (in seconds) +#define LIGHTSENSOR_LIMIT_RESET_TIME \ + (24 * 60 * 60) + // Bounds for converting absolute to relative brightness: Consider everything // near the minimum or maximum 0.0 or 1.0, respectively #define LIGHTSENSOR_LOW_BOUND ((unsigned int)(0.005 * LIGHTSENSOR_MAX)) From a3da7c3803ee4832f13dd1dd5f38581a1cdd7337 Mon Sep 17 00:00:00 2001 From: fruchti Date: Fri, 17 Jul 2020 23:38:02 +0200 Subject: [PATCH 11/31] Define animation based on cycle time --- build-number.txt | 2 +- src/animation.c | 6 ++++-- src/animation.h | 10 ++++++---- 3 files changed, 11 insertions(+), 7 deletions(-) diff --git a/build-number.txt b/build-number.txt index 9524ef4..5e4a522 100644 --- a/build-number.txt +++ b/build-number.txt @@ -1 +1 @@ -424 +425 diff --git a/src/animation.c b/src/animation.c index 7b075f1..c90af11 100644 --- a/src/animation.c +++ b/src/animation.c @@ -69,8 +69,10 @@ void Animation_Poll(void) static unsigned int bottom = 0; static unsigned int top = 0; - bottom += ANIMATION_STEPS / ANIMATION_DURATION_BOTTOM; - top += ANIMATION_STEPS / ANIMATION_DURATION_TOP; + bottom += ANIMATION_STEPS / ANIMATION_CYCLE_TIME_BOTTOM + / ANIMATION_REFRESH_RATE; + top += ANIMATION_STEPS / ANIMATION_CYCLE_TIME_TOP + / ANIMATION_REFRESH_RATE; bottom %= 2 * ANIMATION_STEPS; top %= 2 * ANIMATION_STEPS; diff --git a/src/animation.h b/src/animation.h index eecdd54..28ddc25 100644 --- a/src/animation.h +++ b/src/animation.h @@ -8,10 +8,12 @@ #define ANIMATION_REFRESH_RATE 10 -#define ANIMATION_DURATION_BOTTOM \ - 6048000 - -#define ANIMATION_DURATION_TOP 6652800 +// Cycle time of the animation for the bottom end of the gradient (in seconds) +#define ANIMATION_CYCLE_TIME_BOTTOM \ + (6 * 24 * 60 * 60) +// Cycle time of the animation for the top end of the gradient (in seconds) +#define ANIMATION_CYCLE_TIME_TOP \ + (8 * 24 * 60 * 60) extern const unsigned int Animation_LEDOrder[LED_COUNT]; From d6a5ee168928e3942363ed5de56570555a0f6351 Mon Sep 17 00:00:00 2001 From: fruchti Date: Sat, 18 Jul 2020 00:21:11 +0200 Subject: [PATCH 12/31] Tune light sensor parameters --- build-number.txt | 2 +- src/light_sensor.h | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/build-number.txt b/build-number.txt index 5e4a522..e828e5d 100644 --- a/build-number.txt +++ b/build-number.txt @@ -1 +1 @@ -425 +434 diff --git a/src/light_sensor.h b/src/light_sensor.h index 1315b28..2331eae 100644 --- a/src/light_sensor.h +++ b/src/light_sensor.h @@ -6,23 +6,23 @@ #include "pinning.h" // ADC polling interval in milliseconds -#define LIGHTSENSOR_INTERVAL 250 +#define LIGHTSENSOR_INTERVAL 500 // Resolution of the brightness output #define LIGHTSENSOR_BITS 12 #define LIGHTSENSOR_MAX (1 << LIGHTSENSOR_BITS) // 'Forgetting factor' of the rolling brightness average -#define LIGHTSENSOR_LAMBDA_BITS 4 +#define LIGHTSENSOR_LAMBDA_BITS 2 // Time until minimum and maximum value are completely reset (in seconds) #define LIGHTSENSOR_LIMIT_RESET_TIME \ - (24 * 60 * 60) + (48 * 60 * 60) // Bounds for converting absolute to relative brightness: Consider everything // near the minimum or maximum 0.0 or 1.0, respectively -#define LIGHTSENSOR_LOW_BOUND ((unsigned int)(0.005 * LIGHTSENSOR_MAX)) -#define LIGHTSENSOR_HIGH_BOUND ((unsigned int)(0.9 * LIGHTSENSOR_MAX)) +#define LIGHTSENSOR_LOW_BOUND ((unsigned int)(0.013 * LIGHTSENSOR_MAX)) +#define LIGHTSENSOR_HIGH_BOUND ((unsigned int)(1.0 * LIGHTSENSOR_MAX)) extern int LightSensor_RelativeBrightness; From 2a3db7b9fbea8cdba848beeee3be67cdb7feb122 Mon Sep 17 00:00:00 2001 From: fruchti Date: Wed, 2 Sep 2020 00:12:14 +0200 Subject: [PATCH 13/31] Fix EOF newlines --- src/animation.c | 2 +- src/animation.h | 2 +- src/led.h | 1 - src/light_sensor.c | 2 +- src/main.c | 1 - src/main.h | 1 - src/system.c | 1 - 7 files changed, 3 insertions(+), 7 deletions(-) diff --git a/src/animation.c b/src/animation.c index c90af11..7761c31 100644 --- a/src/animation.c +++ b/src/animation.c @@ -85,4 +85,4 @@ void TIM17_IRQHandler(void) { Animation_FrameFlag = true; TIM17->SR &= ~TIM_SR_UIF; -} \ No newline at end of file +} diff --git a/src/animation.h b/src/animation.h index 28ddc25..144291e 100644 --- a/src/animation.h +++ b/src/animation.h @@ -18,4 +18,4 @@ extern const unsigned int Animation_LEDOrder[LED_COUNT]; void Animation_Init(void); -void Animation_Poll(void); \ No newline at end of file +void Animation_Poll(void); diff --git a/src/led.h b/src/led.h index 2416ffd..cb9f8ea 100644 --- a/src/led.h +++ b/src/led.h @@ -35,4 +35,3 @@ void LED_WakeUp(void); // Display LED_PixelData, starting with the next frame void LED_Commit(void); - diff --git a/src/light_sensor.c b/src/light_sensor.c index 8a87dbd..3cc495b 100644 --- a/src/light_sensor.c +++ b/src/light_sensor.c @@ -126,4 +126,4 @@ void TIM14_IRQHandler(void) LightSensor_NewMeasurement = true; LightSensor_Measure(); -} \ No newline at end of file +} diff --git a/src/main.c b/src/main.c index bb229ca..1f3e7d9 100644 --- a/src/main.c +++ b/src/main.c @@ -36,4 +36,3 @@ int main(void) return 0; } - diff --git a/src/main.h b/src/main.h index 20c8393..60ab8f9 100644 --- a/src/main.h +++ b/src/main.h @@ -10,4 +10,3 @@ #include "animation.h" int main(void); - diff --git a/src/system.c b/src/system.c index 7ca3df8..0a64ee1 100644 --- a/src/system.c +++ b/src/system.c @@ -6,4 +6,3 @@ void SystemInit(void) // Disable all interrupts RCC->CIR = 0x00000000; } - From 1e8da901560228bc49ad4dec825f38d6c10b69d2 Mon Sep 17 00:00:00 2001 From: fruchti Date: Sun, 20 Sep 2020 16:40:55 +0200 Subject: [PATCH 14/31] Allow using 9 columns to enable debugging --- build-number.txt | 2 +- src/animation.c | 9 +++++++++ src/led.c | 31 +++++++++++++++++++++++++++++++ src/led.h | 6 ++++-- 4 files changed, 45 insertions(+), 3 deletions(-) diff --git a/build-number.txt b/build-number.txt index e828e5d..18e1dd6 100644 --- a/build-number.txt +++ b/build-number.txt @@ -1 +1 @@ -434 +442 diff --git a/src/animation.c b/src/animation.c index 7761c31..3da8329 100644 --- a/src/animation.c +++ b/src/animation.c @@ -4,12 +4,21 @@ volatile bool Animation_FrameFlag = false; +#if LED_COLUMNS == 12 const unsigned int Animation_LEDOrder[LED_COUNT] = { 19, 27, 21, 13, 0, 4, 24, 8, 12, 15, 6, 5, 28, 29, 17, 3, 18, 26, 22, 10, 16, 20, 30, 1, 25, 2, 14, 31, 7, 11, 9, 23 }; +#elif LED_COLUMNS == 9 +const unsigned int Animation_LEDOrder[LED_COUNT] = +{ + 19, 21, 13, 0, 4, 8, 12, 15, 6, 5, + 17, 3, 18, 22, 10, 16, 20, 1, + 2, 14, 7, 11, 9, 23 +}; +#endif LED_Colour_t Animation_GetColour(unsigned int step, unsigned int brightness) { diff --git a/src/led.c b/src/led.c index 365545b..b7dea1f 100644 --- a/src/led.c +++ b/src/led.c @@ -6,6 +6,7 @@ volatile bool LED_FrameFlag = false; volatile bool LED_SuspendFlag = false; bool LED_Suspended = false; +#if LED_COLUMNS == 12 #define LED_ODR_MASK ((1 << PIN_LED_R_0) | (1 << PIN_LED_G_0) \ | (1 << PIN_LED_B_0) | (1 << PIN_LED_R_1) \ | (1 << PIN_LED_G_1) | (1 << PIN_LED_B_1) \ @@ -26,6 +27,27 @@ bool LED_Suspended = false; | (1 << PIN_LED_R_2 * 2) | (1 << PIN_LED_G_2 * 2) \ | (1 << PIN_LED_B_2 * 2) | (1 << PIN_LED_R_3 * 2) \ | (1 << PIN_LED_G_3 * 2) | (1 << PIN_LED_B_3 * 2)) +#elif LED_COLUMNS == 9 +#define LED_ODR_MASK ((1 << PIN_LED_R_0) | (1 << PIN_LED_G_0) \ + | (1 << PIN_LED_B_0) | (1 << PIN_LED_R_1) \ + | (1 << PIN_LED_G_1) | (1 << PIN_LED_B_1) \ + | (1 << PIN_LED_R_2) | (1 << PIN_LED_G_2) \ + | (1 << PIN_LED_B_2)) + +#define LED_MODER_MASK ((3 << PIN_LED_R_0 * 2) | (3 << PIN_LED_G_0 * 2) \ + | (3 << PIN_LED_B_0 * 2) | (3 << PIN_LED_R_1 * 2) \ + | (3 << PIN_LED_G_1 * 2) | (3 << PIN_LED_B_1 * 2) \ + | (3 << PIN_LED_R_2 * 2) | (3 << PIN_LED_G_2 * 2) \ + | (3 << PIN_LED_B_2 * 2)) + +#define LED_MODER ((1 << PIN_LED_R_0 * 2) | (1 << PIN_LED_G_0 * 2) \ + | (1 << PIN_LED_B_0 * 2) | (1 << PIN_LED_R_1 * 2) \ + | (1 << PIN_LED_G_1 * 2) | (1 << PIN_LED_B_1 * 2) \ + | (1 << PIN_LED_R_2 * 2) | (1 << PIN_LED_G_2 * 2) \ + | (1 << PIN_LED_B_2 * 2)) +#else +#error Unsupported LED column count +#endif // TIM3 is clocked by APB1 and thus receives only half the system clock. The 4 // LSBs have bit lengths 2, 4, 8, and 16 cycles and are generated blocking from @@ -35,6 +57,7 @@ static const uint16_t LED_BitLengths[LED_BITS - 4] = 16, 32, 64, 128, 256, 512, 1024, 2048 }; +#if LED_COLUMNS == 12 static const int LED_Pins[LED_COLUMNS] = { PIN_LED_R_0, PIN_LED_G_0, PIN_LED_B_0, @@ -42,6 +65,14 @@ static const int LED_Pins[LED_COLUMNS] = PIN_LED_R_2, PIN_LED_G_2, PIN_LED_B_2, PIN_LED_R_3, PIN_LED_G_3, PIN_LED_B_3 }; +#elif LED_COLUMNS == 9 +static const int LED_Pins[LED_COLUMNS] = +{ + PIN_LED_R_0, PIN_LED_G_0, PIN_LED_B_0, + PIN_LED_R_1, PIN_LED_G_1, PIN_LED_B_1, + PIN_LED_R_2, PIN_LED_G_2, PIN_LED_B_2 +}; +#endif // Number of 16-bit values to be transferred to the GPIO's output register via // DMA. Each value contains output values for all columns. For each row, a diff --git a/src/led.h b/src/led.h index cb9f8ea..0d35897 100644 --- a/src/led.h +++ b/src/led.h @@ -5,9 +5,11 @@ #include "stm32f030x6.h" #include "pinning.h" -#define LED_BITS 12 +#define LED_BITS 12 // BCM resolution in bits #define LED_ROWS 8 // Rows are driven by a shift register -#define LED_COLUMNS 12 // Columns are driven by the MCU directly +#define LED_COLUMNS 12 // Columns are driven by the MCU directly. + // Set to 9 to free the SWD pins and enable + // debugging. #define LED_COUNT (LED_ROWS * LED_COLUMNS / 3) typedef struct From 99b3d6a36cbe7d38f7d87bfa15c87de413142551 Mon Sep 17 00:00:00 2001 From: fruchti Date: Sat, 19 Sep 2020 19:54:31 +0200 Subject: [PATCH 15/31] Move animation steps to NVS data --- build-number.txt | 2 +- ld/{common.ld => common_nvs.ld} | 9 + ...ash.ld => stm32f030f4_15k_flash_1k_nvs.ld} | 5 +- makefile | 2 +- src/animation.c | 18 +- src/nvs.c | 183 ++++++++++++++++++ src/nvs.h | 23 +++ 7 files changed, 230 insertions(+), 12 deletions(-) rename ld/{common.ld => common_nvs.ld} (87%) rename ld/{stm32f030f4_flash.ld => stm32f030f4_15k_flash_1k_nvs.ld} (59%) create mode 100644 src/nvs.c create mode 100644 src/nvs.h diff --git a/build-number.txt b/build-number.txt index 18e1dd6..e5a135a 100644 --- a/build-number.txt +++ b/build-number.txt @@ -1 +1 @@ -442 +445 diff --git a/ld/common.ld b/ld/common_nvs.ld similarity index 87% rename from ld/common.ld rename to ld/common_nvs.ld index 9d9835a..91ea21c 100644 --- a/ld/common.ld +++ b/ld/common_nvs.ld @@ -12,6 +12,15 @@ SECTIONS _end_text = .; } >flash + /* Non-volatile storage area in flash */ + .nvstore (NOLOAD) : + { + . = ALIGN(1K); + *(.nvstore*) + . = ALIGN(1K); + _end_nvstore = .; + } > flash_nvstore + /* C++ initialiser code segments */ .preinit_array : { diff --git a/ld/stm32f030f4_flash.ld b/ld/stm32f030f4_15k_flash_1k_nvs.ld similarity index 59% rename from ld/stm32f030f4_flash.ld rename to ld/stm32f030f4_15k_flash_1k_nvs.ld index 9fecb90..93724af 100644 --- a/ld/stm32f030f4_flash.ld +++ b/ld/stm32f030f4_15k_flash_1k_nvs.ld @@ -1,7 +1,8 @@ MEMORY { - flash (rx) : ORIGIN = 0x08000000, LENGTH = 16K + flash (rx) : ORIGIN = 0x08000000, LENGTH = 15K + flash_nvstore (rw) : ORIGIN = 0x08003c00, LENGTH = 1K sram (xrw) : ORIGIN = 0x20000000, LENGTH = 4K } -INCLUDE ld/common.ld +INCLUDE ld/common_nvs.ld diff --git a/makefile b/makefile index f0849c5..6583cf1 100644 --- a/makefile +++ b/makefile @@ -10,7 +10,7 @@ DEBUG := yes H_DEVICE = STM32F030x6 STARTUP_SOURCE_DIR = src STARTUP_SOURCES = $(STARTUP_SOURCE_DIR)/startup.S -LD_SCRIPT = ld/stm32f030f4_flash.ld +LD_SCRIPT = ld/stm32f030f4_15k_flash_1k_nvs.ld ifeq ($(DEBUG),yes) DEBUG_FLAGS = -DDEBUG -g3 diff --git a/src/animation.c b/src/animation.c index 3da8329..6c50ff3 100644 --- a/src/animation.c +++ b/src/animation.c @@ -1,6 +1,7 @@ #include "animation.h" #include "animation_lut.h" #include "light_sensor.h" +#include "nvs.h" volatile bool Animation_FrameFlag = false; @@ -75,18 +76,19 @@ void Animation_Poll(void) } Animation_FrameFlag = false; - static unsigned int bottom = 0; - static unsigned int top = 0; - - bottom += ANIMATION_STEPS / ANIMATION_CYCLE_TIME_BOTTOM + NVS_Data->animation_step_bottom += ANIMATION_STEPS + / ANIMATION_CYCLE_TIME_BOTTOM / ANIMATION_REFRESH_RATE; - top += ANIMATION_STEPS / ANIMATION_CYCLE_TIME_TOP + NVS_Data->animation_step_top += ANIMATION_STEPS + / ANIMATION_CYCLE_TIME_TOP / ANIMATION_REFRESH_RATE; - bottom %= 2 * ANIMATION_STEPS; - top %= 2 * ANIMATION_STEPS; + NVS_Data->animation_step_bottom %= 2 * ANIMATION_STEPS; + NVS_Data->animation_step_top %= 2 * ANIMATION_STEPS; - Animation_DrawGradient(bottom, top, LightSensor_RelativeBrightness); + Animation_DrawGradient(NVS_Data->animation_step_bottom, + NVS_Data->animation_step_top, + LightSensor_RelativeBrightness); LED_Commit(); } diff --git a/src/nvs.c b/src/nvs.c new file mode 100644 index 0000000..1cb4466 --- /dev/null +++ b/src/nvs.c @@ -0,0 +1,183 @@ +#include +#include + +#include "nvs.h" + +// Note that the area used for storage must be aligned to the flash's pages. If +// you want to use a larger area, be sure to also change the size and location +// in the linker script. +#define NVS_AREA_SIZE 1024 +#define NVS_BLOCK_COUNT (NVS_AREA_SIZE / sizeof(NVS_Block_t)) +#define NVS_VALID_BLOCK_MARKER 0xab34 + +typedef struct +__attribute__((packed)) +{ + // Actual block data + NVS_Data_t data; + + // Ensure struct size is divisible by two + uint8_t padding[sizeof(NVS_Data_t) % 2]; + + // Block marker for detecting if the block is the current or an old block + uint16_t marker; + + uint32_t crc; +} NVS_Block_t; + +__attribute__((used, section(".nvstore"))) +volatile NVS_Block_t NVS_Area[NVS_BLOCK_COUNT]; + +volatile NVS_Block_t *NVS_FlashData; +__attribute__((used)) +NVS_Block_t NVS_RAMData; + +NVS_Data_t *const NVS_Data = &NVS_RAMData.data; + +static uint32_t NVS_CalculateCRC(NVS_Data_t *data) +{ + CRC->CR = CRC_CR_RESET; + for(int i = 0; i < sizeof(NVS_Data_t); i++) + { + CRC->DR = ((uint8_t*)(data))[i]; + } + return CRC->DR; +} + +static void NVS_ProgramHalfWord(uint16_t *dest, uint16_t value) +{ + FLASH->CR |= FLASH_CR_PG; + *(volatile uint16_t*)dest = value; + while(FLASH->SR & FLASH_SR_BSY); + if(*dest != value) + { + // Write failed + __asm__("bkpt"); + } +} + +static void NVS_UnlockFlash(void) +{ + while(FLASH->SR & FLASH_SR_BSY); + if(FLASH->CR & FLASH_CR_LOCK) + { + FLASH->KEYR = 0x45670123; + FLASH->KEYR = 0xcdef89ab; + } +} + +static void NVS_EraseArea(void) +{ + for(int i = 0; i < NVS_AREA_SIZE; i += 1024) + { + while(FLASH->SR & FLASH_SR_BSY); + FLASH->CR |= FLASH_CR_PER; + FLASH->AR = (uint32_t)NVS_Area + i; + FLASH->CR |= FLASH_CR_STRT; + while(FLASH->SR & FLASH_SR_BSY); + if(FLASH->SR & FLASH_SR_EOP) + { + // Clear EOP flag + FLASH->SR = FLASH_SR_EOP; + } + else + { + // Erase failed + __asm__("bkpt"); + } + FLASH->CR &= ~FLASH_CR_PER; + } +} + +static bool NVS_BlockEmpty(NVS_Block_t *block) +{ + for(int i = 0; i < sizeof(NVS_Block_t) / 2; i++) + { + if(*((uint16_t*)block + i) != 0xffff) + { + return false; + } + } + return true; +} + +static void NVS_LoadDefaults(void) +{ + NVS_Data->animation_step_bottom = 0; + NVS_Data->animation_step_top = 0; +} + +bool NVS_Load(void) +{ + RCC->AHBENR |= RCC_AHBENR_CRCEN; + + volatile NVS_Block_t *block = NULL; + + // Find valid block + for(int i = 0; i < NVS_BLOCK_COUNT; i++) + { + block = &NVS_Area[i]; + if(block->marker == NVS_VALID_BLOCK_MARKER) + { + // Valid block found, next up is the CRC check + break; + } + else if(block->marker == 0xffff) + { + // This block was erased and no block before was valid, so we can + // assume there is no valid block + block = NULL; + break; + } + } + + if(block == NULL || block->marker != NVS_VALID_BLOCK_MARKER + || block->crc != NVS_CalculateCRC(&block->data)) + { + // No valid block found + NVS_LoadDefaults(); + return false; + } + else + { + // Valid block and CRC check successful + NVS_FlashData = block; + memcpy(&NVS_RAMData, (void*)block, sizeof(NVS_Block_t)); + return true; + } +} + +void NVS_Save(void) +{ + NVS_UnlockFlash(); + + // Currently loaded block. Is NULL if the defaults were loaded in NVS_Load() + // instead of some flash contents. + NVS_Block_t *current_block = NVS_FlashData; + NVS_Block_t *next_block = NVS_FlashData + 1; + + if(current_block == NULL || next_block > NVS_Area + NVS_BLOCK_COUNT + || !NVS_BlockEmpty(next_block)) + { + NVS_EraseArea(); + next_block = &NVS_Area[0]; + current_block = NULL; + } + + NVS_RAMData.crc = NVS_CalculateCRC(NVS_Data); + NVS_RAMData.marker = NVS_VALID_BLOCK_MARKER; + + // The block length is guaranteed to be divisible by 2 + for(int i = 0; i < sizeof(NVS_Block_t) / 2; i++) + { + NVS_ProgramHalfWord((uint16_t*)next_block + i, + *((uint16_t*)&NVS_RAMData + i)); + } + + if(current_block != NULL) + { + NVS_ProgramHalfWord((uint16_t*)¤t_block->marker, 0x0000); + } + + NVS_FlashData = next_block; +} diff --git a/src/nvs.h b/src/nvs.h new file mode 100644 index 0000000..0ef9aab --- /dev/null +++ b/src/nvs.h @@ -0,0 +1,23 @@ +#pragma once + +#include + +#include "stm32f030x6.h" + +typedef struct +__attribute__((packed)) +{ + unsigned int animation_step_bottom; + unsigned int animation_step_top; +} NVS_Data_t; + +extern NVS_Data_t *const NVS_Data; + +// Returns true if the data was successfully loaded from flash and false if the +// defaults were restored instead +bool NVS_Load(void); + +// Stores the current contents of NVS_Data to flash +void NVS_Save(void); + + From 5e4b80343fe45194b8112420ee20fcc6b0242758 Mon Sep 17 00:00:00 2001 From: fruchti Date: Sat, 19 Sep 2020 19:57:34 +0200 Subject: [PATCH 16/31] Fix warnings --- build-number.txt | 2 +- src/nvs.c | 18 +++++++++--------- src/nvs.h | 2 +- 3 files changed, 11 insertions(+), 11 deletions(-) diff --git a/build-number.txt b/build-number.txt index e5a135a..e9b7520 100644 --- a/build-number.txt +++ b/build-number.txt @@ -1 +1 @@ -445 +447 diff --git a/src/nvs.c b/src/nvs.c index 1cb4466..b1f9003 100644 --- a/src/nvs.c +++ b/src/nvs.c @@ -11,7 +11,7 @@ #define NVS_VALID_BLOCK_MARKER 0xab34 typedef struct -__attribute__((packed)) +__attribute__((packed, aligned(2))) { // Actual block data NVS_Data_t data; @@ -26,9 +26,9 @@ __attribute__((packed)) } NVS_Block_t; __attribute__((used, section(".nvstore"))) -volatile NVS_Block_t NVS_Area[NVS_BLOCK_COUNT]; +NVS_Block_t NVS_Area[NVS_BLOCK_COUNT]; -volatile NVS_Block_t *NVS_FlashData; +NVS_Block_t *NVS_FlashData; __attribute__((used)) NVS_Block_t NVS_RAMData; @@ -37,7 +37,7 @@ NVS_Data_t *const NVS_Data = &NVS_RAMData.data; static uint32_t NVS_CalculateCRC(NVS_Data_t *data) { CRC->CR = CRC_CR_RESET; - for(int i = 0; i < sizeof(NVS_Data_t); i++) + for(unsigned int i = 0; i < sizeof(NVS_Data_t); i++) { CRC->DR = ((uint8_t*)(data))[i]; } @@ -47,7 +47,7 @@ static uint32_t NVS_CalculateCRC(NVS_Data_t *data) static void NVS_ProgramHalfWord(uint16_t *dest, uint16_t value) { FLASH->CR |= FLASH_CR_PG; - *(volatile uint16_t*)dest = value; + *(uint16_t*)dest = value; while(FLASH->SR & FLASH_SR_BSY); if(*dest != value) { @@ -91,7 +91,7 @@ static void NVS_EraseArea(void) static bool NVS_BlockEmpty(NVS_Block_t *block) { - for(int i = 0; i < sizeof(NVS_Block_t) / 2; i++) + for(unsigned int i = 0; i < sizeof(NVS_Block_t) / 2; i++) { if(*((uint16_t*)block + i) != 0xffff) { @@ -111,10 +111,10 @@ bool NVS_Load(void) { RCC->AHBENR |= RCC_AHBENR_CRCEN; - volatile NVS_Block_t *block = NULL; + NVS_Block_t *block = NULL; // Find valid block - for(int i = 0; i < NVS_BLOCK_COUNT; i++) + for(unsigned int i = 0; i < NVS_BLOCK_COUNT; i++) { block = &NVS_Area[i]; if(block->marker == NVS_VALID_BLOCK_MARKER) @@ -168,7 +168,7 @@ void NVS_Save(void) NVS_RAMData.marker = NVS_VALID_BLOCK_MARKER; // The block length is guaranteed to be divisible by 2 - for(int i = 0; i < sizeof(NVS_Block_t) / 2; i++) + for(unsigned int i = 0; i < sizeof(NVS_Block_t) / 2; i++) { NVS_ProgramHalfWord((uint16_t*)next_block + i, *((uint16_t*)&NVS_RAMData + i)); diff --git a/src/nvs.h b/src/nvs.h index 0ef9aab..47ba48a 100644 --- a/src/nvs.h +++ b/src/nvs.h @@ -5,7 +5,7 @@ #include "stm32f030x6.h" typedef struct -__attribute__((packed)) +__attribute__((packed, aligned(2))) { unsigned int animation_step_bottom; unsigned int animation_step_top; From 3fd2881ba22c73d4914262049abf66036d7bac47 Mon Sep 17 00:00:00 2001 From: fruchti Date: Sun, 20 Sep 2020 17:29:15 +0200 Subject: [PATCH 17/31] Fix NVS erase by properly resetting FLASH_CR --- build-number.txt | 2 +- src/nvs.c | 9 +++++---- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/build-number.txt b/build-number.txt index e9b7520..515f19a 100644 --- a/build-number.txt +++ b/build-number.txt @@ -1 +1 @@ -447 +454 diff --git a/src/nvs.c b/src/nvs.c index b1f9003..8f7c0b8 100644 --- a/src/nvs.c +++ b/src/nvs.c @@ -46,7 +46,7 @@ static uint32_t NVS_CalculateCRC(NVS_Data_t *data) static void NVS_ProgramHalfWord(uint16_t *dest, uint16_t value) { - FLASH->CR |= FLASH_CR_PG; + FLASH->CR = FLASH_CR_PG; *(uint16_t*)dest = value; while(FLASH->SR & FLASH_SR_BSY); if(*dest != value) @@ -54,6 +54,7 @@ static void NVS_ProgramHalfWord(uint16_t *dest, uint16_t value) // Write failed __asm__("bkpt"); } + FLASH->CR = 0x00000000; } static void NVS_UnlockFlash(void) @@ -68,10 +69,10 @@ static void NVS_UnlockFlash(void) static void NVS_EraseArea(void) { - for(int i = 0; i < NVS_AREA_SIZE; i += 1024) + for(unsigned int i = 0; i < NVS_AREA_SIZE; i += 1024) { while(FLASH->SR & FLASH_SR_BSY); - FLASH->CR |= FLASH_CR_PER; + FLASH->CR = FLASH_CR_PER; FLASH->AR = (uint32_t)NVS_Area + i; FLASH->CR |= FLASH_CR_STRT; while(FLASH->SR & FLASH_SR_BSY); @@ -85,7 +86,7 @@ static void NVS_EraseArea(void) // Erase failed __asm__("bkpt"); } - FLASH->CR &= ~FLASH_CR_PER; + FLASH->CR = 0x00000000; } } From 10a597336185c00ccc53c6670967d361614ab40d Mon Sep 17 00:00:00 2001 From: fruchti Date: Sun, 20 Sep 2020 17:36:58 +0200 Subject: [PATCH 18/31] Load NVS data on power-on --- build-number.txt | 2 +- src/main.c | 1 + src/main.h | 1 + 3 files changed, 3 insertions(+), 1 deletion(-) diff --git a/build-number.txt b/build-number.txt index 515f19a..4930863 100644 --- a/build-number.txt +++ b/build-number.txt @@ -1 +1 @@ -454 +455 diff --git a/src/main.c b/src/main.c index 1f3e7d9..b7f524a 100644 --- a/src/main.c +++ b/src/main.c @@ -13,6 +13,7 @@ int main(void) LightSensor_Poll(); } + NVS_Load(); LED_Init(); Animation_Init(); diff --git a/src/main.h b/src/main.h index 60ab8f9..48e0d25 100644 --- a/src/main.h +++ b/src/main.h @@ -8,5 +8,6 @@ #include "led.h" #include "light_sensor.h" #include "animation.h" +#include "nvs.h" int main(void); From a3799b02a03e427830cfe6ac5ea2de7714537461 Mon Sep 17 00:00:00 2001 From: fruchti Date: Sun, 20 Sep 2020 17:40:13 +0200 Subject: [PATCH 19/31] Save to NVS on suspend and wake-up --- src/main.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/main.c b/src/main.c index b7f524a..d41caa5 100644 --- a/src/main.c +++ b/src/main.c @@ -26,12 +26,14 @@ int main(void) if(LightSensor_RelativeBrightness == 0 && !powered_down) { LED_Suspend(); + NVS_Save(); powered_down = true; } if(powered_down && LightSensor_RelativeBrightness > 0) { - LED_WakeUp(); powered_down = false; + NVS_Save(); + LED_WakeUp(); } } From 8ac36022e0b6d5d5603c806d332ee31a2c147723 Mon Sep 17 00:00:00 2001 From: fruchti Date: Sun, 20 Sep 2020 18:00:35 +0200 Subject: [PATCH 20/31] Also save to NVS from time to time --- build-number.txt | 2 +- src/animation.c | 8 ++++++++ src/animation.h | 4 ++++ src/main.c | 4 ++-- src/nvs.c | 6 +++++- src/nvs.h | 5 +++-- 6 files changed, 23 insertions(+), 6 deletions(-) diff --git a/build-number.txt b/build-number.txt index 4930863..de2a00c 100644 --- a/build-number.txt +++ b/build-number.txt @@ -1 +1 @@ -455 +457 diff --git a/src/animation.c b/src/animation.c index 6c50ff3..72dbb8b 100644 --- a/src/animation.c +++ b/src/animation.c @@ -90,6 +90,14 @@ void Animation_Poll(void) NVS_Data->animation_step_top, LightSensor_RelativeBrightness); LED_Commit(); + + static unsigned int store_counter = 0; + store_counter++; + if(store_counter >= ANIMATION_NVS_STORE_INTERVAL * ANIMATION_REFRESH_RATE) + { + store_counter = 0; + NVS_Save(false); + } } void TIM17_IRQHandler(void) diff --git a/src/animation.h b/src/animation.h index 144291e..04e2a73 100644 --- a/src/animation.h +++ b/src/animation.h @@ -15,6 +15,10 @@ #define ANIMATION_CYCLE_TIME_TOP \ (8 * 24 * 60 * 60) +// Interval for saving the current animation step to NVS (in seconds) +#define ANIMATION_NVS_STORE_INTERVAL \ + (60 * 60) + extern const unsigned int Animation_LEDOrder[LED_COUNT]; void Animation_Init(void); diff --git a/src/main.c b/src/main.c index d41caa5..d921b37 100644 --- a/src/main.c +++ b/src/main.c @@ -26,13 +26,13 @@ int main(void) if(LightSensor_RelativeBrightness == 0 && !powered_down) { LED_Suspend(); - NVS_Save(); + NVS_Save(true); powered_down = true; } if(powered_down && LightSensor_RelativeBrightness > 0) { powered_down = false; - NVS_Save(); + NVS_Save(true); LED_WakeUp(); } } diff --git a/src/nvs.c b/src/nvs.c index 8f7c0b8..ef19b0e 100644 --- a/src/nvs.c +++ b/src/nvs.c @@ -148,7 +148,7 @@ bool NVS_Load(void) } } -void NVS_Save(void) +void NVS_Save(bool erase_if_needed) { NVS_UnlockFlash(); @@ -160,6 +160,10 @@ void NVS_Save(void) if(current_block == NULL || next_block > NVS_Area + NVS_BLOCK_COUNT || !NVS_BlockEmpty(next_block)) { + if(!erase_if_needed) + { + return; + } NVS_EraseArea(); next_block = &NVS_Area[0]; current_block = NULL; diff --git a/src/nvs.h b/src/nvs.h index 47ba48a..ad03133 100644 --- a/src/nvs.h +++ b/src/nvs.h @@ -17,7 +17,8 @@ extern NVS_Data_t *const NVS_Data; // defaults were restored instead bool NVS_Load(void); -// Stores the current contents of NVS_Data to flash -void NVS_Save(void); +// Stores the current contents of NVS_Data to flash. Pass `false` as a parameter +// to skip saving unless it can be done without a flash page erase. +void NVS_Save(bool erase_if_needed); From ea2049efb2b9613408d7d1439086aae0e271b435 Mon Sep 17 00:00:00 2001 From: fruchti Date: Sun, 20 Sep 2020 18:17:37 +0200 Subject: [PATCH 21/31] Make NVS saving more resilient --- build-number.txt | 2 +- src/nvs.c | 36 ++++++++++++++++++++++++++---------- src/nvs.h | 8 +++++--- 3 files changed, 32 insertions(+), 14 deletions(-) diff --git a/build-number.txt b/build-number.txt index de2a00c..658bd78 100644 --- a/build-number.txt +++ b/build-number.txt @@ -1 +1 @@ -457 +459 diff --git a/src/nvs.c b/src/nvs.c index ef19b0e..a28f6a1 100644 --- a/src/nvs.c +++ b/src/nvs.c @@ -44,7 +44,7 @@ static uint32_t NVS_CalculateCRC(NVS_Data_t *data) return CRC->DR; } -static void NVS_ProgramHalfWord(uint16_t *dest, uint16_t value) +static bool NVS_ProgramHalfWord(uint16_t *dest, uint16_t value) { FLASH->CR = FLASH_CR_PG; *(uint16_t*)dest = value; @@ -52,9 +52,11 @@ static void NVS_ProgramHalfWord(uint16_t *dest, uint16_t value) if(*dest != value) { // Write failed - __asm__("bkpt"); + FLASH->CR = 0x00000000; + return false; } FLASH->CR = 0x00000000; + return true; } static void NVS_UnlockFlash(void) @@ -67,7 +69,7 @@ static void NVS_UnlockFlash(void) } } -static void NVS_EraseArea(void) +static bool NVS_EraseArea(void) { for(unsigned int i = 0; i < NVS_AREA_SIZE; i += 1024) { @@ -84,9 +86,11 @@ static void NVS_EraseArea(void) else { // Erase failed - __asm__("bkpt"); + FLASH->CR = 0x00000000; + return false; } FLASH->CR = 0x00000000; + return true; } } @@ -148,7 +152,7 @@ bool NVS_Load(void) } } -void NVS_Save(bool erase_if_needed) +bool NVS_Save(bool erase_if_needed) { NVS_UnlockFlash(); @@ -162,9 +166,13 @@ void NVS_Save(bool erase_if_needed) { if(!erase_if_needed) { - return; + return false; + } + + if(!NVS_EraseArea()) + { + return false; } - NVS_EraseArea(); next_block = &NVS_Area[0]; current_block = NULL; } @@ -175,14 +183,22 @@ void NVS_Save(bool erase_if_needed) // The block length is guaranteed to be divisible by 2 for(unsigned int i = 0; i < sizeof(NVS_Block_t) / 2; i++) { - NVS_ProgramHalfWord((uint16_t*)next_block + i, - *((uint16_t*)&NVS_RAMData + i)); + if(!NVS_ProgramHalfWord((uint16_t*)next_block + i, + *((uint16_t*)&NVS_RAMData + i))) + { + return false; + } } if(current_block != NULL) { - NVS_ProgramHalfWord((uint16_t*)¤t_block->marker, 0x0000); + if(!NVS_ProgramHalfWord((uint16_t*)¤t_block->marker, 0x0000)) + { + return false; + } } NVS_FlashData = next_block; + + return true; } diff --git a/src/nvs.h b/src/nvs.h index ad03133..a26f8e6 100644 --- a/src/nvs.h +++ b/src/nvs.h @@ -17,8 +17,10 @@ extern NVS_Data_t *const NVS_Data; // defaults were restored instead bool NVS_Load(void); -// Stores the current contents of NVS_Data to flash. Pass `false` as a parameter -// to skip saving unless it can be done without a flash page erase. -void NVS_Save(bool erase_if_needed); +// Stores the current contents of NVS_Data to flash. Pass false as a parameter +// to skip saving unless it can be done without a flash page erase. Returns true +// if the operation succeeds and false if there is was an error or erasing was +// disallowed but would have been necessary. +bool NVS_Save(bool erase_if_needed); From 9f230125d8ecbacc30c4b4e0593a1151672715f2 Mon Sep 17 00:00:00 2001 From: fruchti Date: Sun, 20 Sep 2020 18:59:59 +0200 Subject: [PATCH 22/31] Fix NVS erase return typo --- src/nvs.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/nvs.c b/src/nvs.c index a28f6a1..7adcd73 100644 --- a/src/nvs.c +++ b/src/nvs.c @@ -90,8 +90,8 @@ static bool NVS_EraseArea(void) return false; } FLASH->CR = 0x00000000; - return true; } + return true; } static bool NVS_BlockEmpty(NVS_Block_t *block) From e13d522f4ef79a763d0c63a398bc64291a587e0b Mon Sep 17 00:00:00 2001 From: fruchti Date: Sun, 20 Sep 2020 19:00:55 +0200 Subject: [PATCH 23/31] Remove light sensor auto-normalisation --- build-number.txt | 2 +- src/light_sensor.c | 46 +++++++++------------------------------------- src/light_sensor.h | 12 ++++-------- 3 files changed, 14 insertions(+), 46 deletions(-) diff --git a/build-number.txt b/build-number.txt index 658bd78..b283aee 100644 --- a/build-number.txt +++ b/build-number.txt @@ -1 +1 @@ -459 +482 diff --git a/src/light_sensor.c b/src/light_sensor.c index 3cc495b..ebdfb95 100644 --- a/src/light_sensor.c +++ b/src/light_sensor.c @@ -4,11 +4,7 @@ volatile unsigned int LightSensor_Measurement; volatile bool LightSensor_NewMeasurement = false; // Rolling average of the brightness measurement -unsigned int LightSensor_AbsoluteBrightness = LIGHTSENSOR_MAX / 2; - -// Maximum and minimum encountered so far -unsigned int LightSensor_MinimumBrightness = LIGHTSENSOR_MAX; -unsigned int LightSensor_MaximumBrightness = 0; +unsigned int LightSensor_AbsoluteBrightness = 0; int LightSensor_RelativeBrightness; @@ -68,29 +64,16 @@ void LightSensor_Poll(void) LightSensor_NewMeasurement = false; unsigned int brightness = ((1 << 31) / (measurement + 1)) >> (31 - 16); - // LightSensor_AbsoluteBrightness = LIGHTSENSOR_LAMBDA * LightSensor_AbsoluteBrightness - // + (1.0f - LIGHTSENSOR_LAMBDA) * brightness; - LightSensor_AbsoluteBrightness -= LightSensor_AbsoluteBrightness >> LIGHTSENSOR_LAMBDA_BITS; - LightSensor_AbsoluteBrightness += brightness >> LIGHTSENSOR_LAMBDA_BITS; + LightSensor_AbsoluteBrightness -= LightSensor_AbsoluteBrightness + >> LIGHTSENSOR_LAMBDA_BITS; + LightSensor_AbsoluteBrightness += brightness + >> LIGHTSENSOR_LAMBDA_BITS; - if(brightness < LightSensor_MinimumBrightness) - { - LightSensor_MinimumBrightness = brightness; - } - if(brightness > LightSensor_MaximumBrightness) - { - LightSensor_MaximumBrightness = brightness; - } + LightSensor_RelativeBrightness = + ((int)LightSensor_AbsoluteBrightness - LIGHTSENSOR_LOW_BOUND) + * LIGHTSENSOR_MAX + / (LIGHTSENSOR_HIGH_BOUND - LIGHTSENSOR_LOW_BOUND); - // Scale and saturate to get relative brightness value - int range = LightSensor_MaximumBrightness - - LightSensor_MinimumBrightness; - int low = LightSensor_MinimumBrightness - + LIGHTSENSOR_LOW_BOUND * range / LIGHTSENSOR_MAX; - int high = LightSensor_MinimumBrightness - + LIGHTSENSOR_HIGH_BOUND * range / LIGHTSENSOR_MAX; - LightSensor_RelativeBrightness = ((int)LightSensor_AbsoluteBrightness - low) - * LIGHTSENSOR_MAX / (high - low); if(LightSensor_RelativeBrightness < 0) { LightSensor_RelativeBrightness = 0; @@ -99,17 +82,6 @@ void LightSensor_Poll(void) { LightSensor_RelativeBrightness = LIGHTSENSOR_MAX; } - - // Slowly reset limit values - static int decay_counter = 0; - decay_counter++; - if(decay_counter == LIGHTSENSOR_LIMIT_RESET_TIME * 1000 - / LIGHTSENSOR_INTERVAL / LIGHTSENSOR_MAX) - { - decay_counter = 0; - LightSensor_MaximumBrightness -= 1; - LightSensor_MinimumBrightness += 1; - } } } diff --git a/src/light_sensor.h b/src/light_sensor.h index 2331eae..fceb452 100644 --- a/src/light_sensor.h +++ b/src/light_sensor.h @@ -15,14 +15,10 @@ // 'Forgetting factor' of the rolling brightness average #define LIGHTSENSOR_LAMBDA_BITS 2 -// Time until minimum and maximum value are completely reset (in seconds) -#define LIGHTSENSOR_LIMIT_RESET_TIME \ - (48 * 60 * 60) - -// Bounds for converting absolute to relative brightness: Consider everything -// near the minimum or maximum 0.0 or 1.0, respectively -#define LIGHTSENSOR_LOW_BOUND ((unsigned int)(0.013 * LIGHTSENSOR_MAX)) -#define LIGHTSENSOR_HIGH_BOUND ((unsigned int)(1.0 * LIGHTSENSOR_MAX)) +// Bounds for converting absolute to relative brightness (empirically +// determined) +#define LIGHTSENSOR_LOW_BOUND ((int)(0.003 * LIGHTSENSOR_MAX)) +#define LIGHTSENSOR_HIGH_BOUND ((int)(0.65 * LIGHTSENSOR_MAX)) extern int LightSensor_RelativeBrightness; From ad433e2452268fa8a093ba4a1becd657df8ff384 Mon Sep 17 00:00:00 2001 From: fruchti Date: Mon, 21 Sep 2020 00:31:44 +0200 Subject: [PATCH 24/31] Move gamma correction from software to LUT --- build-number.txt | 2 +- src/animation_lut.c | 511 ++++++++++++++++++++++---------------------- src/animation_lut.h | 2 +- src/led.c | 10 +- src/led.h | 8 +- 5 files changed, 266 insertions(+), 267 deletions(-) diff --git a/build-number.txt b/build-number.txt index b283aee..055b667 100644 --- a/build-number.txt +++ b/build-number.txt @@ -1 +1 @@ -482 +507 diff --git a/src/animation_lut.c b/src/animation_lut.c index 8ef4e12..04132a5 100644 --- a/src/animation_lut.c +++ b/src/animation_lut.c @@ -2,259 +2,260 @@ const LED_Colour_t Animation_ColourLUT[] = { - { .r = 255, .g = 51, .b = 51 }, - { .r = 255, .g = 51, .b = 56 }, - { .r = 255, .g = 51, .b = 61 }, - { .r = 255, .g = 51, .b = 65 }, - { .r = 255, .g = 51, .b = 70 }, - { .r = 255, .g = 51, .b = 75 }, - { .r = 255, .g = 51, .b = 80 }, - { .r = 255, .g = 51, .b = 85 }, - { .r = 255, .g = 51, .b = 89 }, - { .r = 255, .g = 51, .b = 94 }, - { .r = 255, .g = 51, .b = 99 }, - { .r = 255, .g = 51, .b = 104 }, - { .r = 255, .g = 51, .b = 109 }, - { .r = 255, .g = 51, .b = 113 }, - { .r = 255, .g = 51, .b = 118 }, - { .r = 255, .g = 51, .b = 123 }, - { .r = 255, .g = 51, .b = 128 }, - { .r = 255, .g = 51, .b = 133 }, - { .r = 255, .g = 51, .b = 137 }, - { .r = 255, .g = 51, .b = 142 }, - { .r = 255, .g = 51, .b = 147 }, - { .r = 255, .g = 51, .b = 152 }, - { .r = 255, .g = 51, .b = 157 }, - { .r = 255, .g = 51, .b = 161 }, - { .r = 255, .g = 51, .b = 166 }, - { .r = 255, .g = 51, .b = 171 }, - { .r = 255, .g = 51, .b = 176 }, - { .r = 255, .g = 51, .b = 181 }, - { .r = 255, .g = 51, .b = 185 }, - { .r = 255, .g = 51, .b = 190 }, - { .r = 255, .g = 51, .b = 195 }, - { .r = 255, .g = 51, .b = 200 }, - { .r = 255, .g = 51, .b = 205 }, - { .r = 255, .g = 51, .b = 209 }, - { .r = 255, .g = 51, .b = 214 }, - { .r = 255, .g = 51, .b = 219 }, - { .r = 255, .g = 51, .b = 224 }, - { .r = 255, .g = 51, .b = 229 }, - { .r = 255, .g = 51, .b = 233 }, - { .r = 255, .g = 51, .b = 238 }, - { .r = 255, .g = 51, .b = 243 }, - { .r = 255, .g = 51, .b = 248 }, - { .r = 255, .g = 51, .b = 253 }, - { .r = 253, .g = 51, .b = 255 }, - { .r = 248, .g = 51, .b = 255 }, - { .r = 243, .g = 51, .b = 255 }, - { .r = 238, .g = 51, .b = 255 }, - { .r = 233, .g = 51, .b = 255 }, - { .r = 229, .g = 51, .b = 255 }, - { .r = 224, .g = 51, .b = 255 }, - { .r = 219, .g = 51, .b = 255 }, - { .r = 214, .g = 51, .b = 255 }, - { .r = 209, .g = 51, .b = 255 }, - { .r = 205, .g = 51, .b = 255 }, - { .r = 200, .g = 51, .b = 255 }, - { .r = 195, .g = 51, .b = 255 }, - { .r = 190, .g = 51, .b = 255 }, - { .r = 185, .g = 51, .b = 255 }, - { .r = 181, .g = 51, .b = 255 }, - { .r = 176, .g = 51, .b = 255 }, - { .r = 171, .g = 51, .b = 255 }, - { .r = 166, .g = 51, .b = 255 }, - { .r = 161, .g = 51, .b = 255 }, - { .r = 157, .g = 51, .b = 255 }, - { .r = 152, .g = 51, .b = 255 }, - { .r = 147, .g = 51, .b = 255 }, - { .r = 142, .g = 51, .b = 255 }, - { .r = 137, .g = 51, .b = 255 }, - { .r = 133, .g = 51, .b = 255 }, - { .r = 128, .g = 51, .b = 255 }, - { .r = 123, .g = 51, .b = 255 }, - { .r = 118, .g = 51, .b = 255 }, - { .r = 113, .g = 51, .b = 255 }, - { .r = 109, .g = 51, .b = 255 }, - { .r = 104, .g = 51, .b = 255 }, - { .r = 99, .g = 51, .b = 255 }, - { .r = 94, .g = 51, .b = 255 }, - { .r = 89, .g = 51, .b = 255 }, - { .r = 85, .g = 51, .b = 255 }, - { .r = 80, .g = 51, .b = 255 }, - { .r = 75, .g = 51, .b = 255 }, - { .r = 70, .g = 51, .b = 255 }, - { .r = 65, .g = 51, .b = 255 }, - { .r = 61, .g = 51, .b = 255 }, - { .r = 56, .g = 51, .b = 255 }, - { .r = 51, .g = 51, .b = 255 }, - { .r = 51, .g = 56, .b = 255 }, - { .r = 51, .g = 61, .b = 255 }, - { .r = 51, .g = 65, .b = 255 }, - { .r = 51, .g = 70, .b = 255 }, - { .r = 51, .g = 75, .b = 255 }, - { .r = 51, .g = 80, .b = 255 }, - { .r = 51, .g = 85, .b = 255 }, - { .r = 51, .g = 89, .b = 255 }, - { .r = 51, .g = 94, .b = 255 }, - { .r = 51, .g = 99, .b = 255 }, - { .r = 51, .g = 104, .b = 255 }, - { .r = 51, .g = 109, .b = 255 }, - { .r = 51, .g = 113, .b = 255 }, - { .r = 51, .g = 118, .b = 255 }, - { .r = 51, .g = 123, .b = 255 }, - { .r = 51, .g = 128, .b = 255 }, - { .r = 51, .g = 133, .b = 255 }, - { .r = 51, .g = 137, .b = 255 }, - { .r = 51, .g = 142, .b = 255 }, - { .r = 51, .g = 147, .b = 255 }, - { .r = 51, .g = 152, .b = 255 }, - { .r = 51, .g = 157, .b = 255 }, - { .r = 51, .g = 161, .b = 255 }, - { .r = 51, .g = 166, .b = 255 }, - { .r = 51, .g = 171, .b = 255 }, - { .r = 51, .g = 176, .b = 255 }, - { .r = 51, .g = 181, .b = 255 }, - { .r = 51, .g = 185, .b = 255 }, - { .r = 51, .g = 190, .b = 255 }, - { .r = 51, .g = 195, .b = 255 }, - { .r = 51, .g = 200, .b = 255 }, - { .r = 51, .g = 205, .b = 255 }, - { .r = 51, .g = 209, .b = 255 }, - { .r = 51, .g = 214, .b = 255 }, - { .r = 51, .g = 219, .b = 255 }, - { .r = 51, .g = 224, .b = 255 }, - { .r = 51, .g = 229, .b = 255 }, - { .r = 51, .g = 233, .b = 255 }, - { .r = 51, .g = 238, .b = 255 }, - { .r = 51, .g = 243, .b = 255 }, - { .r = 51, .g = 248, .b = 255 }, - { .r = 51, .g = 253, .b = 255 }, - { .r = 51, .g = 255, .b = 253 }, - { .r = 51, .g = 255, .b = 248 }, - { .r = 51, .g = 255, .b = 243 }, - { .r = 51, .g = 255, .b = 238 }, - { .r = 51, .g = 255, .b = 233 }, - { .r = 51, .g = 255, .b = 229 }, - { .r = 51, .g = 255, .b = 224 }, - { .r = 51, .g = 255, .b = 219 }, - { .r = 51, .g = 255, .b = 214 }, - { .r = 51, .g = 255, .b = 209 }, - { .r = 51, .g = 255, .b = 205 }, - { .r = 51, .g = 255, .b = 200 }, - { .r = 51, .g = 255, .b = 195 }, - { .r = 51, .g = 255, .b = 190 }, - { .r = 51, .g = 255, .b = 185 }, - { .r = 51, .g = 255, .b = 181 }, - { .r = 51, .g = 255, .b = 176 }, - { .r = 51, .g = 255, .b = 171 }, - { .r = 51, .g = 255, .b = 166 }, - { .r = 51, .g = 255, .b = 161 }, - { .r = 51, .g = 255, .b = 157 }, - { .r = 51, .g = 255, .b = 152 }, - { .r = 51, .g = 255, .b = 147 }, - { .r = 51, .g = 255, .b = 142 }, - { .r = 51, .g = 255, .b = 137 }, - { .r = 51, .g = 255, .b = 133 }, - { .r = 51, .g = 255, .b = 128 }, - { .r = 51, .g = 255, .b = 123 }, - { .r = 51, .g = 255, .b = 118 }, - { .r = 51, .g = 255, .b = 113 }, - { .r = 51, .g = 255, .b = 109 }, - { .r = 51, .g = 255, .b = 104 }, - { .r = 51, .g = 255, .b = 99 }, - { .r = 51, .g = 255, .b = 94 }, - { .r = 51, .g = 255, .b = 89 }, - { .r = 51, .g = 255, .b = 85 }, - { .r = 51, .g = 255, .b = 80 }, - { .r = 51, .g = 255, .b = 75 }, - { .r = 51, .g = 255, .b = 70 }, - { .r = 51, .g = 255, .b = 65 }, - { .r = 51, .g = 255, .b = 61 }, - { .r = 51, .g = 255, .b = 56 }, - { .r = 51, .g = 255, .b = 51 }, - { .r = 56, .g = 255, .b = 51 }, - { .r = 61, .g = 255, .b = 51 }, - { .r = 65, .g = 255, .b = 51 }, - { .r = 70, .g = 255, .b = 51 }, - { .r = 75, .g = 255, .b = 51 }, - { .r = 80, .g = 255, .b = 51 }, - { .r = 85, .g = 255, .b = 51 }, - { .r = 89, .g = 255, .b = 51 }, - { .r = 94, .g = 255, .b = 51 }, - { .r = 99, .g = 255, .b = 51 }, - { .r = 104, .g = 255, .b = 51 }, - { .r = 109, .g = 255, .b = 51 }, - { .r = 113, .g = 255, .b = 51 }, - { .r = 118, .g = 255, .b = 51 }, - { .r = 123, .g = 255, .b = 51 }, - { .r = 128, .g = 255, .b = 51 }, - { .r = 133, .g = 255, .b = 51 }, - { .r = 137, .g = 255, .b = 51 }, - { .r = 142, .g = 255, .b = 51 }, - { .r = 147, .g = 255, .b = 51 }, - { .r = 152, .g = 255, .b = 51 }, - { .r = 157, .g = 255, .b = 51 }, - { .r = 161, .g = 255, .b = 51 }, - { .r = 166, .g = 255, .b = 51 }, - { .r = 171, .g = 255, .b = 51 }, - { .r = 176, .g = 255, .b = 51 }, - { .r = 181, .g = 255, .b = 51 }, - { .r = 185, .g = 255, .b = 51 }, - { .r = 190, .g = 255, .b = 51 }, - { .r = 195, .g = 255, .b = 51 }, - { .r = 200, .g = 255, .b = 51 }, - { .r = 205, .g = 255, .b = 51 }, - { .r = 209, .g = 255, .b = 51 }, - { .r = 214, .g = 255, .b = 51 }, - { .r = 219, .g = 255, .b = 51 }, - { .r = 224, .g = 255, .b = 51 }, - { .r = 229, .g = 255, .b = 51 }, - { .r = 233, .g = 255, .b = 51 }, - { .r = 238, .g = 255, .b = 51 }, - { .r = 243, .g = 255, .b = 51 }, - { .r = 248, .g = 255, .b = 51 }, - { .r = 253, .g = 255, .b = 51 }, - { .r = 255, .g = 253, .b = 51 }, - { .r = 255, .g = 248, .b = 51 }, - { .r = 255, .g = 243, .b = 51 }, - { .r = 255, .g = 238, .b = 51 }, - { .r = 255, .g = 233, .b = 51 }, - { .r = 255, .g = 229, .b = 51 }, - { .r = 255, .g = 224, .b = 51 }, - { .r = 255, .g = 219, .b = 51 }, - { .r = 255, .g = 214, .b = 51 }, - { .r = 255, .g = 209, .b = 51 }, - { .r = 255, .g = 205, .b = 51 }, - { .r = 255, .g = 200, .b = 51 }, - { .r = 255, .g = 195, .b = 51 }, - { .r = 255, .g = 190, .b = 51 }, - { .r = 255, .g = 185, .b = 51 }, - { .r = 255, .g = 181, .b = 51 }, - { .r = 255, .g = 176, .b = 51 }, - { .r = 255, .g = 171, .b = 51 }, - { .r = 255, .g = 166, .b = 51 }, - { .r = 255, .g = 161, .b = 51 }, - { .r = 255, .g = 157, .b = 51 }, - { .r = 255, .g = 152, .b = 51 }, - { .r = 255, .g = 147, .b = 51 }, - { .r = 255, .g = 142, .b = 51 }, - { .r = 255, .g = 137, .b = 51 }, - { .r = 255, .g = 133, .b = 51 }, - { .r = 255, .g = 128, .b = 51 }, - { .r = 255, .g = 123, .b = 51 }, - { .r = 255, .g = 118, .b = 51 }, - { .r = 255, .g = 113, .b = 51 }, - { .r = 255, .g = 109, .b = 51 }, - { .r = 255, .g = 104, .b = 51 }, - { .r = 255, .g = 99, .b = 51 }, - { .r = 255, .g = 94, .b = 51 }, - { .r = 255, .g = 89, .b = 51 }, - { .r = 255, .g = 85, .b = 51 }, - { .r = 255, .g = 80, .b = 51 }, - { .r = 255, .g = 75, .b = 51 }, - { .r = 255, .g = 70, .b = 51 }, - { .r = 255, .g = 65, .b = 51 }, - { .r = 255, .g = 61, .b = 51 }, - { .r = 255, .g = 56, .b = 51 }, + { .r = 4096, .g = 119, .b = 119 }, + { .r = 4096, .g = 145, .b = 119 }, + { .r = 4096, .g = 173, .b = 119 }, + { .r = 4096, .g = 205, .b = 119 }, + { .r = 4096, .g = 239, .b = 119 }, + { .r = 4096, .g = 277, .b = 119 }, + { .r = 4096, .g = 317, .b = 119 }, + { .r = 4096, .g = 360, .b = 119 }, + { .r = 4096, .g = 407, .b = 119 }, + { .r = 4096, .g = 456, .b = 119 }, + { .r = 4096, .g = 509, .b = 119 }, + { .r = 4096, .g = 565, .b = 119 }, + { .r = 4096, .g = 623, .b = 119 }, + { .r = 4096, .g = 686, .b = 119 }, + { .r = 4096, .g = 751, .b = 119 }, + { .r = 4096, .g = 820, .b = 119 }, + { .r = 4096, .g = 891, .b = 119 }, + { .r = 4096, .g = 967, .b = 119 }, + { .r = 4096, .g = 1045, .b = 119 }, + { .r = 4096, .g = 1127, .b = 119 }, + { .r = 4096, .g = 1212, .b = 119 }, + { .r = 4096, .g = 1301, .b = 119 }, + { .r = 4096, .g = 1393, .b = 119 }, + { .r = 4096, .g = 1489, .b = 119 }, + { .r = 4096, .g = 1588, .b = 119 }, + { .r = 4096, .g = 1690, .b = 119 }, + { .r = 4096, .g = 1796, .b = 119 }, + { .r = 4096, .g = 1906, .b = 119 }, + { .r = 4096, .g = 2019, .b = 119 }, + { .r = 4096, .g = 2135, .b = 119 }, + { .r = 4096, .g = 2256, .b = 119 }, + { .r = 4096, .g = 2380, .b = 119 }, + { .r = 4096, .g = 2507, .b = 119 }, + { .r = 4096, .g = 2638, .b = 119 }, + { .r = 4096, .g = 2773, .b = 119 }, + { .r = 4096, .g = 2911, .b = 119 }, + { .r = 4096, .g = 3053, .b = 119 }, + { .r = 4096, .g = 3199, .b = 119 }, + { .r = 4096, .g = 3349, .b = 119 }, + { .r = 4096, .g = 3502, .b = 119 }, + { .r = 4096, .g = 3659, .b = 119 }, + { .r = 4096, .g = 3820, .b = 119 }, + { .r = 4096, .g = 3984, .b = 119 }, + { .r = 4040, .g = 4096, .b = 119 }, + { .r = 3874, .g = 4096, .b = 119 }, + { .r = 3712, .g = 4096, .b = 119 }, + { .r = 3554, .g = 4096, .b = 119 }, + { .r = 3399, .g = 4096, .b = 119 }, + { .r = 3249, .g = 4096, .b = 119 }, + { .r = 3102, .g = 4096, .b = 119 }, + { .r = 2958, .g = 4096, .b = 119 }, + { .r = 2819, .g = 4096, .b = 119 }, + { .r = 2683, .g = 4096, .b = 119 }, + { .r = 2550, .g = 4096, .b = 119 }, + { .r = 2422, .g = 4096, .b = 119 }, + { .r = 2297, .g = 4096, .b = 119 }, + { .r = 2175, .g = 4096, .b = 119 }, + { .r = 2057, .g = 4096, .b = 119 }, + { .r = 1943, .g = 4096, .b = 119 }, + { .r = 1832, .g = 4096, .b = 119 }, + { .r = 1725, .g = 4096, .b = 119 }, + { .r = 1621, .g = 4096, .b = 119 }, + { .r = 1521, .g = 4096, .b = 119 }, + { .r = 1425, .g = 4096, .b = 119 }, + { .r = 1331, .g = 4096, .b = 119 }, + { .r = 1242, .g = 4096, .b = 119 }, + { .r = 1155, .g = 4096, .b = 119 }, + { .r = 1072, .g = 4096, .b = 119 }, + { .r = 992, .g = 4096, .b = 119 }, + { .r = 916, .g = 4096, .b = 119 }, + { .r = 843, .g = 4096, .b = 119 }, + { .r = 773, .g = 4096, .b = 119 }, + { .r = 707, .g = 4096, .b = 119 }, + { .r = 644, .g = 4096, .b = 119 }, + { .r = 584, .g = 4096, .b = 119 }, + { .r = 527, .g = 4096, .b = 119 }, + { .r = 473, .g = 4096, .b = 119 }, + { .r = 423, .g = 4096, .b = 119 }, + { .r = 375, .g = 4096, .b = 119 }, + { .r = 331, .g = 4096, .b = 119 }, + { .r = 290, .g = 4096, .b = 119 }, + { .r = 251, .g = 4096, .b = 119 }, + { .r = 216, .g = 4096, .b = 119 }, + { .r = 184, .g = 4096, .b = 119 }, + { .r = 154, .g = 4096, .b = 119 }, + { .r = 127, .g = 4096, .b = 119 }, + { .r = 119, .g = 4096, .b = 136 }, + { .r = 119, .g = 4096, .b = 163 }, + { .r = 119, .g = 4096, .b = 194 }, + { .r = 119, .g = 4096, .b = 227 }, + { .r = 119, .g = 4096, .b = 264 }, + { .r = 119, .g = 4096, .b = 303 }, + { .r = 119, .g = 4096, .b = 346 }, + { .r = 119, .g = 4096, .b = 391 }, + { .r = 119, .g = 4096, .b = 439 }, + { .r = 119, .g = 4096, .b = 491 }, + { .r = 119, .g = 4096, .b = 546 }, + { .r = 119, .g = 4096, .b = 603 }, + { .r = 119, .g = 4096, .b = 665 }, + { .r = 119, .g = 4096, .b = 729 }, + { .r = 119, .g = 4096, .b = 796 }, + { .r = 119, .g = 4096, .b = 867 }, + { .r = 119, .g = 4096, .b = 941 }, + { .r = 119, .g = 4096, .b = 1019 }, + { .r = 119, .g = 4096, .b = 1099 }, + { .r = 119, .g = 4096, .b = 1184 }, + { .r = 119, .g = 4096, .b = 1271 }, + { .r = 119, .g = 4096, .b = 1362 }, + { .r = 119, .g = 4096, .b = 1456 }, + { .r = 119, .g = 4096, .b = 1554 }, + { .r = 119, .g = 4096, .b = 1656 }, + { .r = 119, .g = 4096, .b = 1760 }, + { .r = 119, .g = 4096, .b = 1869 }, + { .r = 119, .g = 4096, .b = 1981 }, + { .r = 119, .g = 4096, .b = 2096 }, + { .r = 119, .g = 4096, .b = 2215 }, + { .r = 119, .g = 4096, .b = 2338 }, + { .r = 119, .g = 4096, .b = 2464 }, + { .r = 119, .g = 4096, .b = 2594 }, + { .r = 119, .g = 4096, .b = 2728 }, + { .r = 119, .g = 4096, .b = 2865 }, + { .r = 119, .g = 4096, .b = 3006 }, + { .r = 119, .g = 4096, .b = 3150 }, + { .r = 119, .g = 4096, .b = 3298 }, + { .r = 119, .g = 4096, .b = 3450 }, + { .r = 119, .g = 4096, .b = 3606 }, + { .r = 119, .g = 4096, .b = 3766 }, + { .r = 119, .g = 4096, .b = 3929 }, + { .r = 119, .g = 4096, .b = 4096 }, + { .r = 119, .g = 3929, .b = 4096 }, + { .r = 119, .g = 3766, .b = 4096 }, + { .r = 119, .g = 3606, .b = 4096 }, + { .r = 119, .g = 3450, .b = 4096 }, + { .r = 119, .g = 3298, .b = 4096 }, + { .r = 119, .g = 3150, .b = 4096 }, + { .r = 119, .g = 3006, .b = 4096 }, + { .r = 119, .g = 2865, .b = 4096 }, + { .r = 119, .g = 2728, .b = 4096 }, + { .r = 119, .g = 2594, .b = 4096 }, + { .r = 119, .g = 2464, .b = 4096 }, + { .r = 119, .g = 2338, .b = 4096 }, + { .r = 119, .g = 2215, .b = 4096 }, + { .r = 119, .g = 2096, .b = 4096 }, + { .r = 119, .g = 1981, .b = 4096 }, + { .r = 119, .g = 1869, .b = 4096 }, + { .r = 119, .g = 1760, .b = 4096 }, + { .r = 119, .g = 1656, .b = 4096 }, + { .r = 119, .g = 1554, .b = 4096 }, + { .r = 119, .g = 1456, .b = 4096 }, + { .r = 119, .g = 1362, .b = 4096 }, + { .r = 119, .g = 1271, .b = 4096 }, + { .r = 119, .g = 1184, .b = 4096 }, + { .r = 119, .g = 1099, .b = 4096 }, + { .r = 119, .g = 1019, .b = 4096 }, + { .r = 119, .g = 941, .b = 4096 }, + { .r = 119, .g = 867, .b = 4096 }, + { .r = 119, .g = 796, .b = 4096 }, + { .r = 119, .g = 729, .b = 4096 }, + { .r = 119, .g = 665, .b = 4096 }, + { .r = 119, .g = 603, .b = 4096 }, + { .r = 119, .g = 546, .b = 4096 }, + { .r = 119, .g = 491, .b = 4096 }, + { .r = 119, .g = 439, .b = 4096 }, + { .r = 119, .g = 391, .b = 4096 }, + { .r = 119, .g = 346, .b = 4096 }, + { .r = 119, .g = 303, .b = 4096 }, + { .r = 119, .g = 264, .b = 4096 }, + { .r = 119, .g = 227, .b = 4096 }, + { .r = 119, .g = 194, .b = 4096 }, + { .r = 119, .g = 163, .b = 4096 }, + { .r = 119, .g = 136, .b = 4096 }, + { .r = 127, .g = 119, .b = 4096 }, + { .r = 154, .g = 119, .b = 4096 }, + { .r = 184, .g = 119, .b = 4096 }, + { .r = 216, .g = 119, .b = 4096 }, + { .r = 251, .g = 119, .b = 4096 }, + { .r = 290, .g = 119, .b = 4096 }, + { .r = 331, .g = 119, .b = 4096 }, + { .r = 375, .g = 119, .b = 4096 }, + { .r = 423, .g = 119, .b = 4096 }, + { .r = 473, .g = 119, .b = 4096 }, + { .r = 527, .g = 119, .b = 4096 }, + { .r = 584, .g = 119, .b = 4096 }, + { .r = 644, .g = 119, .b = 4096 }, + { .r = 707, .g = 119, .b = 4096 }, + { .r = 773, .g = 119, .b = 4096 }, + { .r = 843, .g = 119, .b = 4096 }, + { .r = 916, .g = 119, .b = 4096 }, + { .r = 992, .g = 119, .b = 4096 }, + { .r = 1072, .g = 119, .b = 4096 }, + { .r = 1155, .g = 119, .b = 4096 }, + { .r = 1242, .g = 119, .b = 4096 }, + { .r = 1331, .g = 119, .b = 4096 }, + { .r = 1425, .g = 119, .b = 4096 }, + { .r = 1521, .g = 119, .b = 4096 }, + { .r = 1621, .g = 119, .b = 4096 }, + { .r = 1725, .g = 119, .b = 4096 }, + { .r = 1832, .g = 119, .b = 4096 }, + { .r = 1943, .g = 119, .b = 4096 }, + { .r = 2057, .g = 119, .b = 4096 }, + { .r = 2175, .g = 119, .b = 4096 }, + { .r = 2297, .g = 119, .b = 4096 }, + { .r = 2422, .g = 119, .b = 4096 }, + { .r = 2550, .g = 119, .b = 4096 }, + { .r = 2683, .g = 119, .b = 4096 }, + { .r = 2819, .g = 119, .b = 4096 }, + { .r = 2958, .g = 119, .b = 4096 }, + { .r = 3102, .g = 119, .b = 4096 }, + { .r = 3249, .g = 119, .b = 4096 }, + { .r = 3399, .g = 119, .b = 4096 }, + { .r = 3554, .g = 119, .b = 4096 }, + { .r = 3712, .g = 119, .b = 4096 }, + { .r = 3874, .g = 119, .b = 4096 }, + { .r = 4040, .g = 119, .b = 4096 }, + { .r = 4096, .g = 119, .b = 3984 }, + { .r = 4096, .g = 119, .b = 3820 }, + { .r = 4096, .g = 119, .b = 3659 }, + { .r = 4096, .g = 119, .b = 3502 }, + { .r = 4096, .g = 119, .b = 3349 }, + { .r = 4096, .g = 119, .b = 3199 }, + { .r = 4096, .g = 119, .b = 3053 }, + { .r = 4096, .g = 119, .b = 2911 }, + { .r = 4096, .g = 119, .b = 2773 }, + { .r = 4096, .g = 119, .b = 2638 }, + { .r = 4096, .g = 119, .b = 2507 }, + { .r = 4096, .g = 119, .b = 2380 }, + { .r = 4096, .g = 119, .b = 2256 }, + { .r = 4096, .g = 119, .b = 2135 }, + { .r = 4096, .g = 119, .b = 2019 }, + { .r = 4096, .g = 119, .b = 1906 }, + { .r = 4096, .g = 119, .b = 1796 }, + { .r = 4096, .g = 119, .b = 1690 }, + { .r = 4096, .g = 119, .b = 1588 }, + { .r = 4096, .g = 119, .b = 1489 }, + { .r = 4096, .g = 119, .b = 1393 }, + { .r = 4096, .g = 119, .b = 1301 }, + { .r = 4096, .g = 119, .b = 1212 }, + { .r = 4096, .g = 119, .b = 1127 }, + { .r = 4096, .g = 119, .b = 1045 }, + { .r = 4096, .g = 119, .b = 967 }, + { .r = 4096, .g = 119, .b = 891 }, + { .r = 4096, .g = 119, .b = 820 }, + { .r = 4096, .g = 119, .b = 751 }, + { .r = 4096, .g = 119, .b = 686 }, + { .r = 4096, .g = 119, .b = 623 }, + { .r = 4096, .g = 119, .b = 565 }, + { .r = 4096, .g = 119, .b = 509 }, + { .r = 4096, .g = 119, .b = 456 }, + { .r = 4096, .g = 119, .b = 407 }, + { .r = 4096, .g = 119, .b = 360 }, + { .r = 4096, .g = 119, .b = 317 }, + { .r = 4096, .g = 119, .b = 277 }, + { .r = 4096, .g = 119, .b = 239 }, + { .r = 4096, .g = 119, .b = 205 }, + { .r = 4096, .g = 119, .b = 173 }, + { .r = 4096, .g = 119, .b = 145 }, }; diff --git a/src/animation_lut.h b/src/animation_lut.h index 09dcff0..79bb8dc 100644 --- a/src/animation_lut.h +++ b/src/animation_lut.h @@ -2,4 +2,4 @@ #include "led.h" -extern const LED_Colour_t Animation_ColourLUT[255]; +extern const LED_Colour_t Animation_ColourLUT[256]; diff --git a/src/led.c b/src/led.c index b7dea1f..197699d 100644 --- a/src/led.c +++ b/src/led.c @@ -105,15 +105,13 @@ void LED_Commit(void) { for(int i = 0; i < LED_COLUMNS; i++) { - // Use pixel data as a raw byte buffer to get R, G, B in order - uint8_t colour_value = ((uint8_t*)LED_PixelData)[r * LED_COLUMNS + i]; - uint16_t gamma_corrected = (uint16_t)colour_value; - gamma_corrected *= gamma_corrected; - gamma_corrected >>= 16 - LED_BITS; + // Use pixel data as a raw word buffer to get R, G, B in order + uint16_t colour_value = + ((uint16_t*)LED_PixelData)[r * LED_COLUMNS + i]; for(int j = 0; j < LED_BITS; j++) { - if(gamma_corrected & (1 << j)) + if(colour_value & (1 << j)) { LED_BackBuffer[r * (LED_BITS + 1) + j] &= ~(1 << LED_Pins[i]); diff --git a/src/led.h b/src/led.h index 0d35897..083bc1d 100644 --- a/src/led.h +++ b/src/led.h @@ -14,10 +14,10 @@ typedef struct { - uint8_t r; - uint8_t g; - uint8_t b; -} __attribute__((packed)) LED_Colour_t; + uint16_t r; + uint16_t g; + uint16_t b; +} __attribute__((packed, aligned(2))) LED_Colour_t; // Pixel data, not displayed until LED_Commit() is called extern LED_Colour_t LED_PixelData[LED_COUNT]; From 6d1481fb95ab17d076c599f96b25b80e52fc696f Mon Sep 17 00:00:00 2001 From: fruchti Date: Mon, 21 Sep 2020 00:34:32 +0200 Subject: [PATCH 25/31] Tune light sensor parameters --- build-number.txt | 2 +- src/light_sensor.h | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/build-number.txt b/build-number.txt index 055b667..77afe23 100644 --- a/build-number.txt +++ b/build-number.txt @@ -1 +1 @@ -507 +509 diff --git a/src/light_sensor.h b/src/light_sensor.h index fceb452..da9f234 100644 --- a/src/light_sensor.h +++ b/src/light_sensor.h @@ -17,8 +17,8 @@ // Bounds for converting absolute to relative brightness (empirically // determined) -#define LIGHTSENSOR_LOW_BOUND ((int)(0.003 * LIGHTSENSOR_MAX)) -#define LIGHTSENSOR_HIGH_BOUND ((int)(0.65 * LIGHTSENSOR_MAX)) +#define LIGHTSENSOR_LOW_BOUND 5 +#define LIGHTSENSOR_HIGH_BOUND ((int)(0.85 * LIGHTSENSOR_MAX)) extern int LightSensor_RelativeBrightness; From 3a6c0a639170d8954484dc294dceacb434f60ee9 Mon Sep 17 00:00:00 2001 From: fruchti Date: Mon, 21 Sep 2020 10:36:50 +0200 Subject: [PATCH 26/31] Fix dimming overflow --- build-number.txt | 2 +- src/light_sensor.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/build-number.txt b/build-number.txt index 77afe23..31cf34b 100644 --- a/build-number.txt +++ b/build-number.txt @@ -1 +1 @@ -509 +513 diff --git a/src/light_sensor.h b/src/light_sensor.h index da9f234..cd56d74 100644 --- a/src/light_sensor.h +++ b/src/light_sensor.h @@ -10,7 +10,7 @@ // Resolution of the brightness output #define LIGHTSENSOR_BITS 12 -#define LIGHTSENSOR_MAX (1 << LIGHTSENSOR_BITS) +#define LIGHTSENSOR_MAX ((1 << LIGHTSENSOR_BITS) - 1) // 'Forgetting factor' of the rolling brightness average #define LIGHTSENSOR_LAMBDA_BITS 2 From f3edcd1c269789baa5fce51c462b34ac75809ac4 Mon Sep 17 00:00:00 2001 From: fruchti Date: Sun, 27 Sep 2020 23:53:08 +0200 Subject: [PATCH 27/31] Fix BCM comments --- build-number.txt | 2 +- src/led.c | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/build-number.txt b/build-number.txt index 31cf34b..3cda32f 100644 --- a/build-number.txt +++ b/build-number.txt @@ -1 +1 @@ -513 +515 diff --git a/src/led.c b/src/led.c index 197699d..f644614 100644 --- a/src/led.c +++ b/src/led.c @@ -144,7 +144,7 @@ static void LED_StartBCM(int row) TIM3->ARR = LED_BitLengths[0]; TIM3->DIER = TIM_DIER_UDE | TIM_DIER_CC1DE; - // DMA channel 3: Output data to port a on TIM3 update + // DMA channel 3: output data to port a on TIM3 update event DMA1_Channel3->CMAR = (uint32_t)&(LED_FrontBuffer[row * (LED_BITS + 1) + 4]); // One transfer for each bit plus one to set the outputs to zero again. // The first 4 are sent out with assembly before the first DMA transfer. @@ -153,7 +153,7 @@ static void LED_StartBCM(int row) DMA1_Channel3->CCR = DMA_CCR_PL | DMA_CCR_MSIZE_0 | DMA_CCR_PSIZE_0 | DMA_CCR_MINC | DMA_CCR_DIR | DMA_CCR_EN | DMA_CCR_TCIE; - // DMA channel 3: Output data to port a on TIM3 update + // DMA channel 4: update TIM3 ARR on TIM3 compare 1 match // The bit lengths table is offset because the first value is already in // the timer's ARR shadow register. DMA1_Channel4->CMAR = (uint32_t)&(LED_BitLengths[1]); From 76f69d33f9adb6c63c811e381567fd095a5556ec Mon Sep 17 00:00:00 2001 From: fruchti Date: Sat, 3 Oct 2020 20:19:31 +0200 Subject: [PATCH 28/31] Add project description, licence information --- LICENCE_ISC.md | 16 ++++++++++++++++ README.md | 7 +++++++ 2 files changed, 23 insertions(+) create mode 100644 LICENCE_ISC.md create mode 100644 README.md diff --git a/LICENCE_ISC.md b/LICENCE_ISC.md new file mode 100644 index 0000000..b785b6e --- /dev/null +++ b/LICENCE_ISC.md @@ -0,0 +1,16 @@ +# ISC licence + +Copyright (c) 2020, fruchti + +Permission to use, copy, modify, and/or distribute this software for any +purpose with or without fee is hereby granted, provided that the above +copyright notice and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY +SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION +OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN +CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + diff --git a/README.md b/README.md new file mode 100644 index 0000000..a01eabb --- /dev/null +++ b/README.md @@ -0,0 +1,7 @@ +# STM32F030F4P6 LED Tree + +This repository contains the code for a 8-by-6 RGB LED matrix driver with software 12-bit BCM output based on a STM32F030F4P6 and some additional code for brightness control and a pretty animation. More information about the project can be found [here](https://25120.org/post/laurelin/). + +## Licence + +The header files in the third_party directory are provided by ARM Limited and ST Microelectronics and contain their own licence information. Everything else is ISC licenced. From 72a55fb0261f002334f9e2ec650932a0b75c79ec Mon Sep 17 00:00:00 2001 From: fruchti Date: Sun, 4 Oct 2020 22:40:06 +0200 Subject: [PATCH 29/31] Add tree photo --- README.md | 2 ++ laurelin.jpg | Bin 0 -> 45509 bytes 2 files changed, 2 insertions(+) create mode 100644 laurelin.jpg diff --git a/README.md b/README.md index a01eabb..428acea 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,7 @@ # STM32F030F4P6 LED Tree +![Photo](laurelin.jpg) + This repository contains the code for a 8-by-6 RGB LED matrix driver with software 12-bit BCM output based on a STM32F030F4P6 and some additional code for brightness control and a pretty animation. More information about the project can be found [here](https://25120.org/post/laurelin/). ## Licence diff --git a/laurelin.jpg b/laurelin.jpg new file mode 100644 index 0000000000000000000000000000000000000000..bc29569fde80871b9b4178afcbac520c61f50f2e GIT binary patch literal 45509 zcmex=Bm<7(6|-7&sUh7V zR4_EPGO@HWHfNA$nAHSvUltPs!(1r4h>3wgsRQB;W(EcZbtroR$U_s@8CVz?7z~Vz z44EfD++D@Qz`z1!_c1XrFiwQ1VFdYQ5*OGNi|%qmL25u4BnFCeP#WXH5j#d$VvHdH6#5|DFw6iB zUn6KND={!LFoR0n@JVPRn4gtEI> z7#IW{*ctvXFfdGDVPFt;fT-_bWnd6>U}q>{U|?9l%D^Dzzyi(zB5Vu{;tnhfpwQ4^ zV_;BYU}i92U|=v|1LY-l29TmDYzz#H4$KUXSQr?l@GvlJL2?^E0}Dee0|P^Z7z4v} z26hIJ2hNByFt9T)F^DrTFr1NQU;xE4$nQ_2LB>IH+Z$;HhG`5;44e!M48Noq7^X5X zF@QpoMFx~}nL*Yv@X0VR2rximG?9s+p&7~s#liyyW(Gk91_mh^1_mCexO>AANl_>p z0GcYnRGZ->3Fu2GtFid7(W{_fFU|1%@z_5UU8C-JQk^y;xnL&wx zf#I7B$TsY_fftnQ5psPWic?iUW(X1@Y3B`g9|!HJ_l7h(|8*;FfbT3fORnL-pvS74^smcgc6ca8bR@#IRkPN$fclS zAOb4$pAnQ~85rIOLu>(+EFeQ+7^EJCL1IvCV5$YG1;pWilu6(shT;GJ|DY-rB+kYT zA{jv06{H=6Ap#)10s;)u42=K(PhbIM2$0p-`~p@Fa{B}!1_n^H7qBxh++}8900lcJ zpf4~nFg!pq8N^?~2z3xxH-unhVqj!oWH8h-(6eMPHPkRN&}3j>h6E-=1$Hupfq?;r zU%=BP14Am3ILN=q7|A)<4P{_pn90Pz!oX6-bm0BOKu{5cQz4QBCs@yGP;elL;%5%D z9#B|-Fvyjlv;?9-`2D`M_dpoLM)nX$41_`ApyD4ygD^-8gh6a%3=#ujWHAsMBnHAD zHppHO8-zi87zT;M*dRF&2Ju1SAT|ht*dQ?w2C+e6APjRGtoZ)_|NnoG8oZbV6z8DI z4zDUuri7>@pbTmn%qh54Lrf!CouG_w{A31DRSbzC5Xl5;sX=Wr(wwpu${eMr6#{Gw zL7)a5%Ol28aCT2%y2S{JXjx_(W^)!3mUoQHm=>_iXW7D}&h(Y#8_Q$nKmQpSelf5x zg8B``oDYQ`2p{HUVwuc1g|Uv|JVPl<1T z4Eq>bSQ=TFIodgX^8MtKU}*t0omo=Z*YnkaSq|(s`0CmJF*CB*vEKkSUZLJh2WH+yK7X)mCo8vz6j;Z5mSTzdY>X_7 zEIn*1`C#Uiir{CZfRpk8Whr`d5p7|a}?hFTf8e^Ph?7AXALcq5mpOeM}hwU;cYAB{ALL zQ)I|svSwPt8^th{Nr0(=r-5My(>KN)+-n#fG2LYRz{SVt&%BZ`f%6L^KZgw1&!F&8 zV^CmlVbEci&!Eqs%izFZ%Am-g#-Pq%%wW!7z+lOs#30Y0$DqTY%Ams_&!EDf$)Lrc z!Jy5c$Dqq#%wWkN&tT4=%%IGm#Gt{T#URU|!XU>W&7jPn&0xh~#o*1L%#g=m!l28b zz@We&1BbE=`T>92lY)EEudA+!*W` zQW>HdJQ(~KEEpUaJQ-XVEEud9Y#3}9Y#AIF(iw6X;ut&_92wjhLKsXKtQm|LbQ#PU zEE)V5JQ*Sw{1~n?>}2p~uw$@cFl8`hFaeWh48{y543P|z8T=XS8Cn^F85kLZ82lOX z7{bA3Sb^=cVQ^sZWN>CkVTfjMX9#AnU~ppaWN>A$XK-Y224h!-Fop_-PzEmsX9iz} zAO926YBS22}R2m`1{0rmfw8Q2+E89*c_gAD^810%RqtHZ#_z`?-A zz{{Y(Ai*HdV89^9Aj;s*Xbx^WF*8UourqKla4~Q)XfZf5h%#6*2s6krOk|K^-~j8d zWAI=oV<=!;&K%QO#dPmzY$ykpKeIO5Oo$^G7#bKDQo%X6m1`T{a`9lkUH`u@@Cw}) zbYtNEug%B6@SlN+fqf1$1A{mdgVqm5hFBv|?qL8`i7+!5{>w8kf-qQs=@^qN6DQMa zroD{y%nVFW$e_Sb&Tt;o3IsL!7!(+48E!#&j118XabT?i3hQMeDjD`RMLkYBF4Jw0U7(nA%ppj%y z0R#yhCZrB3Gn5Td%K~MCRIwtlIgr@=NNiAp+yYuSGJ-~8K^j2~b|f}45}O5y4eHF~ zK;6d5if|hn5}O~1Er7)4Lt^uT#`&Q3va>Qk1_7XaWD3;#fQJP;!agP>HVc#u3JcKS z0f+{f!3`T>U|?Y2M`Cj$sRyYW#iJoG8UiCT1l%%fBP|Nn0S62O5$sz8I7AXAZ*pie;j|Nll9G-L}>?ojnU`2psgRtXo12;k8l>N2 z!cYe?NJE(+6g2k-?ooh3B*@YN)TRfIdVyx-Ko)>Z2Mua4Fff6tFb2>J4>NRz6g1Q= z1)Wg^&1Fg>u{EGH!j14tt)%l{(`nt}{WU^7`jO%Sk+j7)-x%!ZCELLXR_0uvi2 zE)*6iy!ihX0}nGJ1Ct=LAcH-_lY3qBIOXRAFN!@bdHjN~i;35SR)$G?`1e|A2>hOD z;Qm$QNZS&*dmbWhJ7N@=w~H!v&tZ;d$&t2hxB0f{NNpeEj?cmF9C62g28*66ax;81 zZ}*%YzT(@quO{$&7n*cSDI4$PT4g=SpCK^#<>!dAwsXI(+2Eh^R%D~s(kQdwrZxNu zO@>|CZkKi5%{jaJ&b_Fp+?2jY((w_}J-id=h4B2eJRzqYys%`!HRZj#_ssIY8WVRg zH&yqv{kCwvW1bh9A~}5Cu`{#G>6-cKu$S$nU;F3w`%U-P4Ou-suCAwT;nI`KVpPPn z<-T=UmaT3-w5yHb@y3Ia>=PWF5YX9J9M} zcac9sLb*v|&x!-bKQBKnaeM0q$G2Y=GDdAXxTmm7=-n9xXMMJH_ih;!?5S<#wvqFC z{`11|#BMo`wC6G@mk+IbB*8U3S$3gY=32?LlQPeLheWElirb{51Vx-K>`ChMF00R4 zz-(s|GU=F@;a9arh8eYYc+%G80}aK^ z=Mw|CQ)@Jz_8InR?7nLxk+U|f?-S?KnUTx7OcTD{s9j*^W_y!Gz~i~>kL9)dYxvB| zrX9U3aX);MnXF@)LXX?Di_RhqPF@TF?iySX^SBKeXS`G4+OXx~uAVQCGGF9Ay7qPT z%eQ*dvKOhRGCJ$;s1BX-uJ%mr!kx#mrd?iU8tZhllw*-##FqaIOU;Wd`ULgv-Zq!F zt~`Eet1R<{{R>{z`(1md8(13d&HY06@>j;h0&!`lZAHbCS_?SNc|PucHLK`l!q&yJ z)`b1O`ZnwJzo&Ee`_$i@{^3>9xB8aX5=VbboSnH<*WXX@>{{-VIjOaJ%Ei{b3;S2T zJh0B%BH{NAo^^&NP0Us=%KTNnJmu1Py`?O@q7S=Vste3{I8MktT412V$9dq*PHS0Z zuPdzER;RAY68d*_@9v&E_wV|5DE8fyGjYwSd!n_e;XnbqT5GM9wXJMP-Qw(6-CVsm zj(1PprbY6vI2P|JyhHA2QpMvP=QO|kjajrU4kYK7+aXlXjh(`TbOY?@_I7 zFQZoMtG~9s%TVgb%JRDOrbE&m(^Opg40~2JES&w<&*8M3k*9?S^PeYek~Y_tuY9_D zm)NyUFU_uPyy2A8mF-cW*0EDt`-H{R4F~c+-|k7;>~ZR#^NJ@Ga@kvMO|{s)xp$@9 zP17=mi^iWDD%LNuUBZ?)`O)%|!CUktSK69R+!Rn?#6K&Ys!dn^`2@Z**puA{`l_S-bzK@?JZKNvAlr8TR~cu#{MNBr+f%`SY`xg`$TP432pw z_pudzUvpAwB1_$mJJyx!m#W^7oig>Dh0OQm=l3|w+mc}UtHOkB`xU8pzZcqCk_}W)`;g(3#yJ2Bm3y!mBHmV+(Cs_Ral|MsKw(Ez} zt9Qk(vhvEUI~>%wmAmbW+{uY^I6V1O)q8kP2&^mDZk}fleROX9b*uGpuh*}7rEruf z&DuNRW`y$j{M~X-e%dE!tZ%z7P%77OJ#pW*8PZ8VCrYcbxp69#2Ql!lTQKw&t@YV^ zBW+^r&ggkR+CDq&+8(lN=QPeIseNMo{4$f~865ofR;JI}uF)FNI*h0R`*Icw<=vx1m^yEGn6*3B#v z;r(3~mip~K!_Kma2ibNmKURF@y4BHjs~6rsxbki+>y>ru)Qf`L%X1d6Fo`o~vQ#Em z98J9Ta&NBrVoCd=Y^@(ZZU3_EDN)<{RK6stKHqMB{k+u+{Qd@B`Ys!oU8~)>G}k<^ z&8=K8=8e#*B|8r740~*$@S$9jH`rCT^^fl2R{{Pynz^!j>{jc)(!DFfIyFLwTZl8y zYuh8cujlIo?`BG^>$uNna@R_3!VV9S=-GSyytiCg(r5hg$R(@Rj%hMNp>7X6Emr@| zKJ9DlFz@#Y)A<*@mmTY5=D3)9rh3MPi8tF0?ff=z!rgssd+zc(-&`*FN$XMp z$I4@WX8)QV%XxCGc>CPj&vRCLPdJ%>ds*&QU8{r7c1um&x&3SGk~V{uCa*=?WH)hG zZ(3~I=eTA|^gY$vf;@XBN7{GVKWcyYbw`AWNcD2p{%e`+yavoyG6EQmm^b|nNq<&% zwfdrL^pdapeQteKn@}iv(QZjj?S~x?ALmpC|8-0H zIp4ngq5ASQ!<~zn?Y(9u^=R!$X6&;U`P*WUw1>g{-l_v~u8f=~B^K%>@hn&9Un+GV zpX)zEgW;{2Mk|Asq|CedXfnf+gVK(?U)cAGP24BKDqVQ|(VT|@sUe2%8(ms71PY26 z4_F>`n8h^fikI^frsWn#nb*BA+qG?~ZR@Y&s(W4}m!zI3sP5W1J842}*}KP|13Z*U zSdY~-`Ojjh*!`&d@auWa*Jg?EtIaJqx>ilurqa#+Rs1`Hx~)BS92U>^&b}V2zU-^n z^uooj0^fB#eo`v;VouGv{e^#j%4MI4Iy!IerCnwlFV?y)IAMBXz0$7BTP@_dF4nA< zZvO0cfIY~PkHbD=mr10)9Zya0&KG-Kw^YAelB@NbN z#?%!rrnQ-E>C@I&QnpuUbQ9$8J09 zxSDPj=KZF-@>ay0Y0BH)RsUx&?3q>lz*qME$~vnr>%ENU%DQc=ZxVBQvw`_P!{+}C z3gSOjw|!lH)NbADnSXt=L#Hkbp79`fo5p{JTg|uj&x`)in<6nS>CMs3CzC2oKieK$ zb)wyPb85HDyr1u~SJk)Q?^IuGHUAsm%Ho$VZarQTv{^n!_*-B6hUeL}9d_DNg#R;y zUfd=AAU*7B%LeaS$%}8)l!flN)kcc^^!WYw-3|F!dJW$$c5?)u`unnF_wNntw-#K; z@te3f@@4T^+Y4K^cL~}&H#)i1>0Oe|oRo`tDGD7^L(MDS{#>=eg{OZvYkyL8NO13t zn9n`JuGX5$>_2CA&|6ki!Ws(E*`ivO{`!&#I3t5&%?gWBg|f+?*y~q3KU%0a|HZwk~kSr+v&tNM2Z{hXt6Sh6_<2d=B;oX%GA-AOs%=&X=J_o&=h~lsvIO>9S7F#oV{Li<(pqL_BinTKt0HLD13wminbVCnqr;Jn*Qi@0#sF|D5z+ z#a>f65({0qwz)VzJ?bp9>b}ddM^sp-$Cy3FfuW-vaqpp{6E5AE5N|W%*?{b z#KOYL%*4(JQpP06%%aFDWatj| znF8s6th)mDmlzlrnVDEv{~uw{6J%syVq#)s0d>+qYplTiEI}bfLl$8r$3UaRLZ`-w zA|E~~FWh+XA!?86VMB|Ws4z#X2Zx$bYrsS<7SV#Grp{BI>MU8XWTB)Jn?Os^3eQh- z6qz_5ayB$NadAnh3NWl-S!m(v=~U1WpwQvN@{p-zp@|b`h{%zt0V_mV8hi!10%kgJ zh-k?u2?!WYxx~4ENs(c}0#C1Gjh2v8Ow|EW4Xnyj9x|?w;9S7+kfBALL)=s3p@pMn z%R_+`lcs35bT|sEU^x)N)TAh|PAEj7<)MayD%S#4F3w+S!YxiLlUBGWOmQmYTrk5+ zbV;MrYGX=JQ#5h%G+HWhfyt|hQ%NW! zs6}W+!IEVHCX+oGF9}UK=%C2y#WpEZL12Qf!zCvHPPIu)K~t=6`dw;V63C)Dh2d$) z%HL`NEK7G9bF?sMs!d|$;QAHR;NZ!~?6TCt(WqgH%MwwA7?q-#oC*x?o-JK0!kz|= zOSu+mIBE*4_2gLT8KlUv#3_}dLy>DC7q7eKAE;iwo#t;EchWVMRR94BT%wpbh$sxdG@>JVZ?HV5HhcrcvW_mjMhH@GPu?TxC z3=x>4s=>rl0BS)Ph}ozn5r5yDZoJG z(6{ItcdjiCT5BaLbBt4uSA~Dq$IHocJ}{g-5MR9Twa>*bpR;bU?dF+odXC{;pY6fe zu&bR{=k-|VPcM$RH(%(P;5m_pRr)7Np9ZnrpYdn^*&48XtFx!Yp~6;{Ljh|w9A|RsDtdB&l7&H6K#QVF+=CS5iJuHK&< zbIWn7%8u!2s_84YZfQ0-m7hJ^<=swQBiZ0(fA>GFOy)lC@m=>^O|9+4c|SDYUMe+y zs(SRC$|TQ|8mt zI7$a~XmznHFgwMZ7~sotXoj|n6VC}vKd~i=j#(UY6hxzgcu#S$ayoLbvX)G8nNrkv zN|3|BM^i&nX`+{~MvE|qFGqv9w%dmeU*;gEg@?E}ZuK$k2zbKaMCQ8b({-%t zWSSIOFIW6$aM_renR!Jo+9ay@h4HWRJ71RBDU?oI{IT0s;MSI#K2k^9oVy*>?H9!B zmKFwmd$smz{Q1z!a_o+Yvn-?pH3M{!i**gT+@1% zr82j$2{&>$vG}kmObQiL^vGg!Vrg}5abgfrnc?8&Ic0*-3=gI`S`wVrTn*k?ESG|( z91d0zV0g&!Qf5JdUSD_7XQ&+k^T+LxE_zFeCmM6@B2RA$?UuQ zu-5#9r3GB>R?w>|!OSGV`R=7qoX z_H7Q1S?t!V5xTQ_1UYzcTRrs^_-vjGo(^RDTtI z^^f?f@Y=V!dMiaWQqSlcWlnsUK51rB<=dr3$=7A=ihiA1Hn%qF(n{7ZaZw%*)>y^X zcdS_*yr=f_+?~@+V~S75@U#EXv-+C6CvIa#<@?CB%T#`P_&hW`p5<{ z{PN~+uIH5R*Nz%}Y+~2$|5&%R1VW9vq*Ex z)fKmLg`NvAaPRv)?PdtywjDQ4xo7Z}>gLefl=K=-S6``jTnKHu`uw39g#FQbT z(4rWq(ehAffl!D*fRmz$!jz5=tAtvF1TU@N6zH0w+@jGN)T7Svkl~`FhKj-rk4}fB zKB)$rS`V2Rl|&B(teE)JAh?C?se_kr%VG_NS^S(#jZ=2HEEZ^FIpiC{#L9VwMP!2F zsU;g`aXw@;5@2FjkfPSZ!l}oV6?=&5rDnk8OI!Ga9vTD$X!=iKn8Cpy?3A0nBB5wk z$1TPq3#MMqYhGdP_a`_vORD(iwnyTYZ*F*dva~MUcx&TMk1ZZqlTT&xg?XhM@S7|j z`F6Q)U-g{?(?y1sea|D87sS8J z6w%;+spMjq0_;suZ^ZJx#XKU_k7`*etFjq zHjg*A-ukidSnFA70cKw-lfMr4WL`Wik3MFdm0a!FU$ptFz|*)e@m;M^S5~rBZRMWr zyVvu9{?euC|E9gRQeAc~A~x*9rr2E;a;Nr*UA&z$X0m2E-Ho8Rq>7mev& z{X%MO_IA_z*3w-*eatn_iuPD;^2`2R`tQ9T*DUZkuz17~iZr`@dTCTGP zD?U81j+c72!(vMlhaqd#mbFgnX4dr^~rDd(P8`qk~_uB9jTZd~m*`PH+n;=2^@ zIekg)PpdfJE-d%u-8$jS`7Zl-eDqzHZgw@lcZXx!!s+4#pD(-b`m5}7`OCLII;`GR z`RU7IK1Hcayz?`DW>&ap-a5r|ybC@4GjMgYnC^Ri@_XE#UDsB)S*&}Tzi$8TGuQ3b zMol;Exw)RB$M~|%|7$icuKv)cAjY0-_t<$?LuqI{OXP8_#<%X&R*NbUpD@r|Y+}UNry5!vAoJHnVwQjvm^X5CQt9u?&>>AZ0z#_+J z@3Xc};m3@a7x!OY)%-QpyYZ{K0)M{tV~x8paiOcLL*M^1-WPVBp4pzsEmZheyY)d>I8We0UV&ADT zWil5_M~G8EBSWjFLO_SFitr&}O`Zizf&#%DQUWbZo(q{mxB}*LFilcrWSUacq{^U> z;Uu6^(xTw$v}7U6hYp{Gq6V!2Eeu7;nodj|>6%(z0?eEXSS~PfI4!CU>2PpRd`i(#DR|Nh4u|eZcUB4AYT#;`;iJGYrC^qO3&#wPR3lZh1&RWS0xd$( ztag)?oP0ktWH@DtFj$66;9SMJ)Zl_+q2Zg&ElkxOb5*|mZvRv4|Ci_1|45Uc`6tV0+Dn0y2lLbFx zDKR=%#&*jZ9oo!leS7)MYpZ>Bb+KlSC8qrxw@HT$1B@$Xrp>>jg_UcgqIZAV zx4OJmU3+8w^58u`+WtLzw0(JMe#qAI>Uxboqn|AQ@UJ`Qbm;aSSGsez{dto-hqf2Zk8MU@aaCD=>F+#RrA)CRxkf0HsyZu z@=N6(4j4S|V_7HToS^TwaK)7?>tbWih(t0NO|D3gsl7fm|K-QC+B-k%>~-_l@@k*L z?Nb*7jJL?IIvSuRzWlt8cIKLQa?7*Dx2)_gj9R`lpocxl>EL_))0=A6u35S1N!a!Z z={YBtB`Y4*`tsK7anPz~L5ypc-My-1&6`{^wI@p3Soq7=)RQ$+*5{h6)&E%iE>Hf& z%>df}Rioe{RzkJKD)FYNlQkQRi z+2i$W^H;mrxLxmTyb2}?9xB)?7H0ZYdRx@(?Zx-%4wU(C+H5Ghqwl`^g4-{ymbC49 zb^dx~b!+%L>)CSbtw*kxysFAu9(1q!`=y3oDWA7=3hjCxvGGFIKcQQb*PWHIy!~zK z`6K64dP;VPfB(8w`_}f-$FwDl*D0$W)uiAyu$Mis(KVR@*i`MKpW zhqxz;B6G_DcMT3sPa}~jA{HJ?EPNRz33D*Cs1!6z@$e{W;Yl?TVl+I(%GuD=V9;1L zpTkjuX*!3Cf*^;Fg6ATE#^y_xTJ57&Yo3t4BW)kFxo35rb(r9{^*_S>R>rN}s_;r} znyq{2r2h;R532dE*1Uax__|5f)ns3(FEds4Zk3jJ(NuYydwurD-79Xjg&34U-`=ba-CeK2Z(w)IgO_nF1LFZE#gY=OX;Z#Nm*02XF3R>(xV0x}qtuMd zoNf0W#kmI;s23>wUZEuZV*0J#-nUn?Ol3}eTYp&MRPY4LyBFe3i$8od53ks?Z~KZn z&T=o5s?_`MrvF8}mkveIPld&RXGPp017c`?UiH`nrzby;_I?v8Y7-gI5C z)9%g>XAS#@zpJKZ1>aq1IoWsrjDSqTofmJO(Nn$~v^D(dicjD7Ot(BfDJHEu|LL>*rU!RxtZPHB z=B-(=ZLfHEHFKt9G2i1RtB9nobCI)umQK1H6DT@eJWa$$^uq2-U!}$Jc5Pbgx&O+u z$6sT9Oxica$G`pjg%U5R@QcxZEOT?0Zu`#=xl62R#Xny2qBjW>&)@zR&iOTGU4E?7 zxs}Jq5@@@Ne;pb=B z-TmdP>5N&$sla>tokRznW)sFY5gIspmFdUYED7 zRy)gY#q&9A{l8nM+|ti0etbA$&85u|pPx?16OLW}M|)*(%8uL3w`&s>i`MC8-wH8# zeYx1Z+Vsg+S8!*WrAPCr1)`U?ua11X+;8@r=K>cP-h9|y^d-}(*4OLj z6sgPGr|;lDWwUg9d_+i`tn)2Uf%_88wquUuq-?qZRGe+u|-CU znTsjVprggZ;aQNPzm;3du}DwR&WLcRhE z9JDMPBpo>fJ!BL82#NQl^d&d_pE92P0x1|%CG26ci(W} zx9ZwEX8YF0p2?14)teYO5?%(k5b@TEI;^kA;-VF#6m63|8$ogfQ9CG=~ zs}hrjUt8X2n)JI(lMmyZRNHy#x@t0i#J`)#Gtd57eEham|rtNxoge%u#0-JZg_x`*c8}sf3&D5>gC^~Px;Igbz$)L&H=ReNe(qQ>< z-lqLdm!iY2KX_#>=GgS=iTIVu0``0c&iFZZZU=3;I@{~k>O74LiN$j6&)Iuw>o@(L ztM7AZeMazx#lBzG?-g)9>iedW*+~8Aul`-1vo?R6dF|ewjk!e~_vS=}m%a!sei384 zZW4F%vbxtd|Lu)itMBx0#-8Q!=N`$}JuH5wv+CBB>RFeh?w>iN&^lZ0)>^)-U%_qD zdVTC-Qn&a$yt!@ZqAL}4OAk~pP8BnGc<-I|YrmyGYz@z^^qsQ$&H??~tP6bh^fP~t zmtS%tc!gGcTwZGLin{@Qaa9T5wRznG)~<}ZvZGRQ`O{?w3XU;<+M4?Iyq;m^-%EG@ zJbkLN^IY+qIL}GXi)v4GRkePd{rcL*vu*0bY1<&OiJqit?PHdrWqCy5U4^2u` zdcqf^u9d3!Js?xU!NV)%5F;nktc6|+LOyf`1PM7UTrx+IWs$I^hAP)WPltt^3t3f! zJv6j6)eS_>OkrYhs#PdxYFluK#ZAP@X{Eb{imgFVagEB&x2&p?VJtJ8Z$t$sboeCN zO;8YUO5}UYp%l#N7%JfytKj66Xu!21V8%n+l}=hQ6MR~DG~_)ziq^0Os<$-wEW5C1 zd1mmAEl;z#yq$Nt@RmK8C$eo$Vg!SzBS&j|N*;%rSnb*k4>l`tF?^V7GsBUE z)*UW3C%>5`pY`wrzwm6QZR^*@Y-_cjwKROvb2;{u z`HMbC2j)9&x%=<+3a)-W8P5s!{_nQ^D34yb^v2zbsfPPr1YL(3jy> zdNZZE*SA(Z+&xR?Fpv72vij7Qi}Flo>0Qyjo~vbU#QjQ0Mzgu6@Hz9TL*MQ$O7bZ* zUGq*R*W5QX`2#zwWKuL`_@;Dz?zGTY`weNw+J^%NAaLvBAkC+AQrOS; zl4=&G=li$!rsbIKKJET$s@AQb8@J}0UGg-x+LBjQZ+pFZS@urVyj(J8AK(4; z{kwj?*4114v~1_v;;5omea75xO=Rt++B)5sp}UoBb&6k<;jby{WUuAg`+a);*hcy7 zrK@kf?`CH^&0HPXtF(a0XYvm7H>yQ*m%O}{>K=9_FY$o5w0Hf?CqD`w?*I2AHa7Qa z$?vL?%scz$9zE+Gw&Tl5mNU8RH}sepCtY`BzdiNkVevVIa*7X5n|7c1IW_*(yDK;S zdbNDEZ(iiN)N!tV%D3>|9j?)d+4@WD+b#23Oxn#HCxkEB+I4JU%dOleKjWk0rb@@! z{@Q(acDJM3Y~@K0*}tE^S+kbQe|_wf%+6YOw(YONFJ|?b&&oZp-V7#B z)za)g+V#$U;sK|&++m!RmQcNP;f2(xYomW}c*fqKG5OEU^RE(WuV0I<@lN&jEGYP= z?I<@Rvt9U~_+pu3+Xc5Tnm1qd^{EYio~_mQe_F16=E2_WN4M(5edArL&O5X9i4xai zz3tcA%=(NC&RA?UN>SLBnwGjW zN0oy?(PXzzB9pJsqy>yY4r&FB$%o83eN-wP`IkR*kc|E^F{#PnkjttLk4{JBsg9lk zD_1aaF@$pPsB4HQ3AC^*3gmE5X;Bd3opQ-hm2-+hL7zaN>=K=(kIU5=E zL_diNadM`GHa^w6D3Btu(`Tkepy$#kDHe4}7oWsnCt-;Yr4B#iDNDQxrPX2pO8Z=ux<3z-xdEnjclYVEcD&*97YlTURWW?1yB z|6kbBsI|IF*gOx~9W2QRSnRLBF8|7AdFa{|+*;eKWlw0GEobpto~PI8_c;2;s;doo z;-ZWSUoOu7&+tTl!H>RU_r3?LEzOMU)yk>6u`J50qeCgJ_WJUNYt81$dfX3OqFE<2 z`Tou(#)$&qXRLj%$yWwFYGz(-`EJEQFXKCc_ry`b(qsk)_mPTksm*&p%g%OY+BEIQ~q>Gq`iQJWXuTkSBNr@*M*X~Wi?Dff0| zAIpr*-_HEF*KgY57u$Mgzqqh;=b_DKq>el|@Ud}zK6huf*X(s`-*)#@DZb;iC@eqY z_;~n<{+(`d;Y%#pvNou-?^QYTkNj%x zxhEQ`<-{+X-Je(TbH?sHhF50>PVY0#lA3zw!mHN17U%2^iCb)2zJjBm`O-_fJ9n%; zJ?eD}6Uo|rB|BDi!A$<|?B~6;ZsnfVzJ2k_;kiYZentDqG0c%Wzv;YKPEC^8Wi^>O z%St{~W*=$V!@neV)nq?k|XVI`@2;SuAg|EDG}#=B-*z8%h$_=O{rHezuGy=Zi&o?Yo@)rw(Vz_>^H{z zx@{Z$^65T*J=>|Tck6At_%^5QVbI6_3>_gw8^+omuYSgv|EOWfW6M^@hg*6Li( z?P70Pu6%iEx&D`ljA+~H+U)$$DeKs1!zZ{Mu_?D8k`+iN+= z`=?(Xy=I#A_2ddqQSP$9C3o2V?$6(J@7|}U+nVNvo9y}YK)i3Vmc{!u51#w9HMac~ zTXN{O*h^Wf?JKtAX{$Z*&x+}sV)3x-(}FWg12x_1$DxJHEoyEeCnX%(HRmX(YO%j-N>yrW5xMfzV?yxnRoa5BvwO7I zIc26W$E*us5M|^LQVjA?V?3lJ>?0zqbvwu)WQkK#ce!TbvS7S&pPyOm#{0;1J|sP;+vg!p_09 z(DNy?iyF&<1ulwBUNtS|LXGxQOf9B6UD%%B;*b}t%<)S*Uos?pt3gxIj6g?=K&?d; zL5?mSPt6rr$~ji$F)diach!S2gn3I#Q)kn(7p?(DLA*aVwS5a^Ruv3jXVf{hKp^^E z^WG2ZwmQ4-D_rTbCp7QS%if)F>pS)0*X#%vnRD!NihxJwx6iGg4meJ++FNFFIXApb z{^=Z@;-K#xk97;8rfm7t;%<=Y&v_wxZusTZ0dwz9{ScFWgf(u9~2 zzi;_U@5`~*$~iKpF6NP0k>a)E)Ww+-H8;0fA9*WpyodGWQpa@)VRa`zY*}0Bw;(Cv z5sS&p4`RKWXWaR_;CE{6*Xf78+*}%1Yqs&L%-M7Eo_EBn*T0Ak>q@aac>Hq#d&H-8 zUa!=Ww|zY-92fl}B2)C0O?P&>#M9&JS8Cr4e|XkmTJII9=LNih^EW+~x+?ef{MwK| zw;x|I+=+)YR>77EasP&%5>MU0w5&(nY1G`gUD$UK{!H>b7KGdmjb~ zmWg_jlmAYyet6g8>X!9xTg|3jKFfR2-lF`Zeeth#rt79xX1$&tG}mjYZNzFGd(8*@ z7xbR>ez>@&cz%FYtJTrnQ3bJECi!pr;3u2BcBSpU{@syVSJo}!{PK3IwXU_-uIZQ7 z9p8IyhQh?1%c`0b*3Vm_6|;4_sB^@p3m2|!&&YOXVEuM${^5JAVfQRAXkNa-?Zei; z{MA0C-lD%2S)$ymN}mozrSW`l`tsoT+xz9|^DH0VvwM{4Hf7EkepU0!d>PDKv4vhU z`Kph|vH1L*zU$UhtEp1g<-?Xb#GDeD!@Pj=&ZB~3zvrw^-E?W=>jZ=}fkDp6}G^ zUA1g2*VW8x>TI)n)?IDo^|`^7yJt(TmY%4@>s4#zW-nj!U|q4P_NsGwn|U^G?_F|P z=vDK7h8NGox7@nEG$U`FmR&cu+ksOF6CXd7e^{~pQ1-#?dx{#+T5(NS+;DKDsG!-*CklpdHA>K+wIG%^KMyZm>mdT_Ufvy)_b+7 zinseyz8;Vh59{oovbHEZCUM)`{SGRB* zMt<97YRWyWX3fed@3`v18OOiJoK25jSs(r3?eZ5lHuSsBwX#sZTRi8nLD-DFHn)_n zy?l0ZcF)^ueRbpYw)w3)M08ErIu^&&tX=(|;pn~B z&u%4V9G|vBY!b%_#j4s_$4g&DRByJ8KI}caYTMNLZkxHaOb)VEo{V?*`gS^Y`|`{c zS4y>y)ofYk?8x`6oO|+$m*TUM+aGBP9G9QA;B@Tw*s$NP?t0G-jy>MqBhZjO#mRL; zWz-Jkc6G~BlX#X@If^n#7)*7V%_kzNB~!E})p=1sun)_3O-92*TvJsN4JH~jGORG? zc%Zm^icqCPNg}(Re})66s*RFBV}pQF5Jw2Z)1YaewVD`O_D*(Dm~x0mp`f9~NmXXS zl;x+E2yg_rYaUUT6MmvFrQ=YBhN8hlhX4~r4vmzl(S=&kPc4rqY+#X$+^yADG-ZLb z@RTE*LF{L?Wiy^xaE{X@H7lYVbiJuAs;-KB>jUXwz37Uu=kOt>`A^*Z{<2o}tk~5b_I|mEJFl#hJNBb5 zEWCee>QeQ`20xb>o-Z+6GXG{(WQt#wUG1#BjvE^cY&7S1-JDe`wsHUA{F`czWbg0V zd(YmbgXPq_ZL`ijDAWC?aaH%NwByu+852}e_*Xr=mbl{6cL$X-t**;EJnCvU?b_zK zd0+8b<1Z&?YBEUNICMwsOyQ-jUcrXV1wJb{7=ybjl&8)+QyYBpDQml7fAg*FrVDpB zo=|AI=lC*pOI^mP3zlm$iyxSzoKy7dxVAJYz1D8E|CQd8rBBuNUiVwRX)zDmE{30X z(i^`_J$=>mR@ton6=&V{MqTmb`En$fr7rR5@x}IE;=H7$YR^s&(yQGzH|zYLqmJ$2 zmFEsrJ@~t1{j|CTZ`0nr{1Wx5WC53MCU3HY{fhbdSEhbkw((TOkK1n_-Yqrzy!`He zhDWF8cbI&?8EUe2h5ggDC631n*rz_+y7{t6VdRw3UFBuvKDVPfmrwl9aH7^aPS-wW z+r}TYS7Q^j_7~DDL3>#vL(d-?BStr2@}&Uv^pb z&5e40d5zZ1UsHASGV|_T?=;oiAldMrLGD=Cc5$Y?)w64@Fa5DSeR}4L%eQ{@+FE=H zm+PCKbL>j;icKr3Ju+8#-h5}YM`zO&U2azO%XTw=%3a=izw}B~bx?Kh#`%HX$6sB3 zc{EyAa@~K1fc3s#HMfY}z9fCpmQ!MW{#Dm&n=4mrytmfbZ+81{pPvqLJi1?2RoU%c zw&~W3I<|N7^$oW^tC;-ctM=K|nOC>$o}3@T(|fksO-8&TS5p7;XL+Au_w7>4e^z7$ zY}{5*DYCZh;jR^VQPzK_yRF;o^yu8YnM-c%xjC~m>gasi2#a}-w|=g=J^u&0WmDO1 zd)IjWn5)~ZXVq2Z@nzV0#HK8X*2#WZcsru{&+gpJ70)(JINia-^rj}TI`7tV@2v_u z-(=@`yzFIQt1q3K{p$NP{vFE~nOs};K)UGG0h8T#ifVuUTBp0Vn#(L?y{Xsrd20{f z$qGqTa4h*!edy6o?dl`(YgG-Sw$2XUyKJvixrwj(L3Vkkoom19zOIvc^5^g;`{k`) zbhlp4f9t)aH)?9uf%8kFG$+|EXZd?A>R9KCw%b?l>Lq6d*EkBS4?gBnm3F-p7yEBWP@(c=lgr&cSmj9>ZQ^5GRvo*U;NVQ+H6;=-UBPr z?LU5x7kjYV{QY&wv}^JI6sKfK@)opB{ySYRojYdQn(sV|F4{?iXl_X4|8rqwlgoOm z`CJ}T)48J0C^!a4YWXzHFlhXs$SdKp>~IUmhXqC3dUX6XB%Vf{DcX`bh4Y4{np+Tu zJcpmb1cw8MnAR~lpIRuwBG4dUF~y0Ai+wv+h5wRG4K7}u%^X4kL9I+v9^T6l64wycqffGKGvLR|csRb!tdHWnyAZ4UXbk$iS7W zlDyQNQ$a07=MyJ)Kvlr|GY!m=&Qm)SHRdQt9`bA8EM(w2b>y!3N8infnQJ~OZaxrG zy*KEw-?dijYg|*>68icR&8)9P{aUNH?C^%7&kcJ`)=J3gTc7c(_$wN;;?xz%pvjG? zjtZ^2HQ!ZaOm1~!*dcWD$@(=|I zrF<(_Ft|8J9BZmu$5das`)h9Mm2;>1l4t*9)!MJ~Eq1qRmHP_eAUJ++8h2e?74n8&i3S-zh2+>KfGPl`&A>pLH4Ju+X?%!Q~SOq ze=T`daQ@4;pL4d%DVnn5+@tWMZH+4~3Ekq|A!zJ)@Z-0qMe?Cr?nDN>sW_M^;P_!{ zcUX67qRj)>dQG1EOp_fg2s&?>R&U&}%85_e(=7VwT11_=e z42k!BQ=G-{Y=w2$pL&j8tv0Fe%*_ZZ zo4VHequ2yquD2;W7yN$kpW)DR+2yi7U2^9|RZqFH_G^z1>x>oe{EGH9XZhRv8<>e&(ho9DZuAe^j$rX9cV*Y6}Vxzl% z_l8|MbZd9>yXcTh*|UtE9aE|N9;asf%#Q2RtCDQ1zTZU;gC^d&yWBo?UQD39#%JDw zCs#ghZ@a21|M}7ig{SLSQqw;i<*uHav~_FMY|SlFt#`bmjM#U*j*i(Ke^rOE;BKYs z((|_`L@R!NraX1)zJ7QA{X5sl`mJ5S`YeX;hfTF)|J6#P^jDkZ;`UUOp4#?dZqd(g z*S2b1*!oAb*R0{p>DQC&;`cfBUoF(h`4(|Oyn24%B5%1z&$js-kYRnj`gQDG>DjN_ z!&k^X-y&l)Q{BKmGvuLx_X7Pe8Jp_u)2Hjj{>gqQTB~lJxP;@8SqJy4m*HEknViE8KwJHzey zu3XW1dGF;`*Awh*|Ekh|#n_*EH1kn<@2=@#hYjNb?^vpMO=eiTDq!c58|(9a`N_|6 z6D!fpzISI^>t5ra#eGs@A2Vj$F}iZ(Rie5L>$iHo%rChn<#)sXu^#obp5LA$!(e+| zV%zP9ACB+1y!T3ZVsFXig))<>jr;#Ih^xJN_v+R9wZ#R$w(^|rv;1Hc>ASXW_2W<5 zjtj<5`SxpXeYnnr+sm#k-?jLT+}$U63GBz?#7}rk=`1{HR5Nw*9fxSQe>cPSB!rxv zHs!#r4GcST4l~M{7`SLM@Ju-=u)vx{Y4+y{OOy?!9O8PSogCtSTC2GsYvGOwo65CL zs;Ib$a89(0%6X%iaOze;6T?$auH~viHCpORW+mEn25~j}OYUC&v$O5Yu;_iNXz@7c39?AgPwS=PmK=LLGig^MS>2q|AByThtj`}Hl|e>#`d zW{SLD$x-aD{;&Jdk|Pyyd(A#=n_s4v5hil)pi)Korb+9*AO1BrR_*Jp+!s2Dl^J*c z^!;aO{k8nitlF)AFZZ72xtz1bU;Fdy-_`XUCJR?Z322-Dy(X_bfB*Nm^)3HY*Zk_) zvOePSdYAtUyqBdLUccJ;`O54W@8rurCTHFZ5exfrBK_2N&gWAw@OD6ZZUD%^<7uDm?9~ z+Qy74YjX|n*nTzbYLdXzZPI=j;2pqfLm?HrrG;wz%B>Wdt)JmT#fBdh#o$F2Q`%V*gw4eMBUt3TBs>AJnjZ*GWGydxv`;IHVKXKU)b-MIgq-?05heb_G6Gn=cUc*H|~GW-o+ z{GWkeN}uU-$fiZg&zfT8?(nShoBmO4+oRJ}Q|G!)ls7f_Xj?e>`_wN}->)nArDL`C zN2%8WLBHmtW%n!p>`xFEKR?atTGxZ)&(aHyPLJo!SUdmdrlqGD+ilo))PJ3ke_y+M z{h!d~d)NKbz5BA*?ZB_u(w5CzQu_Ro>PyX|`ubN_P03qp&yg+cJ2mRj`vto1@BEC( z)^F0v+Om4r{m?B-gQhyMYMs?B{PiQQy4?EYj_J#0b^NF(e!G=pwcoR~W)|<;+v|cS z+7$M`*=^M}G3e2RV$G|jZ{=Uisx}w)Jr+6fe53uU=XW!5F0Z_NdDiXa@mI>EclIxP zd{b}&`>yYi*{WY}+-~%XxqtX-jxbAI5YOW?%+GA}a(}Ll`YyG3b@Gb4nNxY=4{hyF zcC?$ledoNCzQ5D2W{W9@Zr^fMGk0mwR{KZqCUDBmkXhfjzUo#;{ipINFDHK5+q(M^ z&%4adQ@_2v|2j=SY;rW(YHIfFn&a6;7b+j$n6Ue}wO+0F zF+BWf(&ZDUzVcc>;_jLn8}+O=K6GK9*jGlw#_jX-Hc#F1^laX)_F4Dl%h^5OZXCSU z+wE#**rj!`AwkJ2?=aM*?Kt=1gZQlLQTt8g{jS|F<$d6M!%F4Br^++yPH)Lxoo2b_ z@~cgrDx2n-g1#gQ{j> z%VK5t7kxc^;!d6t2aDGR&dKVnGgkZX6|C7K*sQrE!v2is#EBA$x!Kim=XS2oE)4Cr zpD}rN`L)W317+;7tAs6B&M~x}xwiS4tm|4i)k_O&J=gBPvTlXJ)-Sbeo6CQ%FAreo zsc${DXma#?yGs{DZg!Y$hdW3;zO#f3vEUq$h7f5b|Q?e{wLioatH{`$Z}?lc*rE-(X)KZ;uZ!C zO}?U8OaWdhYD}!B<}j@}5GfZB_M|VEfpN-Chg~f^EC&DakOyAWMFiRj_ zk-;fL@(=?W~t){;M@{OIL(m(Vo?BuW>l~q3GPVZ=+vN4_oz8z42iJr;X}2 zm(NUseg&N#ijEgAe(kyyda~HFRH`{Qs`%&Z{|wrjSHHZHc_DA}@{oUwif3i+uC z`f2s{58pPKeBXENP1Q7Wd0n@8((jfW^|Yg^W}7Qz1vGnUzv{rt|oK$>9^%a3^R z6_IypELUFMwY9=TtLDlao+kSx>(^}9yL46Lw!LzEGW@#btyjD<7q>nBDq9gbpLyx- z2ni2K(JjEA&4De@Jk}pW9x~Lbuo2SR9-Yzx-~E=d#dCbyZU{H!UvPc{XKM#ear<;?J*o z*Tz2zVOzY3(cVzjf7iQmv=`PDp@0|#^d zGZ@5Wnb>!!nCBNC;QsPTH0SQ(i@9^n_RY-T$&jfHofEpv?s0Wh;kmUtYSfRE$ga8< zHQP*l|A#lZK8|dKU!E5KXSi%~+e2#Fl54yCUWG{Sy&!(6#xw4==gy7m3%B;{SifyG zqXOG;i;vom-oFUmzJJG}^E0=s5^J_pUbH7{>C5^&-Rtx3zSGRsEoMoy@clTf8x;A6c z-k9QGsmk*v{&sxxA@U$*n{CG;0KeXiLaudzat-0w^dPY1Ce(Q&Et~vIf z;pC)`f^YBttaW{>?VIge`e9MIS?-NPH}Czu@mFeJ`NgeitZZ9O3Z1EYSZi3Ct*dq0 zFIVC9b#~j|n>3o|9>2r!SNyd6x6=`I-E)G)_b&gQ`byWTvv1zCYqdMs=03e^aX{p? z6~ixo?%VT@Lo|bx++Lta{_K1dyz2Ca}N7mY|DSg*f?np?6?YfewyZ79^>eYE!moo02 zlnd{#SnXrM@rmDi)k&4f^FQnnpSEny^`#2@XAVvX_lsmOTk=jgyLq;vbckeIT7cW7 zEf3Qi)dCFGE^?aI^hTRGrzKEQBV$7H6i=;LEbUlOE~5%vv4d@ROWOl zY~f_q5ClO+h9ADCEju0Vo{CxEz0-e>0>h_ODoQG;`k4-@oPUEHI)c3RXec^NnCQg2 zK#j>d4@^r~oTa`4ajwxVF-K?gxr$?-A5GIK|~yqO_v9kWudi%PEq%PZp%N+ zB&^{W(}J7Y2DMkWx41gFxT_jYnQPc0q_C4OCza>#@U2gxNbxPvT)B`r$%hs^4cWnP_rA{GDPLw-w8%Rziodvbo0aOn>%nyzAvPQD zsitQ}y?|HbC>nH|r+^V-^@Di`c3RU z|LxF)K}&PZdKd0cPL8cKIa2S@+4J?+dOn};Yg}sgUOn7*d+t~L>Gg;93r1yyY=8NF zt(TeEQge2bj*~Nw->p?`KYZ-cY`yi(uR;#5ys(t-`_GPlhrh;k=epf?-MKE`PHAy~ zLvrq|>ox{_pHF?tlFC24YwF>zY}w&v`#XgBviF*QNvfK+v^eJab$R)nIi*i`zI}Yv zwAbn5S5~)63tj&NE_r*NLH>Ec=SNlCQ?yK%C3${-S)%<%%R$-yYHq&Iqh4|6j+pg+ zTh%T5_!2YQ`MF>Gs#p>pI=$OmbK6^~_!T^24o3vmZp}Q9Q*$qjW#?K0wyFr*BTqQw zUS}rrFaA;XtX0UDYqIdbW3oySCRYUFpu$j9KTF?x;AI{72V}eV3>D z9ev5WJGL&FVJWaRH~4hhwf?ImPjBt5+OjDR-?xgB^2VZ`ZJ4gRD!*l*` zk7DZX>ebC%8MJHr*A?Qnv$yRK{A~Ve+3~$+jTL>K3E$D;%$%?+VZo&tVNPA{r?v}n ztdt9~aC@N2Yvjc#WXZM2WmQ>{a0iDm!$D2+oneoaM7ipfIBwgh83~SjopJROOh&8D!)oVS8C- zfgcAm*Blj9i53P<>zPwNA5vsuF8Se~(PEMq@Q6!UYk9WvsgtpZ6P7479*XpNXlAnG zfkMeMt~(B#x!KW&SezJ-ST51IVc;0WVG*(CZDor{<})tW#p|ZLFI3iSYV2XnIn~(e zFyjy}=R6U?8L9>k4+cxmJ*Dh$P{O$**;Ad7*MMi9_}SOLuAE)Fd*k6df^oUk5nu1* zzP-yMKcnwDdm)omh|iOiZ(UW?zk0AZvvl@dk`!$DyW69vOG@R)GWCAVX>V=*b(X(t zxaD%}Jay+1|99eNUsm6L&_A_RC`-HCdv*@Qjo9P|A)vB%MP_pS- z`&kpeeG-=uvJ|wO61;P5Zo#Q9TKjs#=Ge|@ox0NcX}qp>#&v0%r0!WUS{9Q}&T;&( zuis<6zvqQj_d>E)+3odza4C;_8>jlqQ_`zm-n#z5be7Y-CyCojHrTD>zP~DSe%p__ z=}V+Tzg>IJyGr`g?&^k#wiEXJ+?2N`%~bbea>v~F%MaYo;@`bvbz$u7RiTCYhD_l~ zd)*|Z)3sLzo$;Ta`n+~F=ezKArUIYyiynmDit%o*EuA{;jsyFb*yja65>ba(NO;qxc zjf#=E#F=~JKSY$U`zPVwckISYCia?-u2U>YepUgaxP9^Z~nS=RWfLz zZmoG<_N;{df@$lmK9+hHZo6A{asTB~d7t*c^}Y|>*{b5xSJhY+uew!dHkCeYU=A8Pl^@xaRJiKTo_gK%8uk8yKaF3sf{JGci0w$?_sZPRl)8H3+RW`+)@9wf6Hsg$K0V=G zp1=FnN6Y`1F+J=reQ{c}yQaowcU#Ggw})IBs-g|rzHX@u@u>DVVAC*L>cBwb8Cd2Ka!-v^e{?HA=Gqi)@fubQIy=c}}-l7js5sh@gRyh_fF<^86ebzOgL=z0CZ z;+?96ULWtxjMB|aZ8$$&c+SdX=Xw)?Wh)CUiq2Or={RyMJXbcF*KccU!0)VOoew77 z`0n@U<@M5-U*}D^drn%$M6Z6G?OpVH)}t9VlFwg0t@*a_<6Ng$f8V9s3bbv!_Z@Eh zz1;JV`Sz=?#J5G|@_sM9>ASJb|IGUZ@v1BTWEK~iY`K-zoMh{y`uyd?;+!wB!I3*e zv+`~SoL(Jj#PDY7-PGfU<9RO^Yc|)H3&aYu-}qo(v~y;uz~h(y8TjH}&0d$6Z8qEU z-hd@gshso*S97ypS#z&0~G_GtF$}asBCF=UKU{3=^%e*#qJdz6L(5CYDk<~dxWJa zSzW`pc=@Xtd4fS~0hhP~WDliC7&O?W-0@%&@V3{S!pE~{=FF+Ci%)S#`0nI9qQJo> zVA!gDli|z!2uCKzNi!VEMYvMH9LTDs$O}9#0+1BbPbk6Y}4m+@So=})ZlOm zo^tXKL%EhGqn%T9h)_(G(*hR70Qc6#NkW;8oE;Ax6pe2&-V*Sfa{Ey9cZnAB&6l`r z9x`*4Gv3-Z;R)x?kQEL0jFz(H<;<|r6e(`n`j$ohs^E+zoX^4<*g6;k7?e4bR9QDp zIc9t*W%79r#pSB$;h&jSH@EUjcd$HVkm7J;(PC!MI)9Tb_F%~6OL-|;$%&f+96xJr zIh2yzsKY0CkZnI;O&-~JFR!g@())bR-=YIV9VyV>9egP*A2jM9T zg}F2jywu&<@^`{zVGZ>yljn#`b(T?>e5m5~y9sYa4!%@wNl{}^VM@Ns#ICvYO~>S0 zOzBfEU);)e>-rbH@?GclpAKF7p_+T%we}4M+qbV*beMUtBcSwE;O-0gt!Kqg*RS8Up1WFC>uCyZz0-HAMt{2-1o+c$5#{z{1b#;>c~9TLi0xZ4j-N-5j6L^5c`N}sJ~3Z_k{gp7{&f(v*-K1Zo zZ~Cgm5)8J-@1`aE;PyH$aoIY0%G+lq&llSX=`3?(XFsDGbJEE0`$GrDs~p8Hm90rJ z8x67zz_6lx`8;AI7&S!4<{;<^Q zS8CL&uxs;`Idc@InM=2v4DIomujrwt+m)NCzVd0&MK@;8CqHCYsx%oZYF%A%JE%0f zoy+9l>3IqIGq2j|Cr4$xJfo-LxH_Od@jru>R4wn-yV_oAYtOn}UUG>+&(hxU*3Y@# zkKP_VbNQu%{jaG;b%&Q8D1V$EqZMkZ#dCO0!S>}pq}|x9|1%gzsb>ltoBwIqmEg@+ zN|Sys&o5?wwmRT|e9@|F&#wEf+@G*MB)P%mcm1i)F|W+cos;jm8Xh%!xcJSU zcgwzgo4@E)(^USytDLj_ub;nLoT|U7-i0erqor^POGm7%c(7IJg)~gQ>2)hr%l!UwB?Ax*@i<~ zm8S-3@Ci9uct{*#71V6%{N~s=p}}CH?5daQK8b9FQ@TC4&da^*c+2MIqU+8TW0!Jp zju-P?K@Hnf3r7P1uat`5TTB5)eW?lpCMvCL&5H7+N)D$MsR%Xgo}yymQ#gf7;PRyk zMMgosWzQwKX53w?6xuSkrzKcs)k}X(Np+3(KPRVJFF3T4VbL@XKQ84%g)J_YOPU%C zR@q*Xn9ywQVYDePeqQ+Et=)aMmUHNsRQPzfJ-*?~D8#dH@m*m?1{Ox))|olFKCH7l zG;F@k;PwmW4!+!4CT>vku$J-3dR@0IQ^F7L>`U3w)6|nzw7Y8CT;r&2pFW3}AQs_+ z9~L+j)huB+m3v;@R`F8TjFtn+%IhxFt$e4OnW4t77_eAqnx{`~u)U%B7RKijLYii> z?yzut6v8O^QjcFP@TB(^9%bP<`^@ z@Vn&Sa0ac#ptugt%J$G4(dHpAO*4pgmp6MY!YrB5* zIs{)^)3-X;cg?OP411Q{^KWl_eClnD_3HW+QQcMNnDz=X%zq|#>d4{^{?@U&k80M) zJUcn_*Q(;O`7^3DYS54+}yUX|-9yI>FQgi81(fsTCYNw?L_&J_;b)2hUv{(6K6U+KpWfs0rEdJYiw{gId}tiD)MrP{F@{D7?hBe5XEvm_<|ZdM zFfEpHm{fUu>gy$MH(qE}>^;!1HCcK_&x}(!Mvsd8?Sw5eh3dMx%YyVPbFGr6+Z^D2 zzE!{LZLZ_el#6l8^Xw`b&5IdD6COfaf;KR zLEd<2h+By9Q>{6v8ye*bL`xQK*81h38M0)Bz*9R_;Z|b~<~J=row%8N)DN_HOzHd} zAd|{?DU@yU8-dJYP7JCVr`$Po_uploGOLhth69T*$6G!&3kODx$_5+7<)`*8cs#|4 zk9|wt6BP-mmp+}RwEX-8c;<;R2(T4&xX4Yh-TnC>cibt3G%p_KIBTwbiI>)HoayYc zXi6VP@E&H?>}^~M6)wqog$Jhes(*2a?w_)pox^6?{h3Amj*A<$uRYu`|87FPp`1YF zecvSl?%Gec_L|%dF45E_TRU9d92x^X~}$v z58u9#PuhEZ+M}~qN^?!`|IAuHt?#AvFRsVyrcGR7cJZt3yBS$G?l5R?;AyxlyXSCBo+&46axm|*+%dry56&Flv~A1nQ?5xihVv41PA{JLJ+9e@`TDkyS-BH; zm7Vk8ZujAopPl)Tx8OE|hpes{#{JnceG1y`J>_nruQZm`l=+ z=#?$}$UC=N=9cSr(PIqxPxG(d{X3!b#>K7Y3q@|R#rVgEp4$Io)uDsymgn3*dhN{_ zgWBJ}^=Iz9@>Mr$!y=hS*CleblBEAL)Q4F6Z`<&n;qTT~ul>J6<{Ez05Pi0MW%kqq zYeRPMpZ496Tz9IT+3B+R*2aqe3>TNB-Y-7zpJ9*A>v(TpE0dd!?aO%ntv+eA;#>HY zRhDg28FXLF$w{vdJ{f2HP-h|Ylcz?Gub5_NI25#0t28cDQK(Tg+p^<0hXV6C0WX0T zb`K6o3!hI7YF>;j6Pn-JiCx`WCEvX2Wv$^S;Ty5vCAB|WTd$f{uCvzX@Qn0V`&~Ee zT3(xRx+|SeQjUxFaN)Z({3*8&6)PWFRAIB~PRNld&-kZ^a9DCmv@G&tQHlCsvmo$I z+a9I~g~p2B_2%{|Qnrso&OLje7Nr)

pDY7iZ|aV0ptw0&Yhbx#k|>3(8PPSrD^~ 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 LEMCK+@c$+Ndg3|Q literal 0 HcmV?d00001 From 6b55403334543492e691f84759f36f2698848764 Mon Sep 17 00:00:00 2001 From: fruchti Date: Sun, 4 Oct 2020 22:43:42 +0200 Subject: [PATCH 30/31] Add animation LUT generation script --- animation_lut.py | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100755 animation_lut.py diff --git a/animation_lut.py b/animation_lut.py new file mode 100755 index 0000000..0ad7c04 --- /dev/null +++ b/animation_lut.py @@ -0,0 +1,20 @@ +#!/usr/bin/env python + +from colorsys import hsv_to_rgb + +LUT_SIZE = 256 +SATURATION = 0.8 +VALUE = 1 +RESOLUTION = 12 # in bits + +print('const LED_Colour_t Animation_ColourLUT[] =') +print('{') +for i in range(LUT_SIZE): + h = i / LUT_SIZE + rgb = hsv_to_rgb(h, SATURATION, VALUE) + # Gamma-correct + rgb = tuple(pow(x, 2.2) for x in rgb) + # Scale to BCM resolution + (r, g, b) = tuple(round(2 ** RESOLUTION * x) for x in rgb) + print(' {{ .r = {}, .g = {}, .b = {} }},'.format(r, g, b)) +print('};') From 87b734cd93b1aedd04c1e059a61a3bd01cf8fbfa Mon Sep 17 00:00:00 2001 From: fruchti Date: Sun, 12 Mar 2023 13:37:43 +0100 Subject: [PATCH 31/31] Add larger photo for Gitea --- laurelin.jpg | Bin 45509 -> 65246 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/laurelin.jpg b/laurelin.jpg index bc29569fde80871b9b4178afcbac520c61f50f2e..a295deb2d3ca1042428164fed0d1c780a7d47246 100644 GIT binary patch delta 58553 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%0D0U`p@7q)u=nD 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 a?b2ul#!QvJUj8rEyD>05PhepFe-i-B8-_pt delta 38665 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 G|2F}3H~T^W