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