diff --git a/Core/Clock/clock_manager.c b/Core/Clock/clock_manager.c index cb7fc0a..54f6a06 100644 --- a/Core/Clock/clock_manager.c +++ b/Core/Clock/clock_manager.c @@ -3,6 +3,7 @@ #include "rtc.h" static uint8_t dutyValue = 5; +static uint8_t fadeValue = 5; static time_t currentTime; static uint8_t led_enable; RTC_TimeTypeDef rtc_time; @@ -12,6 +13,7 @@ RTC_TimeTypeDef rtc_time; #define BKP_DR_POWERON_SONG RTC_BKP_DR5 #define BKP_DR_ALARM_SONG RTC_BKP_DR6 #define BKP_DR_MENU_SOUND RTC_BKP_DR3 +#define BKP_DR_FADE RTC_BKP_DR4 // Яркость в RTC Backup Register (используем BKP_DR1) static void SaveDuty(void) { @@ -23,9 +25,20 @@ static void LoadDuty(void) { dutyValue = (val <= 10 && val > 0) ? (uint8_t)val : 5; } +static void SaveFade(void) { + HAL_RTCEx_BKUPWrite(&hrtc, BKP_DR_FADE, fadeValue); +} + +static void LoadFade(void) { + uint32_t val = HAL_RTCEx_BKUPRead(&hrtc, BKP_DR_FADE); + fadeValue = (val <= 10) ? (uint8_t)val : 5; +} + void ClockManager_Init(void) { LoadDuty(); + LoadFade(); Segment_SetBrightness(dutyValue * 10); + Segment_SetFade(fadeValue); // Если RTC не инициализирован - сброс времени if (HAL_RTC_GetTime(&hrtc, &rtc_time, RTC_FORMAT_BIN) != HAL_OK) { @@ -77,6 +90,17 @@ void ClockManager_SetDuty(uint8_t value) { SaveDuty(); } +uint8_t ClockManager_GetFade(void) { + return fadeValue; +} + +void ClockManager_SetFade(uint8_t value) { + if (value > 10) value = 10; + fadeValue = value; + Segment_SetFade(fadeValue); + SaveFade(); +} + void ClockManager_ResetTime(void) { ClockManager_SetTime(0, 0, 0); } diff --git a/Core/Clock/clock_manager.h b/Core/Clock/clock_manager.h index 211421f..cafa012 100644 --- a/Core/Clock/clock_manager.h +++ b/Core/Clock/clock_manager.h @@ -20,6 +20,10 @@ uint8_t ClockManager_GetDuty(void); // Установить яркость (0-10) и сохранить void ClockManager_SetDuty(uint8_t value); +// Fade level: 0 - instant, 10 - slowest +uint8_t ClockManager_GetFade(void); +void ClockManager_SetFade(uint8_t value); + // Сброс времени на 00:00:00 void ClockManager_ResetTime(void); diff --git a/Core/Clock/segment.c b/Core/Clock/segment.c index fab55fa..7ba9627 100644 --- a/Core/Clock/segment.c +++ b/Core/Clock/segment.c @@ -4,6 +4,12 @@ // ==================== Конфигурация ==================== #define DIGITS_COUNT 6 // Количество разрядов #define PROCESS_INTERVAL_US 10 // Интервал вызова Segment_Process в микросекундах +#define SEGMENT_FADE_INTERVAL_US 20000 +#define SEGMENT_FADE_DEFAULT 5 +#define SEGMENT_FADE_MAX 10 +#define SEGMENT_FADE_STEPS_PER_LEVEL 5 +#define SEGMENT_BRIGHTNESS_MAX 100 +#define SEGMENT_FADE_INTERVAL_TICKS (SEGMENT_FADE_INTERVAL_US / PROCESS_INTERVAL_US) volatile uint32_t REFRESH_RATE = 80; // Частота обновления всех цифр в Гц (можно менять) volatile uint8_t GLOBAL_BRIGHTNESS = 100; // Глобальная яркость в процентах (0-100) @@ -74,11 +80,18 @@ static const CharMap charTable[] = { // ==================== Статические переменные ==================== static uint8_t displayBuffer[DIGITS_COUNT]; // Буфер СРАЗУ МАСОК сегментов +static uint8_t segmentBrightness[DIGITS_COUNT][7]; +static uint8_t segmentTargetBrightness[DIGITS_COUNT][7]; static uint8_t currentPos; static uint32_t pwmCounter; static uint32_t currentDigitTime; -static uint32_t currentPwmThreshold; +static uint32_t currentSegmentThreshold[7]; +static uint32_t fadeCounter; +static uint8_t fadeLevel = SEGMENT_FADE_DEFAULT; +static uint8_t fadeStepPercent = SEGMENT_BRIGHTNESS_MAX / + (SEGMENT_FADE_DEFAULT * SEGMENT_FADE_STEPS_PER_LEVEL); +static uint8_t lastOutputMask; // ==================== Низкий уровень ==================== static void SetSegments(uint8_t mask) { @@ -100,6 +113,73 @@ static uint8_t GetCharMask(char c) { return 0xFF; // если символ неизвестен — пусто } +static uint8_t GetSegmentTarget(uint8_t mask, uint8_t segment) { + return ((mask >> segment) & 1) ? 0 : SEGMENT_BRIGHTNESS_MAX; +} + +static void SetDigitTargetMask(uint8_t pos, uint8_t mask) { + if (pos >= DIGITS_COUNT) return; + if (displayBuffer[pos] == mask) return; + + displayBuffer[pos] = mask; + for (uint8_t segment = 0; segment < 7; segment++) { + segmentTargetBrightness[pos][segment] = GetSegmentTarget(mask, segment); + } +} + +static void UpdateFadeLevels(void) { + uint32_t interval = SEGMENT_FADE_INTERVAL_TICKS; + if (interval < 1) interval = 1; + + fadeCounter++; + if (fadeCounter < interval) return; + fadeCounter = 0; + + for (uint8_t pos = 0; pos < DIGITS_COUNT; pos++) { + for (uint8_t segment = 0; segment < 7; segment++) { + uint8_t current = segmentBrightness[pos][segment]; + uint8_t target = segmentTargetBrightness[pos][segment]; + uint8_t step = fadeStepPercent; + if (step < 1) step = 1; + + if (current < target) { + uint8_t next = current + step; + segmentBrightness[pos][segment] = (next > target) ? target : next; + } else if (current > target) { + segmentBrightness[pos][segment] = + (current > step + target) ? (current - step) : target; + } + } + } +} + +static void UpdateCurrentSegmentThresholds(void) { + for (uint8_t segment = 0; segment < 7; segment++) { + uint32_t threshold = currentDigitTime * + (uint32_t)GLOBAL_BRIGHTNESS * + segmentBrightness[currentPos][segment]; + threshold /= (100UL * SEGMENT_BRIGHTNESS_MAX); + + if (threshold > currentDigitTime) threshold = currentDigitTime; + currentSegmentThreshold[segment] = threshold; + } +} + +static uint8_t BuildCurrentDigitMask(void) { + uint8_t mask = 0xFF; + + for (uint8_t segment = 0; segment < 7; segment++) { + if (pwmCounter < currentSegmentThreshold[segment]) { + mask &= (uint8_t)~(1U << segment); + } + } + + if (currentPos == 5) + mask = SWAP_BIT5_BIT6(mask); + + return mask; +} + // ==================== Управление разрядами ==================== void DisableAllDigits(void) { SET_SEGMENT_A(1); SET_SEGMENT_B(1); SET_SEGMENT_C(1); @@ -138,30 +218,29 @@ static void NextDigit(void) { currentDigitTime = totalCalls / DIGITS_COUNT; if (currentDigitTime < 1) currentDigitTime = 1; - currentPwmThreshold = (currentDigitTime * GLOBAL_BRIGHTNESS) / 100; - pwmCounter = 0; + lastOutputMask = 0xFF; } static void UpdateOneDigit(void) { + uint8_t mask; + if (pwmCounter == 0) { DisableAllDigits(); __NOP(); __NOP(); // анти-гостинг - uint8_t mask = displayBuffer[currentPos]; - - // SWAP только для позиции 5 - if (currentPos == 5) - mask = SWAP_BIT5_BIT6(mask); - + UpdateCurrentSegmentThresholds(); + mask = BuildCurrentDigitMask(); SetSegments(mask); + lastOutputMask = mask; EnableDigit(currentPos); - } - - if (pwmCounter >= currentPwmThreshold) { - DisableAllDigits(); - SetSegments(0xFF); + } else { + mask = BuildCurrentDigitMask(); + if (mask != lastOutputMask) { + SetSegments(mask); + lastOutputMask = mask; + } } pwmCounter++; @@ -178,10 +257,19 @@ void Segment_Init(void) { currentPos = 0; pwmCounter = 0; currentDigitTime = 1; - currentPwmThreshold = 1; + fadeCounter = 0; + lastOutputMask = 0xFF; - for (int i = 0; i < DIGITS_COUNT; i++) + for (int i = 0; i < DIGITS_COUNT; i++) { displayBuffer[i] = 0xFF; // пусто + for (int segment = 0; segment < 7; segment++) { + segmentBrightness[i][segment] = 0; + segmentTargetBrightness[i][segment] = 0; + } + } + for (int segment = 0; segment < 7; segment++) { + currentSegmentThreshold[segment] = 0; + } NextDigit(); } @@ -189,22 +277,22 @@ void Segment_Init(void) { // Установить символ в разряд void Segment_SetChar(uint8_t pos, char c) { if (pos >= DIGITS_COUNT) return; - displayBuffer[pos] = GetCharMask(c); + SetDigitTargetMask(pos, GetCharMask(c)); } // Установить напрямую маску сегментов void Segment_SetRaw(uint8_t pos, uint8_t mask) { if (pos >= DIGITS_COUNT) return; - displayBuffer[pos] = mask; + SetDigitTargetMask(pos, mask); } // Установить строку (например "HELLO ") void Segment_SetString(const char *str) { for (int i = 0; i < DIGITS_COUNT; i++) { if (str[i] == 0) - displayBuffer[i] = 0xFF; + SetDigitTargetMask(i, 0xFF); else - displayBuffer[i] = GetCharMask(str[i]); + SetDigitTargetMask(i, GetCharMask(str[i])); } } @@ -215,8 +303,31 @@ void Segment_SetBrightness(uint8_t percent) { GLOBAL_BRIGHTNESS = percent; } +void Segment_SetFade(uint8_t level) { + if (level > SEGMENT_FADE_MAX) level = SEGMENT_FADE_MAX; + fadeLevel = level; + + if (fadeLevel == 0) { + fadeStepPercent = SEGMENT_BRIGHTNESS_MAX; + for (uint8_t pos = 0; pos < DIGITS_COUNT; pos++) { + for (uint8_t segment = 0; segment < 7; segment++) { + segmentBrightness[pos][segment] = segmentTargetBrightness[pos][segment]; + } + } + } else { + fadeStepPercent = SEGMENT_BRIGHTNESS_MAX / + (fadeLevel * SEGMENT_FADE_STEPS_PER_LEVEL); + if (fadeStepPercent < 1) fadeStepPercent = 1; + } +} + +uint8_t Segment_GetFade(void) { + return fadeLevel; +} + // Основная функция обновления дисплея // Вызывается каждые 10 микросекунд из таймера void Segment_Process(void) { + UpdateFadeLevels(); UpdateOneDigit(); } diff --git a/Core/Clock/segment.h b/Core/Clock/segment.h index 98c25b3..0480e00 100644 --- a/Core/Clock/segment.h +++ b/Core/Clock/segment.h @@ -26,6 +26,10 @@ void Segment_SetTime(uint8_t hours, uint8_t minutes, uint8_t seconds); // Установка глобальной яркости (0-100%) void Segment_SetBrightness(uint8_t percent); +// Fade level: 0 - instant, 10 - slowest +void Segment_SetFade(uint8_t level); +uint8_t Segment_GetFade(void); + // ==================== Обновление дисплея ==================== // Основная функция обновления дисплея // Вызывается каждые PROCESS_INTERVAL_US микросекунд или из таймера diff --git a/Core/Menu/settings.c b/Core/Menu/settings.c index e75c6df..6c00ff3 100644 --- a/Core/Menu/settings.c +++ b/Core/Menu/settings.c @@ -16,6 +16,8 @@ typedef struct { static TimeEditData g_timeData; static uint8_t g_originalDuty; static uint8_t g_editDuty; +static uint8_t g_originalFade; +static uint8_t g_editFade; /////// TIME EDIT //////// static void Display_TimeEdit(void) { @@ -189,6 +191,54 @@ static void DutyEdit_OnButton(Button_Type btn, bool longPress) { } +/////// FADE EDIT //////// +static void Display_FadeEdit(void) { + char buf[7] = "FADE "; + if (g_editFade == 10) { + buf[4] = '1'; + buf[5] = '0'; + } else { + buf[5] = '0' + g_editFade; + } + Segment_SetString(buf); +} + +static void OnEnter_FadeEdit(void) { + g_originalFade = ClockManager_GetFade(); + g_editFade = g_originalFade; + Segment_SetFade(g_editFade); +} + +static void FadeEdit_OnButton(Button_Type btn, bool longPress) { + (void)longPress; + switch (btn) { + case BUTTON_UP: + if (g_editFade < 10) { + g_editFade++; + Segment_SetFade(g_editFade); + Menu_Refresh(); + } + break; + case BUTTON_DOWN: + if (g_editFade > 0) { + g_editFade--; + Segment_SetFade(g_editFade); + Menu_Refresh(); + } + break; + case BUTTON_SELECT: + ClockManager_SetFade(g_editFade); + Menu_GoBack(); + break; + case BUTTON_BACK: + ClockManager_SetFade(g_originalFade); + Menu_GoBack(); + default: + break; + } +} + + /////// RESET //////// @@ -201,6 +251,7 @@ static void Reset_OnButton(Button_Type btn, bool longPress) { if ((btn == BUTTON_SELECT) && longPress) { ClockManager_ResetTime(); ClockManager_SetDuty(5); + ClockManager_SetFade(5); Menu_GoBack(); } } @@ -354,6 +405,19 @@ MenuNode g_dutyEditNode = { .data = &g_editDuty }; +MenuNode g_fadeEditNode = { + .name = "FADE", + .parent = &g_settingsNode, + .children = NULL, + .childCount = 0, + .selectedChild = 0, + .display = Display_FadeEdit, + .onEnter = OnEnter_FadeEdit, + .onUpdate = NULL, + .onButton = FadeEdit_OnButton, + .data = &g_editFade +}; + MenuNode g_LEDEditNode = { .name = "LED", .parent = &g_settingsNode, @@ -422,6 +486,7 @@ MenuNode g_resetNode = { MenuNode* g_settingsChildren[] = { &g_timeEditNode, &g_dutyEditNode, + &g_fadeEditNode, &g_LEDEditNode, &g_MenuSoundNode, &g_PowerOnSongNode, diff --git a/Core/Menu/settings.h b/Core/Menu/settings.h index 739f43c..ce50366 100644 --- a/Core/Menu/settings.h +++ b/Core/Menu/settings.h @@ -5,11 +5,12 @@ extern MenuNode g_timeEditNode; extern MenuNode g_dutyEditNode; +extern MenuNode g_fadeEditNode; extern MenuNode g_LEDEditNode; extern MenuNode g_MenuSoundNode; extern MenuNode g_PowerOnSongNode; extern MenuNode g_AlarmSongNode; extern MenuNode g_resetNode; -extern MenuNode* g_settingsChildren[7]; +extern MenuNode* g_settingsChildren[8]; #endif diff --git a/MDK-ARM/lamp.uvoptx b/MDK-ARM/lamp.uvoptx index 19c0a76..e5aedb9 100644 --- a/MDK-ARM/lamp.uvoptx +++ b/MDK-ARM/lamp.uvoptx @@ -120,7 +120,7 @@ 0 ST-LINKIII-KEIL_SWO - -U-O206 -O206 -SF10000 -C0 -A0 -I0 -HNlocalhost -HP7184 -P1 -N00("ARM CoreSight SW-DP (ARM Core") -D00(1BA01477) -L00(0) -TO131090 -TC10000000 -TT10000000 -TP21 -TDS8007 -TDT0 -TDC1F -TIEFFFFFFFF -TIP8 -FO7 -FD20000000 -FC1000 -FN1 -FF0STM32F10x_128.FLM -FS08000000 -FL020000 -FP0($$Device:STM32F103C8$Flash\STM32F10x_128.FLM) -WA0 -WE0 -WVCE4 -WS2710 -WM0 -WP2 + -U37FF71064E57343634C31443 -O206 -SF10000 -C0 -A0 -I0 -HNlocalhost -HP7184 -P1 -N00("ARM CoreSight SW-DP (ARM Core") -D00(1BA01477) -L00(0) -TO131090 -TC10000000 -TT10000000 -TP21 -TDS8007 -TDT0 -TDC1F -TIEFFFFFFFF -TIP8 -FO7 -FD20000000 -FC1000 -FN1 -FF0STM32F10x_128.FLM -FS08000000 -FL020000 -FP0($$Device:STM32F103C8$Flash\STM32F10x_128.FLM) -WA0 -WE0 -WVCE4 -WS2710 -WM0 -WP2 0 @@ -148,7 +148,24 @@ (105=-1,-1,-1,-1,0) - + + + 0 + 0 + 210 + 1 +
0
+ 0 + 0 + 0 + 0 + 0 + 0 + ..\Core\Menu\settings.c + + +
+
0 diff --git a/MDK-ARM/lamp.uvprojx b/MDK-ARM/lamp.uvprojx index 6b99bea..4feed40 100644 --- a/MDK-ARM/lamp.uvprojx +++ b/MDK-ARM/lamp.uvprojx @@ -10,8 +10,9 @@ lamp 0x4 ARM-ADS - 5060960::V5.06 update 7 (build 960)::.\ARMCC - 0 + 6190000::V6.19::ARMCLANG + 6190000::V6.19::ARMCLANG + 1 STM32F103C8 @@ -314,7 +315,7 @@ 1 - 1 + 3 0 0 1 @@ -323,7 +324,7 @@ 0 0 0 - 2 + 3 0 0 1