diff --git a/LICENCE_ISC.md b/LICENCE_ISC.md deleted file mode 100644 index b785b6e..0000000 --- a/LICENCE_ISC.md +++ /dev/null @@ -1,16 +0,0 @@ -# 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 deleted file mode 100644 index 428acea..0000000 --- a/README.md +++ /dev/null @@ -1,9 +0,0 @@ -# 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 - -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. diff --git a/animation_lut.py b/animation_lut.py deleted file mode 100755 index 0ad7c04..0000000 --- a/animation_lut.py +++ /dev/null @@ -1,20 +0,0 @@ -#!/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('};') diff --git a/build-number.txt b/build-number.txt index 3cda32f..a1f7f63 100644 --- a/build-number.txt +++ b/build-number.txt @@ -1 +1 @@ -515 +298 diff --git a/laurelin.jpg b/laurelin.jpg deleted file mode 100644 index a295deb..0000000 Binary files a/laurelin.jpg and /dev/null differ diff --git a/ld/common_nvs.ld b/ld/common.ld similarity index 87% rename from ld/common_nvs.ld rename to ld/common.ld index 91ea21c..9d9835a 100644 --- a/ld/common_nvs.ld +++ b/ld/common.ld @@ -12,15 +12,6 @@ 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_15k_flash_1k_nvs.ld b/ld/stm32f030f4_flash.ld similarity index 59% rename from ld/stm32f030f4_15k_flash_1k_nvs.ld rename to ld/stm32f030f4_flash.ld index 93724af..9fecb90 100644 --- a/ld/stm32f030f4_15k_flash_1k_nvs.ld +++ b/ld/stm32f030f4_flash.ld @@ -1,8 +1,7 @@ MEMORY { - flash (rx) : ORIGIN = 0x08000000, LENGTH = 15K - flash_nvstore (rw) : ORIGIN = 0x08003c00, LENGTH = 1K + flash (rx) : ORIGIN = 0x08000000, LENGTH = 16K sram (xrw) : ORIGIN = 0x20000000, LENGTH = 4K } -INCLUDE ld/common_nvs.ld +INCLUDE ld/common.ld diff --git a/makefile b/makefile index 6583cf1..f0849c5 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_15k_flash_1k_nvs.ld +LD_SCRIPT = ld/stm32f030f4_flash.ld ifeq ($(DEBUG),yes) DEBUG_FLAGS = -DDEBUG -g3 diff --git a/src/animation.c b/src/animation.c index 72dbb8b..fc3657f 100644 --- a/src/animation.c +++ b/src/animation.c @@ -1,107 +1,18 @@ #include "animation.h" -#include "animation_lut.h" -#include "light_sensor.h" -#include "nvs.h" +#include "led.h" -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) -{ - 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) >> LIGHTSENSOR_BITS; - colour.g = ((unsigned int)colour.g * brightness) >> LIGHTSENSOR_BITS; - colour.b = ((unsigned int)colour.b * brightness) >> LIGHTSENSOR_BITS; - - return colour; -} - -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) - / (LED_COUNT - 1); - LED_PixelData[Animation_LEDOrder[i]] = Animation_GetColour(step, - brightness); - } -} - -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(!Animation_FrameFlag) - { - return; - } - Animation_FrameFlag = false; - - NVS_Data->animation_step_bottom += ANIMATION_STEPS - / ANIMATION_CYCLE_TIME_BOTTOM - / ANIMATION_REFRESH_RATE; - NVS_Data->animation_step_top += ANIMATION_STEPS - / ANIMATION_CYCLE_TIME_TOP - / ANIMATION_REFRESH_RATE; - - NVS_Data->animation_step_bottom %= 2 * ANIMATION_STEPS; - NVS_Data->animation_step_top %= 2 * ANIMATION_STEPS; - - Animation_DrawGradient(NVS_Data->animation_step_bottom, - 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) -{ - Animation_FrameFlag = true; - TIM17->SR &= ~TIM_SR_UIF; -} + // 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 +}; \ No newline at end of file diff --git a/src/animation.h b/src/animation.h index 04e2a73..7b9637e 100644 --- a/src/animation.h +++ b/src/animation.h @@ -1,25 +1 @@ -#pragma once - -#include - -#include "led.h" - -#define ANIMATION_STEPS (1 << 24) - -#define ANIMATION_REFRESH_RATE 10 - -// 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) - -// 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); -void Animation_Poll(void); +#pragma once \ No newline at end of file diff --git a/src/animation_lut.c b/src/animation_lut.c deleted file mode 100644 index 04132a5..0000000 --- a/src/animation_lut.c +++ /dev/null @@ -1,261 +0,0 @@ -#include "animation_lut.h" - -const LED_Colour_t Animation_ColourLUT[] = -{ - { .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 deleted file mode 100644 index 79bb8dc..0000000 --- a/src/animation_lut.h +++ /dev/null @@ -1,5 +0,0 @@ -#pragma once - -#include "led.h" - -extern const LED_Colour_t Animation_ColourLUT[256]; diff --git a/src/led.c b/src/led.c index f644614..bad20ff 100644 --- a/src/led.c +++ b/src/led.c @@ -3,10 +3,7 @@ LED_Colour_t LED_PixelData[LED_COUNT] = {{0}}; 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) \ @@ -27,27 +24,6 @@ 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 @@ -57,7 +33,6 @@ 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, @@ -65,14 +40,6 @@ 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 @@ -91,11 +58,6 @@ 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. @@ -105,13 +67,15 @@ void LED_Commit(void) { for(int i = 0; i < LED_COLUMNS; i++) { - // 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]; + // 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; for(int j = 0; j < LED_BITS; j++) { - if(colour_value & (1 << j)) + if(gamma_corrected & (1 << j)) { LED_BackBuffer[r * (LED_BITS + 1) + j] &= ~(1 << LED_Pins[i]); @@ -144,7 +108,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 event + // DMA channel 3: Output data to port a on TIM3 update 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 +117,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 4: update TIM3 ARR on TIM3 compare 1 match + // DMA channel 3: Output data to port a on TIM3 update // 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]); @@ -251,7 +215,6 @@ void LED_Init(void) RCC->APB1ENR |= RCC_APB1ENR_TIM3EN; // Fill both DMA buffers - LED_QueuePageFlip = false; LED_Commit(); LED_PageFlip(); LED_Commit(); @@ -281,53 +244,6 @@ void LED_Init(void) LED_StartBCM(0); } -void LED_Suspend(void) -{ - if(LED_Suspended) - { - return; - } - - 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; - - LED_Suspended = true; -} - -void LED_WakeUp(void) -{ - if(!LED_Suspended) - { - return; - } - - LED_Init(); - - LED_Suspended = false; -} - void DMA1_Channel2_3_IRQHandler(void) { // Interrupt when all bits have been sent @@ -352,12 +268,6 @@ 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 083bc1d..83833c2 100644 --- a/src/led.h +++ b/src/led.h @@ -5,19 +5,17 @@ #include "stm32f030x6.h" #include "pinning.h" -#define LED_BITS 12 // BCM resolution in bits +#define LED_BITS 12 #define LED_ROWS 8 // Rows are driven by a shift register -#define LED_COLUMNS 12 // Columns are driven by the MCU directly. - // Set to 9 to free the SWD pins and enable - // debugging. +#define LED_COLUMNS 12 // Columns are driven by the MCU directly #define LED_COUNT (LED_ROWS * LED_COLUMNS / 3) typedef struct { - uint16_t r; - uint16_t g; - uint16_t b; -} __attribute__((packed, aligned(2))) LED_Colour_t; + uint8_t r; + uint8_t g; + uint8_t b; +} __attribute__((packed)) LED_Colour_t; // Pixel data, not displayed until LED_Commit() is called extern LED_Colour_t LED_PixelData[LED_COUNT]; @@ -29,11 +27,6 @@ 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/light_sensor.c b/src/light_sensor.c index ebdfb95..6f0d8e1 100644 --- a/src/light_sensor.c +++ b/src/light_sensor.c @@ -4,9 +4,13 @@ volatile unsigned int LightSensor_Measurement; volatile bool LightSensor_NewMeasurement = false; // Rolling average of the brightness measurement -unsigned int LightSensor_AbsoluteBrightness = 0; +float LightSensor_AbsoluteBrightness = 0.5f; -int LightSensor_RelativeBrightness; +// Maximum and minimum encountered so far +float LightSensor_MinimumBrightness = 1.0f; +float LightSensor_MaximumBrightness = 0.0f; + +float LightSensor_RelativeBrightness; static void LightSensor_Measure(void) { @@ -62,25 +66,35 @@ void LightSensor_Poll(void) { unsigned int measurement = LightSensor_Measurement; LightSensor_NewMeasurement = false; - unsigned int brightness = ((1 << 31) - / (measurement + 1)) >> (31 - 16); - LightSensor_AbsoluteBrightness -= LightSensor_AbsoluteBrightness - >> LIGHTSENSOR_LAMBDA_BITS; - LightSensor_AbsoluteBrightness += brightness - >> LIGHTSENSOR_LAMBDA_BITS; + float brightness = 65535.0f / measurement; + LightSensor_AbsoluteBrightness = LIGHTSENSOR_LAMBDA * LightSensor_AbsoluteBrightness + + (1.0f - LIGHTSENSOR_LAMBDA) * brightness; - LightSensor_RelativeBrightness = - ((int)LightSensor_AbsoluteBrightness - LIGHTSENSOR_LOW_BOUND) - * LIGHTSENSOR_MAX - / (LIGHTSENSOR_HIGH_BOUND - LIGHTSENSOR_LOW_BOUND); - - if(LightSensor_RelativeBrightness < 0) + if(LightSensor_AbsoluteBrightness < LightSensor_MinimumBrightness) { - LightSensor_RelativeBrightness = 0; + LightSensor_MinimumBrightness = LightSensor_AbsoluteBrightness; } - if(LightSensor_RelativeBrightness > LIGHTSENSOR_MAX) + if(LightSensor_AbsoluteBrightness > LightSensor_MaximumBrightness) { - LightSensor_RelativeBrightness = LIGHTSENSOR_MAX; + LightSensor_MaximumBrightness = LightSensor_AbsoluteBrightness; + } + + // Scale and saturate to get relative brightness value + float range = LightSensor_MaximumBrightness + - LightSensor_MinimumBrightness; + float low = LightSensor_MinimumBrightness + + range * LIGHTSENSOR_LOW_BOUND; + float high = LightSensor_MinimumBrightness + + range * LIGHTSENSOR_HIGH_BOUND; + LightSensor_RelativeBrightness = (LightSensor_AbsoluteBrightness - low) + / (high - low); + if(LightSensor_RelativeBrightness < 0.0f) + { + LightSensor_RelativeBrightness = 0.0f; + } + if(LightSensor_RelativeBrightness > 1.0f) + { + LightSensor_RelativeBrightness = 1.0f; } } } @@ -98,4 +112,4 @@ void TIM14_IRQHandler(void) LightSensor_NewMeasurement = true; LightSensor_Measure(); -} +} \ No newline at end of file diff --git a/src/light_sensor.h b/src/light_sensor.h index cd56d74..4ae31cc 100644 --- a/src/light_sensor.h +++ b/src/light_sensor.h @@ -6,21 +6,17 @@ #include "pinning.h" // ADC polling interval in milliseconds -#define LIGHTSENSOR_INTERVAL 500 - -// Resolution of the brightness output -#define LIGHTSENSOR_BITS 12 -#define LIGHTSENSOR_MAX ((1 << LIGHTSENSOR_BITS) - 1) +#define LIGHTSENSOR_INTERVAL 250 // 'Forgetting factor' of the rolling brightness average -#define LIGHTSENSOR_LAMBDA_BITS 2 +#define LIGHTSENSOR_LAMBDA 0.9f -// Bounds for converting absolute to relative brightness (empirically -// determined) -#define LIGHTSENSOR_LOW_BOUND 5 -#define LIGHTSENSOR_HIGH_BOUND ((int)(0.85 * LIGHTSENSOR_MAX)) +// 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 -extern int LightSensor_RelativeBrightness; +extern float LightSensor_RelativeBrightness; void LightSensor_Init(void); void LightSensor_Poll(void); diff --git a/src/main.c b/src/main.c index d921b37..781c397 100644 --- a/src/main.c +++ b/src/main.c @@ -5,37 +5,74 @@ 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++) { LightSensor_Poll(); } - NVS_Load(); LED_Init(); - Animation_Init(); + unsigned int counter = 0; + const unsigned int blink_period = 1000; + const unsigned int group_size = 6; while(1) { __WFI(); LightSensor_Poll(); - Animation_Poll(); - if(LightSensor_RelativeBrightness == 0 && !powered_down) + if(LED_FrameFlag) { - LED_Suspend(); - NVS_Save(true); - powered_down = true; - } - if(powered_down && LightSensor_RelativeBrightness > 0) - { - powered_down = false; - NVS_Save(true); - LED_WakeUp(); + 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(); } } return 0; } + diff --git a/src/main.h b/src/main.h index 48e0d25..48ce191 100644 --- a/src/main.h +++ b/src/main.h @@ -1,13 +1,11 @@ #pragma once #include -#include #include "stm32f030x6.h" #include "buildid.h" #include "led.h" #include "light_sensor.h" -#include "animation.h" -#include "nvs.h" int main(void); + diff --git a/src/nvs.c b/src/nvs.c deleted file mode 100644 index 7adcd73..0000000 --- a/src/nvs.c +++ /dev/null @@ -1,204 +0,0 @@ -#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, aligned(2))) -{ - // 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"))) -NVS_Block_t NVS_Area[NVS_BLOCK_COUNT]; - -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(unsigned int i = 0; i < sizeof(NVS_Data_t); i++) - { - CRC->DR = ((uint8_t*)(data))[i]; - } - return CRC->DR; -} - -static bool NVS_ProgramHalfWord(uint16_t *dest, uint16_t value) -{ - FLASH->CR = FLASH_CR_PG; - *(uint16_t*)dest = value; - while(FLASH->SR & FLASH_SR_BSY); - if(*dest != value) - { - // Write failed - FLASH->CR = 0x00000000; - return false; - } - FLASH->CR = 0x00000000; - return true; -} - -static void NVS_UnlockFlash(void) -{ - while(FLASH->SR & FLASH_SR_BSY); - if(FLASH->CR & FLASH_CR_LOCK) - { - FLASH->KEYR = 0x45670123; - FLASH->KEYR = 0xcdef89ab; - } -} - -static bool NVS_EraseArea(void) -{ - for(unsigned 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 - FLASH->CR = 0x00000000; - return false; - } - FLASH->CR = 0x00000000; - } - return true; -} - -static bool NVS_BlockEmpty(NVS_Block_t *block) -{ - for(unsigned 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; - - NVS_Block_t *block = NULL; - - // Find valid block - for(unsigned 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; - } -} - -bool NVS_Save(bool erase_if_needed) -{ - 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)) - { - if(!erase_if_needed) - { - return false; - } - - if(!NVS_EraseArea()) - { - return false; - } - 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(unsigned int i = 0; i < sizeof(NVS_Block_t) / 2; i++) - { - if(!NVS_ProgramHalfWord((uint16_t*)next_block + i, - *((uint16_t*)&NVS_RAMData + i))) - { - return false; - } - } - - if(current_block != NULL) - { - 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 deleted file mode 100644 index a26f8e6..0000000 --- a/src/nvs.h +++ /dev/null @@ -1,26 +0,0 @@ -#pragma once - -#include - -#include "stm32f030x6.h" - -typedef struct -__attribute__((packed, aligned(2))) -{ - 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. 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); - - diff --git a/src/system.c b/src/system.c index 0a64ee1..7ca3df8 100644 --- a/src/system.c +++ b/src/system.c @@ -6,3 +6,4 @@ void SystemInit(void) // Disable all interrupts RCC->CIR = 0x00000000; } +