Доброго вам!
Сейчас как-то вопрос сформировался на базе этой темы (https://anklab.ru/forum/index.php?topic=197.0)
К теме он конечно привязан, но мне видится, вопрос более универсален.
Суть заключается в том, чтобы имея одну кнопку для нажимания :) которая имеет единственный контакт на замыкание и не имеет фиксации (например, типовая тактильная кнопка), заставить её выполнять некий функционал, применительно к той теме, включать и выключать блок питания.
На мой взгляд, желаемый алгоритм для одной кнопки:
Обеспечение питанием: дежурное напряжение, присутствует всегда при включенном силовом выключателе (ну и напряжении в розетке)
При подаче питания блок должен гарантированно оставаться в состоянии "Выключено".
Питание подано.
1. Блок питания выключен:
a) Короткое нажатие - ничего не происходит.
б) Длинное нажатие - блок включается.
в) Удержание - блок включается, затем выключается. В выключенном состоянии находится до того, как кнопка будет отжата и затем не произойдёт длинное нажатие.
2. Блок питания включен:
a) Короткое нажатие - выключение.
б) Длинное нажатие - выключение.
в) Удержание - выключение.
Можно ввести учёт сигнала "AC Power Good" и "PowerGood". Наличие "AC PowerGood" может являться условием для включения, а пропадание "PowerGood" признаком необходимости выключения.
Если взять Тиньку, то у неё восемь ног из которых две - питание. Итого шесть портов. Один порт для кнопки. Один порт для управляющего выходного сигнала. Укладываемся :)
Тут сразу внутренняя лягушка негодует, и говорит ещё надо порт на измерение температуры и порт на шим вентилятора. :)
Это мы посчитаем, но и перегружать не надо. Сейчас задача - разобраться с диаграммами и общим алгоритмом работы. Пока нарисую, но предложения о том, как это можно сделать - это с вниманием :)
Проверка пина раз в 10-50 мсек, счетчик если пин внизу, увеличиваем до тех пор пока не отожмётся, проверка отжатия, -> результат длинное/короткое нажати, пауза... повтор.
Как-то так делал для кнопок на вольтметре, ничего правда не рисовал, придумывал на ходу. :)
ы. Ну и ещё нам надо, чтобы тинька знала, включен блок или выключен, в зависимости от этого - действие.
На старой макетке сообразил схему, код малость корявый кое-где, надо бы покрасивее, помощь не помешает.
Работает так
https://youtu.be/CZx2pOhqQfk?si=zYbhuVRFhl1_qL9D
Кнопка и ШИМ.
↓ спойлер ↓
#include <avr/io.h>
#include <util/delay.h>
#include <avr/interrupt.h>
#define PWMPIN PB0
#define P4PIN PB1
#define SWPIN PB3
#define ADCPIN PB4
#define SAMPLES_MAX (64) // max number of samples
static uint32_t samples_sum = 0L;
static volatile uint8_t samples_cnt = 0;
volatile uint32_t ttms = 0;
volatile uint32_t lastms = 0;
volatile uint8_t
volatile uint8_t pressed = 0, released = 0, lastbtcnt = 0, btcnt = 0;
static void adc_init(void)
{
DDRB &= ~_BV(ADCPIN); // set pin as INPUT
PORTB &= ~_BV(ADCPIN); // turn off pull-up resistor
ADCSRA |= _BV(ADPS2)|_BV(ADPS1)|_BV(ADPS0); // set ADC division factor 1024;
ADCSRA |= _BV(ADEN)|_BV(ADIE); // ADC interrupt enable
ADMUX = _BV(MUX1); //
sei();
ADCSRA |= _BV(ADSC); // start first signal acquisition
}
static void pwm_init(uint32_t N)
{
//DDRB |= _BV(PWMPIN); // вывoд PWM кaк ВЫХОД
DDRB |= (1 << PWMPIN);
TCCR0A |= _BV(WGM01)|_BV(WGM00); // уcтaнoвить рeжим тaймeрa нa FAST PWM
TCCR0A |= _BV(COM0A1); // пoдключить cигнaл PWM к вывoду (AC0A => PB0)
// Кoгдa тaймeр уcтaнoвлeн в рeжим Fast PWM, чacтoтa мoжeт быть рaccчитaнa пo фoрмулe: F = F_CPU / (N * 256)
// Вoзмoжныe чacтoты (при 1,2 МГц):
// -> F (N_1) = 4,687 кГц -> F (N_8) = 585 Гц -> F (N_64) = 73 Гц -> F (N_256) = 18 Гц -> F (N_1024) = 4 Гц
TCCR0B = (TCCR0B & ~((1<<CS02)|(1<<CS01)|(1<<CS00))) | N; // sуcтaнoвить прeдвaритeльный дeлитeль
}
static void pwm_set_duty(uint8_t duty)
{
OCR0A = duty; // уcтaнoвить OCRnx
}
static void pwm_stop(void)
{
TCCR0B &= ~((1<<CS02)|(1<<CS01)|(1<<CS00)); // ocтaнoвить тaймeр
}
void adc_process(void)
{
uint16_t value;
if (samples_cnt < SAMPLES_MAX) {return;}
value = samples_sum / (uint32_t)samples_cnt;
samples_sum = samples_cnt = 0;
ADCSRA |= _BV(ADSC);
if (value > 540) { pwm_set_duty(250); }
if (value < 540) { pwm_set_duty( 50); }
// 538 27 градусов
}
void button_process()
{
if (lastms > ttms || ttms - lastms > 100)
{
lastms = ttms;
if (((PINB & (1 << SWPIN)) >> SWPIN) == 0) {btcnt++; lastbtcnt = btcnt;} else btcnt = 0;
if (btcnt) pressed = 1;
if ((pressed) & (btcnt == 0)) released = 1;
if ((lastbtcnt > 1) & (released))
{
PORTB |= (1 << P4PIN);//pwm_set_duty(250);
pressed = 0; released = 0; lastbtcnt = 0;
}
if (lastbtcnt > 20)
{
PORTB &= ~(1 << P4PIN);//pwm_set_duty(50);
pressed = 0; released = 0; lastbtcnt = 0;
}
}
}
int main(void)
{
DDRB |= (1 << P4PIN); // пин на выход //DDRB |= _BV(PB1); // Set PB1 as output, ignore the rest
adc_init();
// pwm_stop();
pwm_init(1);
pwm_set_duty(150);
_delay_ms(2000);
pwm_set_duty(50);
while(1)
{
adc_process();
button_process();
}
return 0;
}
ISR(ADC_vect)
{
uint8_t low, high;
if (samples_cnt < SAMPLES_MAX)
{
low = ADCL;
high = ADCH;
samples_sum += (high << 8) | low;
samples_cnt++;
ADCSRA |= _BV(ADSC);
ttms++;
}
}
// EOF
Не реализовано плавное увеличение оборотов в зависимости от температуры.
Прерывание ADC используется и для инкремента переменной ttms.
Термистор в верхнем плече делителя, нижний резистор 10 кОм.
Файлик калькулятора найден на просторах.
:: добавлено 29 Янв., 2024, 14:17Вот такое управление вентилятором получилось:
uint16_t duty = value - 555; //так включение при ~ 37градусах
if (duty < 30) duty = 0; //выключение 35
if (duty > 240) duty = 254;
pwm_set_duty(duty);
value тут значение полученное с АЦП.
Надо внедрение выключения по перегреву придумать.
Как-то так.
https://youtu.be/AGyT6PhjzWM
Исходник, можно было все флаги битывыими сделать, было бы компактнее, но и так помещается, 850 байт.
При перегреве выключается, температуру поставил ~51 (800), посмотрю как вести себя будет.
Схема, плата https://github.com/minamonra/MKiCadPro/tree/main/Power/tiny13butfan
Дополнил код в соответсвии с алгоритмом нажатий из первого поста.
Диаграмму состояний не составлял?
p.s. да, я что-то тормознул... но были причины, уж извините...
Не, не составлял.
Что-то пошло не так.
Добавил в условие длинного нажатия
if (pwrstate == 0) poweron(); else poweroff(); // Если БП выключен -> включим
а не учёл, что по этому куску кода мы пробегаемся часто, и получается не то, что хотел.
Если оставить poweroff() на короткое и poweron() на длинное, всё хорошо.
:: добавлено 05 Фев., 2024, 20:08
В общем либо так оставлять, либо переделать так, чтобы кнопка срабатывала только один раз, без повторов.