Files
Lamps/Core/Clock/segment.c

334 lines
11 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#include "segment.h"
#include "main.h"
// ==================== Конфигурация ====================
#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)
// Маска переназначения: новый_бит = старый_бит
// Так как перепутаны бит5 (F) и бит6 (G), меняем их местами
#define SWAP_BIT5_BIT6(x) (((x) & 0x9F) | (((x) & 0x20) << 1) | (((x) & 0x40) >> 1))
// Макросы для быстрой работы с пинами (вместо HAL_GPIO_WritePin)
#define SET_SEGMENT_A(val) SEGMENT_A_GPIO_Port->BSRR = ((uint32_t)SEGMENT_A_Pin << 16) | ((val) ? 0 : SEGMENT_A_Pin)
#define SET_SEGMENT_B(val) SEGMENT_B_GPIO_Port->BSRR = ((uint32_t)SEGMENT_B_Pin << 16) | ((val) ? 0 : SEGMENT_B_Pin)
#define SET_SEGMENT_C(val) SEGMENT_C_GPIO_Port->BSRR = ((uint32_t)SEGMENT_C_Pin << 16) | ((val) ? 0 : SEGMENT_C_Pin)
#define SET_SEGMENT_D(val) SEGMENT_D_GPIO_Port->BSRR = ((uint32_t)SEGMENT_D_Pin << 16) | ((val) ? 0 : SEGMENT_D_Pin)
#define SET_SEGMENT_E(val) SEGMENT_E_GPIO_Port->BSRR = ((uint32_t)SEGMENT_E_Pin << 16) | ((val) ? 0 : SEGMENT_E_Pin)
#define SET_SEGMENT_F(val) SEGMENT_F_GPIO_Port->BSRR = ((uint32_t)SEGMENT_F_Pin << 16) | ((val) ? 0 : SEGMENT_F_Pin)
#define SET_SEGMENT_G(val) SEGMENT_G_GPIO_Port->BSRR = ((uint32_t)SEGMENT_G_Pin << 16) | ((val) ? 0 : SEGMENT_G_Pin)
// ==================== Таблица символов ====================
// Для общего анода: 0 - сегмент горит, 1 - сегмент не горит
// Биты: A B C D E F G
typedef struct {
char c;
uint8_t mask;
} CharMap;
static const CharMap charTable[] = {
// ==================== Цифры ====================
{'0', 0xC0},
{'1', 0xF9},
{'2', 0xA4},
{'3', 0xB0},
{'4', 0x99},
{'5', 0x92},
{'6', 0x82},
{'7', 0xF8},
{'8', 0x80},
{'9', 0x90},
// ==================== Буквы ====================
{'A', 0x88}, {'a', 0x88},
{'B', 0x83}, {'b', 0x83},
{'C', 0xC6}, {'c', 0xC6},
{'D', 0xA1}, {'d', 0xA1},
{'E', 0x86}, {'e', 0x86},
{'F', 0x8E}, {'f', 0x8E},
{'G', 0xC2}, {'g', 0xC2},
{'H', 0x89}, {'h', 0x89},
{'I', 0xF9}, {'i', 0xF9},
{'J', 0xF1}, {'j', 0xF1},
{'L', 0xC7}, {'l', 0xC7},
{'N', 0xAB}, {'n', 0xAB},
{'O', 0xC0}, {'o', 0xC0},
{'P', 0x8C}, {'p', 0x8C},
{'Q', 0x98}, {'q', 0x98},
{'R', 0xAF}, {'r', 0xAF},
{'S', 0x92}, {'s', 0x92},
{'T', 0x87}, {'t', 0x87},
{'U', 0xC1}, {'u', 0xC1},
{'Y', 0x91}, {'y', 0x91},
// ==================== Символы ====================
{'-', 0xBF},
{'_', 0xF7},
{' ', 0xFF}
};
#define CHAR_TABLE_SIZE (sizeof(charTable)/sizeof(CharMap))
// ==================== Статические переменные ====================
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 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) {
SET_SEGMENT_A((mask >> 0) & 1);
SET_SEGMENT_B((mask >> 1) & 1);
SET_SEGMENT_C((mask >> 2) & 1);
SET_SEGMENT_D((mask >> 3) & 1);
SET_SEGMENT_E((mask >> 4) & 1);
SET_SEGMENT_F((mask >> 5) & 1);
SET_SEGMENT_G((mask >> 6) & 1);
}
// Получить маску по символу
static uint8_t GetCharMask(char c) {
for (int i = 0; i < CHAR_TABLE_SIZE; i++) {
if (charTable[i].c == c)
return charTable[i].mask;
}
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);
SET_SEGMENT_D(1); SET_SEGMENT_E(1); SET_SEGMENT_F(1);
SET_SEGMENT_G(1);
DIGIT_HOUR_H_GPIO_Port->BSRR = DIGIT_HOUR_H_Pin << 16;
DIGIT_HOUR_L_GPIO_Port->BSRR = DIGIT_HOUR_L_Pin << 16;
DIGIT_MIN_H_GPIO_Port->BSRR = DIGIT_MIN_H_Pin << 16;
DIGIT_MIN_L_GPIO_Port->BSRR = DIGIT_MIN_L_Pin << 16;
DIGIT_SEC_H_GPIO_Port->BSRR = DIGIT_SEC_H_Pin << 16;
DIGIT_SEC_L_GPIO_Port->BSRR = DIGIT_SEC_L_Pin << 16;
}
void EnableDigit(uint8_t pos) {
switch(pos) {
case 0: DIGIT_HOUR_H_GPIO_Port->BSRR = DIGIT_HOUR_H_Pin; break;
case 1: DIGIT_HOUR_L_GPIO_Port->BSRR = DIGIT_HOUR_L_Pin; break;
case 2: DIGIT_MIN_H_GPIO_Port->BSRR = DIGIT_MIN_H_Pin; break;
case 3: DIGIT_MIN_L_GPIO_Port->BSRR = DIGIT_MIN_L_Pin; break;
case 4: DIGIT_SEC_H_GPIO_Port->BSRR = DIGIT_SEC_H_Pin; break;
case 5: DIGIT_SEC_L_GPIO_Port->BSRR = DIGIT_SEC_L_Pin; break;
}
}
// ==================== Логика ====================
static void NextDigit(void) {
currentPos++;
if (currentPos >= DIGITS_COUNT) currentPos = 0;
if (REFRESH_RATE == 0) REFRESH_RATE = 1;
uint32_t totalCalls = (1000000UL / REFRESH_RATE) / PROCESS_INTERVAL_US;
if (totalCalls < DIGITS_COUNT) totalCalls = DIGITS_COUNT;
currentDigitTime = totalCalls / DIGITS_COUNT;
if (currentDigitTime < 1) currentDigitTime = 1;
pwmCounter = 0;
lastOutputMask = 0xFF;
}
static void UpdateOneDigit(void) {
uint8_t mask;
if (pwmCounter == 0) {
DisableAllDigits();
__NOP(); __NOP(); // анти-гостинг
UpdateCurrentSegmentThresholds();
mask = BuildCurrentDigitMask();
SetSegments(mask);
lastOutputMask = mask;
EnableDigit(currentPos);
} else {
mask = BuildCurrentDigitMask();
if (mask != lastOutputMask) {
SetSegments(mask);
lastOutputMask = mask;
}
}
pwmCounter++;
if (pwmCounter >= currentDigitTime) {
NextDigit();
}
}
// ==================== Публичные функции ====================
// Инициализация модуля
void Segment_Init(void) {
currentPos = 0;
pwmCounter = 0;
currentDigitTime = 1;
fadeCounter = 0;
lastOutputMask = 0xFF;
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();
}
// Установить символ в разряд
void Segment_SetChar(uint8_t pos, char c) {
if (pos >= DIGITS_COUNT) return;
SetDigitTargetMask(pos, GetCharMask(c));
}
// Установить напрямую маску сегментов
void Segment_SetRaw(uint8_t pos, uint8_t mask) {
if (pos >= DIGITS_COUNT) return;
SetDigitTargetMask(pos, mask);
}
// Установить строку (например "HELLO ")
void Segment_SetString(const char *str) {
for (int i = 0; i < DIGITS_COUNT; i++) {
if (str[i] == 0)
SetDigitTargetMask(i, 0xFF);
else
SetDigitTargetMask(i, GetCharMask(str[i]));
}
}
// Установка глобальной яркости (0-100%)
void Segment_SetBrightness(uint8_t percent) {
if (percent > 100) percent = 100;
if (percent < 1) percent = 1;
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();
}