init работает
This commit is contained in:
331
Core/Src/segment_tim.c
Normal file
331
Core/Src/segment_tim.c
Normal file
@@ -0,0 +1,331 @@
|
||||
#include "segment.h"
|
||||
#include "main.h"
|
||||
|
||||
// ==================== ТАЙМЕР ДЛЯ ПРЕРЫВАНИЙ ====================
|
||||
#define SEGMENT_PROCESS_TIMER htim1
|
||||
|
||||
// ==================== КОНФИГУРАЦИЯ ДИСПЛЕЯ ====================
|
||||
#define DIGITS_COUNT 6
|
||||
#define MULTIPLEX_FREQ_HZ 10000
|
||||
#define PWM_FREQUENCY_HZ 10000
|
||||
#define PWM_RESOLUTION 100
|
||||
#define TIMER_BUS_FREQ_MHZ 72
|
||||
|
||||
#define SWAP_BIT5_BIT6(x) (((x) & 0x9F) | (((x) & 0x20) << 1) | (((x) & 0x40) >> 1))
|
||||
|
||||
// ==================== ИНИЦИАЛИЗАЦИЯ СЕГМЕНТОВ ====================
|
||||
SegCtrl_t segments[7] = {
|
||||
SEG_A_CONFIG,
|
||||
SEG_B_CONFIG,
|
||||
SEG_C_CONFIG,
|
||||
SEG_D_CONFIG,
|
||||
SEG_E_CONFIG,
|
||||
SEG_F_CONFIG,
|
||||
SEG_G_CONFIG
|
||||
};
|
||||
|
||||
// ==================== ТАБЛИЦА СЕГМЕНТОВ ====================
|
||||
// 1 - сегмент включен, 0 - выключен
|
||||
static const uint8_t segmentTable[10] = {
|
||||
0x3F, // 0: 0011 1111 - A,B,C,D,E,F
|
||||
0x06, // 1: 0000 0110 - B,C
|
||||
0x5B, // 2: 0101 1011 - A,B,D,E,G
|
||||
0x4F, // 3: 0100 1111 - A,B,C,D,G
|
||||
0x66, // 4: 0110 0110 - B,C,F,G
|
||||
0x6D, // 5: 0110 1101 - A,C,D,F,G
|
||||
0x7D, // 6: 0111 1101 - A,C,D,E,F,G
|
||||
0x07, // 7: 0000 0111 - A,B,C
|
||||
0x7F, // 8: 0111 1111 - все сегменты
|
||||
0x6F // 9: 0110 1111 - A,B,C,D,F,G
|
||||
};
|
||||
|
||||
static const uint8_t activeSegmentsCount[10] = {
|
||||
6, 2, 5, 5, 4, 5, 6, 3, 7, 6
|
||||
};
|
||||
|
||||
// ==================== ПЕРЕМЕННЫЕ ====================
|
||||
static uint8_t displayBuffer[DIGITS_COUNT];
|
||||
static uint8_t currentPos = 0;
|
||||
static TimeStruct currentTime;
|
||||
static uint8_t globalBrightness = 100;
|
||||
static uint8_t digitCompensation[10];
|
||||
static uint32_t switchIntervalTicks;
|
||||
static uint32_t tickCounter = 0;
|
||||
|
||||
// ==================== ФУНКЦИИ УПРАВЛЕНИЯ РАЗРЯДАМИ ====================
|
||||
|
||||
static void DisableAllDigits(void) {
|
||||
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;
|
||||
}
|
||||
|
||||
static 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;
|
||||
}
|
||||
}
|
||||
|
||||
// ==================== ИНИЦИАЛИЗАЦИЯ CCMR ДЛЯ КАЖДОГО СЕГМЕНТА ====================
|
||||
|
||||
static void InitChannel(SegCtrl_t *seg) {
|
||||
// Определяем указатель на CCMR регистр и сдвиг в зависимости от канала
|
||||
if (seg->channel == TIM_CHANNEL_1) {
|
||||
seg->ccmr_ptr = (uint32_t*)&seg->htim->Instance->CCMR1;
|
||||
seg->ccmr_shift = 0;
|
||||
if(!seg->isComplementary)
|
||||
seg->htim->Instance->CCER &= ~TIM_CCER_CC1P; // Сброс бита CC1P
|
||||
else
|
||||
seg->htim->Instance->CCER |= TIM_CCER_CC1P; // Установка бита CC1P
|
||||
|
||||
} else if (seg->channel == TIM_CHANNEL_2) {
|
||||
seg->ccmr_ptr = (uint32_t*)&seg->htim->Instance->CCMR1;
|
||||
seg->ccmr_shift = 8;
|
||||
if(!seg->isComplementary)
|
||||
seg->htim->Instance->CCER &= ~TIM_CCER_CC2P; // Сброс бита CC2P
|
||||
else
|
||||
seg->htim->Instance->CCER |= TIM_CCER_CC2P; // Установка бита CC2P
|
||||
|
||||
} else if (seg->channel == TIM_CHANNEL_3) {
|
||||
seg->ccmr_ptr = (uint32_t*)&seg->htim->Instance->CCMR2;
|
||||
seg->ccmr_shift = 0;
|
||||
if(!seg->isComplementary)
|
||||
seg->htim->Instance->CCER &= ~TIM_CCER_CC3P; // Сброс бита CC3P
|
||||
else
|
||||
seg->htim->Instance->CCER |= TIM_CCER_CC3P; // Установка бита CC3P
|
||||
|
||||
} else if (seg->channel == TIM_CHANNEL_4) {
|
||||
seg->ccmr_ptr = (uint32_t*)&seg->htim->Instance->CCMR2;
|
||||
seg->ccmr_shift = 8;
|
||||
if(!seg->isComplementary)
|
||||
seg->htim->Instance->CCER &= ~TIM_CCER_CC4P; // Сброс бита CC4P
|
||||
else
|
||||
seg->htim->Instance->CCER |= TIM_CCER_CC4P; // Установка бита CC4P
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
// ==================== ФУНКЦИИ УПРАВЛЕНИЯ ШИМ ====================
|
||||
|
||||
static inline void PWM_StartChannel(SegCtrl_t *seg) {
|
||||
if (seg->isComplementary) {
|
||||
HAL_TIMEx_PWMN_Start(seg->htim, seg->channel);
|
||||
} else {
|
||||
HAL_TIM_PWM_Start(seg->htim, seg->channel);
|
||||
}
|
||||
}
|
||||
|
||||
static inline void PWM_SetMode(SegCtrl_t *seg, uint32_t mode) {
|
||||
uint32_t mask = 0x7 << (seg->ccmr_shift+4);
|
||||
*seg->ccmr_ptr &= ~mask;
|
||||
*seg->ccmr_ptr |= (mode << seg->ccmr_shift);
|
||||
}
|
||||
|
||||
static inline void PWM_SetDuty(SegCtrl_t *seg, uint32_t duty) {
|
||||
uint32_t final_duty = duty;
|
||||
|
||||
if (duty > PWM_RESOLUTION) duty = PWM_RESOLUTION;
|
||||
|
||||
if (final_duty == 0) {
|
||||
PWM_SetMode(seg, TIM_OCMODE_FORCED_INACTIVE);
|
||||
} else {
|
||||
PWM_SetMode(seg, TIM_OCMODE_PWM1);
|
||||
__HAL_TIM_SET_COMPARE(seg->htim, seg->channel, final_duty);
|
||||
}
|
||||
}
|
||||
|
||||
// ==================== АВТОНАСТРОЙКА ТАЙМЕРА ====================
|
||||
|
||||
static void TimerAutoConfig(TIM_HandleTypeDef *htim) {
|
||||
uint32_t timer_clock_hz = TIMER_BUS_FREQ_MHZ * 1000000;
|
||||
|
||||
uint32_t arr = PWM_RESOLUTION - 1;
|
||||
uint32_t prescaler_plus1 = timer_clock_hz / (PWM_FREQUENCY_HZ * PWM_RESOLUTION);
|
||||
|
||||
if (prescaler_plus1 < 1) prescaler_plus1 = 1;
|
||||
if (prescaler_plus1 > 65535) prescaler_plus1 = 65535;
|
||||
|
||||
uint32_t prescaler = prescaler_plus1 - 1;
|
||||
|
||||
__HAL_TIM_SET_PRESCALER(htim, prescaler);
|
||||
__HAL_TIM_SET_AUTORELOAD(htim, arr);
|
||||
}
|
||||
|
||||
// ==================== ФУНКЦИИ УПРАВЛЕНИЯ СЕГМЕНТАМИ ====================
|
||||
|
||||
static void SetSegment(uint8_t segIndex, uint8_t state) {
|
||||
SegCtrl_t *seg = &segments[segIndex];
|
||||
seg->isActive = state;
|
||||
if (state) {
|
||||
PWM_SetDuty(seg, seg->Duty);
|
||||
} else {
|
||||
PWM_SetDuty(seg, 0);
|
||||
}
|
||||
}
|
||||
|
||||
static void SetSegmentBrightness(uint8_t segIndex, uint8_t percent) {
|
||||
SegCtrl_t *seg = &segments[segIndex];
|
||||
if (percent > 100) percent = 100;
|
||||
seg->Duty = (percent * PWM_RESOLUTION) / 100;
|
||||
}
|
||||
|
||||
static void DisplayDigit(uint8_t digit, uint8_t pos) {
|
||||
if (digit > 9) digit = 0;
|
||||
uint8_t segmentMask = segmentTable[digit];
|
||||
|
||||
if (pos == 5) {
|
||||
segmentMask = SWAP_BIT5_BIT6(segmentMask);
|
||||
}
|
||||
|
||||
// Применяем компенсацию для текущей цифры
|
||||
uint8_t comp = digitCompensation[digit];
|
||||
uint8_t finalBrightness = (globalBrightness * comp) / 100;
|
||||
|
||||
for (int i = 0; i < 7; i++) {
|
||||
SetSegmentBrightness(i, finalBrightness);
|
||||
if ((segmentMask >> i) & 1) {
|
||||
SetSegment(i, 1);
|
||||
} else {
|
||||
SetSegment(i, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ==================== ФУНКЦИИ ОБНОВЛЕНИЯ БУФЕРА ====================
|
||||
|
||||
static void UpdateDisplayBuffer(void) {
|
||||
uint8_t hours = currentTime.hours;
|
||||
uint8_t minutes = currentTime.minutes;
|
||||
uint8_t seconds = currentTime.seconds;
|
||||
|
||||
static const uint8_t div10[100] = {
|
||||
0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,
|
||||
2,2,2,2,2,2,2,2,2,2,3,3,3,3,3,3,3,3,3,3,
|
||||
4,4,4,4,4,4,4,4,4,4,5,5,5,5,5,5,5,5,5,5,
|
||||
6,6,6,6,6,6,6,6,6,6,7,7,7,7,7,7,7,7,7,7,
|
||||
8,8,8,8,8,8,8,8,8,8,9,9,9,9,9,9,9,9,9,9
|
||||
};
|
||||
|
||||
static const uint8_t mod10[100] = {
|
||||
0,1,2,3,4,5,6,7,8,9,0,1,2,3,4,5,6,7,8,9,
|
||||
0,1,2,3,4,5,6,7,8,9,0,1,2,3,4,5,6,7,8,9,
|
||||
0,1,2,3,4,5,6,7,8,9,0,1,2,3,4,5,6,7,8,9,
|
||||
0,1,2,3,4,5,6,7,8,9,0,1,2,3,4,5,6,7,8,9,
|
||||
0,1,2,3,4,5,6,7,8,9,0,1,2,3,4,5,6,7,8,9
|
||||
};
|
||||
|
||||
displayBuffer[0] = div10[hours];
|
||||
displayBuffer[1] = mod10[hours];
|
||||
displayBuffer[2] = div10[minutes];
|
||||
displayBuffer[3] = mod10[minutes];
|
||||
displayBuffer[4] = div10[seconds];
|
||||
displayBuffer[5] = mod10[seconds];
|
||||
}
|
||||
|
||||
static void NextDigit(void) {
|
||||
DisableAllDigits();
|
||||
|
||||
currentPos++;
|
||||
if (currentPos >= DIGITS_COUNT) {
|
||||
currentPos = 0;
|
||||
}
|
||||
|
||||
DisplayDigit(displayBuffer[currentPos], currentPos);
|
||||
EnableDigit(currentPos);
|
||||
}
|
||||
|
||||
// ==================== ПУБЛИЧНЫЕ ФУНКЦИИ ====================
|
||||
|
||||
void Segment_Init(void) {
|
||||
TIM_HandleTypeDef* configuredTimers[10] = {0};
|
||||
int timerCount = 0;
|
||||
|
||||
// Инициализируем CCMR для каждого сегмента
|
||||
for (int i = 0; i < 7; i++) {
|
||||
InitChannel(&segments[i]);
|
||||
}
|
||||
|
||||
// Настраиваем и запускаем все уникальные таймеры для ШИМ
|
||||
for (int i = 0; i < 7; i++) {
|
||||
TIM_HandleTypeDef *htim = segments[i].htim;
|
||||
|
||||
int alreadyConfigured = 0;
|
||||
for (int j = 0; j < timerCount; j++) {
|
||||
if (configuredTimers[j] == htim) {
|
||||
alreadyConfigured = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!alreadyConfigured) {
|
||||
TimerAutoConfig(htim);
|
||||
configuredTimers[timerCount++] = htim;
|
||||
}
|
||||
|
||||
PWM_StartChannel(&segments[i]);
|
||||
}
|
||||
|
||||
// Инициализация сегментов
|
||||
for (int i = 0; i < 7; i++) {
|
||||
segments[i].Duty = 0;
|
||||
segments[i].isActive = 0;
|
||||
PWM_SetDuty(&segments[i], 0);
|
||||
}
|
||||
|
||||
// Рассчитываем компенсацию яркости для каждой цифры
|
||||
// Чем меньше сегментов у цифры, тем ярче должен гореть каждый сегмент
|
||||
for (int i = 0; i < 10; i++) {
|
||||
// Максимальное количество сегментов = 7 (цифра 8)
|
||||
// Коэффициент = (7 / количество_сегментов_у_цифры) * 100
|
||||
digitCompensation[i] = (activeSegmentsCount[i] * 100) / 7;
|
||||
if (digitCompensation[i] > 200) digitCompensation[i] = 200; // Ограничиваем
|
||||
}
|
||||
|
||||
currentTime.hours = 0;
|
||||
currentTime.minutes = 0;
|
||||
currentTime.seconds = 0;
|
||||
UpdateDisplayBuffer();
|
||||
|
||||
switchIntervalTicks = PWM_FREQUENCY_HZ / MULTIPLEX_FREQ_HZ;
|
||||
if (switchIntervalTicks < 1) switchIntervalTicks = 1;
|
||||
tickCounter = 0;
|
||||
|
||||
DisplayDigit(displayBuffer[0], 0);
|
||||
EnableDigit(0);
|
||||
|
||||
// Запускаем таймер прерываний
|
||||
HAL_TIM_Base_Start_IT(&SEGMENT_PROCESS_TIMER);
|
||||
}
|
||||
|
||||
void Segment_SetBrightness(uint8_t percent) {
|
||||
if (percent > 100) percent = 100;
|
||||
globalBrightness = percent;
|
||||
DisplayDigit(displayBuffer[currentPos], currentPos);
|
||||
}
|
||||
|
||||
void Segment_SetTime(uint8_t hours, uint8_t minutes, uint8_t seconds) {
|
||||
if (hours > 23) hours = 23;
|
||||
if (minutes > 59) minutes = 59;
|
||||
if (seconds > 59) seconds = 59;
|
||||
|
||||
currentTime.hours = hours;
|
||||
currentTime.minutes = minutes;
|
||||
currentTime.seconds = seconds;
|
||||
UpdateDisplayBuffer();
|
||||
}
|
||||
|
||||
void Segment_Process(void) {
|
||||
tickCounter++;
|
||||
if (tickCounter >= switchIntervalTicks) {
|
||||
tickCounter = 0;
|
||||
NextDigit();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user