Каждый день следует прочитать хоть какое-нибудь мудрое изречение.
Иоганн Вольфганг Гёте

Меню навигации для мобильных

Тинька и Кнопка

Автор Slabovik, 24 Янв., 2024, 18:10

« предыдущая - следующая »

Slabovik

Доброго вам!

Сейчас как-то вопрос сформировался на базе этой темы (https://anklab.ru/forum/index.php?topic=197.0)
К теме он конечно привязан, но мне видится, вопрос более универсален.
Суть заключается в том, чтобы имея одну кнопку для нажимания :) которая имеет единственный контакт на замыкание и не имеет фиксации (например, типовая тактильная кнопка), заставить её выполнять некий функционал, применительно к той теме, включать и выключать блок питания.

На мой взгляд, желаемый алгоритм для одной кнопки:

Обеспечение питанием: дежурное напряжение, присутствует всегда при включенном силовом выключателе (ну и напряжении в розетке)

При подаче питания блок должен гарантированно оставаться в состоянии "Выключено".

Питание подано.

1. Блок питания выключен:

a) Короткое нажатие - ничего не происходит.
б) Длинное нажатие - блок включается.
в) Удержание - блок включается, затем выключается. В выключенном состоянии находится до того, как кнопка будет отжата и затем не произойдёт длинное нажатие.

2. Блок питания включен:

a) Короткое нажатие - выключение.
б) Длинное нажатие - выключение.
в) Удержание - выключение.

Можно ввести учёт сигнала "AC Power Good" и "PowerGood". Наличие "AC PowerGood" может являться условием для включения, а пропадание "PowerGood" признаком необходимости выключения.

Если взять Тиньку, то у неё восемь ног из которых две - питание. Итого шесть портов. Один порт для кнопки. Один порт для управляющего выходного сигнала. Укладываемся :)
Общением на форуме подпитываю свою эгоистичную, склонную к самолюбованию сущность.

zenon

Тут сразу внутренняя лягушка негодует, и говорит ещё надо порт на измерение температуры и порт на шим вентилятора. :)
https://github.com/minamonra/

Slabovik

Это мы посчитаем, но и перегружать не надо. Сейчас задача - разобраться с диаграммами и общим алгоритмом работы. Пока нарисую, но предложения о том, как это можно сделать - это с вниманием :)
Общением на форуме подпитываю свою эгоистичную, склонную к самолюбованию сущность.

zenon

#3
Проверка пина раз в 10-50 мсек, счетчик если пин внизу, увеличиваем до тех пор пока не отожмётся, проверка отжатия, -> результат длинное/короткое нажати, пауза... повтор.
Как-то так делал для кнопок на вольтметре, ничего правда не рисовал, придумывал на ходу. :)
ы. Ну и ещё нам надо, чтобы тинька знала, включен блок или выключен, в зависимости от этого - действие.
https://github.com/minamonra/

zenon

#4
На старой макетке сообразил схему, код малость корявый кое-где, надо бы покрасивее, помощь не помешает.
Работает так
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://github.com/minamonra/

zenon

#5
Как-то так.
https://youtu.be/AGyT6PhjzWM
Исходник, можно было все флаги битывыими сделать, было бы компактнее, но и так помещается, 850 байт.
При перегреве выключается, температуру поставил ~51 (800), посмотрю как вести себя будет.
Схема, плата https://github.com/minamonra/MKiCadPro/tree/main/Power/tiny13butfan
https://github.com/minamonra/

zenon

Дополнил код в соответсвии с алгоритмом нажатий из первого поста.
https://github.com/minamonra/

Slabovik

Диаграмму состояний не составлял?

p.s. да, я что-то тормознул... но были причины, уж извините...
Общением на форуме подпитываю свою эгоистичную, склонную к самолюбованию сущность.

zenon

#8
Не, не составлял.
Что-то пошло не так.
Добавил в условие длинного нажатия
if (pwrstate == 0) poweron(); else poweroff(); // Если БП выключен -> включим
а не учёл, что по этому куску кода мы пробегаемся часто, и получается не то, что хотел.
Если оставить poweroff() на короткое и poweron() на длинное, всё хорошо.

:: добавлено 05 Фев., 2024, 20:08
В общем либо так оставлять, либо переделать так, чтобы кнопка срабатывала только один раз, без повторов.
https://github.com/minamonra/