// ПР4. Таймер в режиме ШИМ: регулирование яркости светодиода
// Плата: NUCLEO-C031C6, Wokwi + STM32duino
// Автор: Алексеев Ф.В., ЭФБО-08-23
//
// ШИМ: TIM3_CH1 на выводе PA6 (D12)
// f_TIM = 48 МГц, PSC = 47 -> f_cnt = 1 МГц, ARR = 999 -> f_PWM = 1 кГц
// Разрешение: ARR + 1 = 1000 градаций (шаг 0,1 %), CCR1 = D * (ARR + 1) / 100
#define PWM_PIN D12 // PA6 = TIM3_CH1
#define BTN_UP D4 // PB10 — увеличить яркость
#define BTN_DOWN D5 // PB4 — уменьшить яркость
#define BTN_MODE D7 // PA15 — режим: авто (плавно) / ручной
const uint32_t PSC = 47, ARR = 999;
HardwareTimer *pwmTim;
uint32_t channel;
int duty10 = 0; // коэффициент заполнения в десятых долях процента (0…1000)
int dir = 1; // направление плавного изменения
bool autoMode = true;
// Запись коэффициента заполнения в регистр сравнения CCR1
void setDuty(int d10) {
duty10 = constrain(d10, 0, 1000);
pwmTim->setCaptureCompare(channel, duty10 * (ARR + 1) / 1000, TICK_COMPARE_FORMAT);
}
// Обнаружение нажатия (фронт HIGH->LOW) с антидребезгом 25 мс
bool pressed(uint8_t pin, uint8_t idx) {
static bool stable[3] = {HIGH, HIGH, HIGH}, last[3] = {HIGH, HIGH, HIGH};
static uint32_t t[3] = {0, 0, 0};
bool raw = digitalRead(pin);
if (raw != last[idx]) { last[idx] = raw; t[idx] = millis(); }
if (millis() - t[idx] > 25 && raw != stable[idx]) {
stable[idx] = raw;
return raw == LOW;
}
return false;
}
void report() {
Serial.print("t="); Serial.print(millis());
Serial.print(autoMode ? " AUTO " : " MANUAL");
Serial.print(" D = "); Serial.print(duty10 / 10.0, 1);
Serial.print(" % CCR1 = "); Serial.println(TIM3->CCR1);
}
void setup() {
Serial.begin(115200);
pinMode(BTN_UP, INPUT_PULLUP);
pinMode(BTN_DOWN, INPUT_PULLUP);
pinMode(BTN_MODE, INPUT_PULLUP);
// Определяем таймер и канал, к которым подключён вывод
PinName pn = digitalPinToPinName(PWM_PIN);
TIM_TypeDef *inst = (TIM_TypeDef *)pinmap_peripheral(pn, PinMap_PWM);
channel = STM_PIN_CHANNEL(pinmap_function(pn, PinMap_PWM));
pwmTim = new HardwareTimer(inst);
pwmTim->setMode(channel, TIMER_OUTPUT_COMPARE_PWM1, pn); // режим PWM1
pwmTim->setPrescaleFactor(PSC + 1);
pwmTim->setOverflow(ARR + 1, TICK_FORMAT);
setDuty(0);
pwmTim->resume();
Serial.print("f_TIM = "); Serial.print(pwmTim->getTimerClkFreq()); Serial.println(" Hz");
Serial.print("PSC = "); Serial.print(TIM3->PSC);
Serial.print(" ARR = "); Serial.print(TIM3->ARR);
Serial.print(" f_PWM = ");
Serial.print(pwmTim->getTimerClkFreq() / ((PSC + 1) * (ARR + 1))); Serial.println(" Hz");
}
void loop() {
static uint32_t tStep = 0, tRep = 0;
if (pressed(BTN_MODE, 2)) { autoMode = !autoMode; report(); }
if (pressed(BTN_UP, 0)) { autoMode = false; setDuty(duty10 + 100); report(); } // +10 %
if (pressed(BTN_DOWN, 1)) { autoMode = false; setDuty(duty10 - 100); report(); } // -10 %
// Плавное изменение 0 -> 100 -> 0 % за 2 + 2 с (шаг 1 % каждые 20 мс)
if (autoMode && millis() - tStep >= 20) {
tStep = millis();
int d = duty10 + dir * 10;
if (d >= 1000) { d = 1000; dir = -1; }
if (d <= 0) { d = 0; dir = 1; }
setDuty(d);
}
if (autoMode && millis() - tRep >= 500) { tRep = millis(); report(); }
}
Loading
st-nucleo-c031c6
st-nucleo-c031c6