Frekvenční výstup

Odpovědět
QRocky
Příspěvky: 36
Registrován: 28 zář 2017, 16:30
Reputation: 0

Frekvenční výstup

Příspěvek od QRocky » 03 srp 2021, 07:30

Ahojte,

potřeboval bych pomoci s jedním programem na Arduinu, které pomocí změny frekvence na pinu "PWM"
řídí malý VDC motorek.

K Arduinu jsou připojen čtyři tlačítka:
Button_Speed_01 > Definuje rychlost č. 1
Button_Speed_02 > Definuje rychlost č. 2
ButtonPlus > Přidává k frekvenci ideálně 0,1 Hz
ButtonMinus > Ubírá od frekvence ideálně 0,1 Hz

Rychlost č. 1 by měla být dána časovou periodou v mikrovteřinách.
Defaultně nastaveno na 10706 uS jako půl perioda > po tuto dobu je PWM výstupu logická jednička (stejný čas pak logická nula).
Frekvence je tedy dána vzorečkem 1 / f > 1 / 2x 10706 * 10na6, tj. 47, 6 Hz.
Pro rychlost č. 2 je základní hodnotou 63 Hz.

To v základu docela funguje, problém je, když začnu používat tlačítka PlusButtonState a MinusButtonState. Počet stisku těchto tlačítek se ukládá do paměti EEPROM, aby si Arduino umělo spočítat poslední použitou hodnotu (to také docela hezky funguje).

Chci, abych, když stisknu jedno z těchto tlačítek, jsem přidal nebo ubral od zmíněných půlperiod cca 23 uS (interval_Speed_01), nebo 12 uS od interval_Speed_02. V důsledku by to mělo znamenat změnu frekvence o cca 0,1 Hz, nicméně obvykle je změna frekvence o cca 2 Hz. Potřebuji jemnější ladění, 0,1 Hz pro plus i mínus ideálně.
Když změním hodnotu pro stepTime_Speed_02, nic moc se nezmění. Ladění je sice jemnější, ale stejně to poskočí o 0,7 nebo 0,8 Hz...

Nevíte někdo proč?

Druhý problém - potřeboval bych eliminovat problém při stisku tlačítek. Pokud bude zrovna motor v běhu a bude na něm například Speed 01 a já stisknu tlačítko Button_Speed_02, tak chci, aby se motor vypnul. Teprve až když jej stisknu za vypnutého stavu, tak by se měl rozjet, nesmí se přepnout bez přechodného vypnutí mezi rychlostmi, což se bohužel často děje.
Ke všem tlačítkům mám připojený pull down rezistor 10k, tak samo také k PWM pinu č. 13.

Děkuji

Kód: Vybrat vše

#include <EEPROM.h>

// Buttons pins
const int Button_Speed_01 = A3;
const int Button_Speed_02 = A4;
const int ButtonPlus = A1;
const int ButtonMinus = A2;

// Button state variable
int ButtonState_Speed_01 = LOW;
int ButtonState_Speed_02 = LOW;
int PlusButtonState = LOW;
int MinusButtonState = LOW;

// Pins for LEDs
const int LedON = 5;
const int LeedSpeed_1 = 6;
const int LeedSpeed_2 = 7;

// Motor pins
const int enable = 10; // Pin for motor activating
const int PWM = 13; // Digital output as PWM

int PWMstate = LOW;          // PWM State
int previousMicros = 0; // Value for previous uS state

bool Xspeed_01 = false; // One speed
bool Xspeed_02 = false; // second speed
bool MotorRunning = false;

// Time interval has half period
// in uS
unsigned long int interval_Speed_01 = 10706;
unsigned long int interval_Speed_02 = 7936;


// Number of button presses
// How many times did I pressed
// the buttons
int Plus_Button_Speed01 = 0;
int Minus_Button_Speed01 = 0;
int Plus_Button_Speed02 = 0;
int Minus_Button_Speed02 = 0;

int time_for_EEPROM = 5; // Time to store values into the EEPROM
unsigned long previousuS = 0;


// Time step for one button
// plus and minus in uS
int stepTime_Speed_01 = 23;
int stepTime_Speed_02 = 12;



void setup() {

pinMode(Button_Speed_01, INPUT);
pinMode(Button_Speed_02, INPUT);
pinMode(ButtonPlus, INPUT);
pinMode(ButtonMinus, INPUT);

pinMode(LedON, OUTPUT);
pinMode(LeedSpeed_1, OUTPUT);
pinMode(LeedSpeed_2, OUTPUT);

pinMode(enable, OUTPUT);
pinMode(PWM, OUTPUT);

MotorOFF(); // Motor state on start

digitalWrite(LedON, HIGH);

Plus_Button_Speed01 = EEPROM.read(0);
delay(time_for_EEPROM);
Minus_Button_Speed01 = EEPROM.read(1);
delay(time_for_EEPROM);
Plus_Button_Speed02 = EEPROM.read(2);
delay(time_for_EEPROM);
Minus_Button_Speed02 = EEPROM.read(3);
delay(time_for_EEPROM);


interval_Speed_01 = (interval_Speed_01 + (Plus_Button_Speed01 * stepTime_Speed_01)) - (Minus_Button_Speed01 * stepTime_Speed_01);
interval_Speed_02 = (interval_Speed_02 + (Plus_Button_Speed02 * stepTime_Speed_02)) - (Minus_Button_Speed02 * stepTime_Speed_02);


}



void MotorOFF()
{
  MotorRunning = false;
  digitalWrite(enable, LOW);
  digitalWrite(PWM, LOW);
  digitalWrite(LeedSpeed_1, LOW);
  digitalWrite(LeedSpeed_2, LOW);
  Xspeed_01 = false;
  Xspeed_02 = false;
  ButtonState_Speed_01 = LOW;
  ButtonState_Speed_02 = LOW; 
}

void MotorON()
{
  MotorRunning = true;
  digitalWrite(enable, HIGH);
}


void ButtonTest()
{
  ButtonState_Speed_01 = digitalRead(Button_Speed_01);
  ButtonState_Speed_02 = digitalRead(Button_Speed_02);
  

if (ButtonState_Speed_01 == HIGH)
  {
    
      if (MotorRunning == false)
      {
        Xspeed_01 = true;        
        digitalWrite(LeedSpeed_1, HIGH);
        MotorON();
      }

      else
      {
        Xspeed_01 = false;
        MotorOFF();
      }      
    
    
    if (Xspeed_02 == true)
    {
      MotorOFF();
    }
    ButtonState_Speed_01 = LOW;
  }





if (ButtonState_Speed_02 == HIGH)
  {
    if (ButtonState_Speed_01 == LOW)
    {
      if (MotorRunning == false)
      {
        Xspeed_02 = true;        
        digitalWrite(LeedSpeed_2, HIGH);
        
        MotorON();
      }

      else
      {
        Xspeed_02 = false;
        MotorOFF();
      }      
    }
    
    if (Xspeed_01 == true)
    {
      MotorOFF();
    }
    ButtonState_Speed_02 = LOW;
  }
}





void Speed_01_ON()
{
    unsigned long currentTime = micros();
    
    if(currentTime - previousuS > interval_Speed_01) {
        previousuS = currentTime;
   
        if (PWMstate == LOW)
          PWMstate = HIGH;
       
            
        else
          PWMstate = LOW;
        
            
        
        digitalWrite(PWM, PWMstate);
        PlusButtonState = digitalRead(ButtonPlus);
        MinusButtonState = digitalRead(ButtonMinus);
        

        
        if (PlusButtonState == HIGH)
        {
          PlusButtonState = LOW;
          interval_Speed_01 = interval_Speed_01 - stepTime_Speed_01;
          if (Minus_Button_Speed01 >> 0)
          {
            Minus_Button_Speed01 = Minus_Button_Speed01 - 1;
            EEPROM.write(1, Minus_Button_Speed01);
            delay(time_for_EEPROM);
          }

          else
          {
            Plus_Button_Speed01 = Plus_Button_Speed01 + 1;
            EEPROM.write(0, Plus_Button_Speed01);
            delay(time_for_EEPROM);
          }
        }

        if (MinusButtonState == HIGH)
        {
          MinusButtonState = LOW;
          interval_Speed_01 = interval_Speed_01 + stepTime_Speed_01;
          if (Plus_Button_Speed01 <= 255)
          {
            Plus_Button_Speed01 = Plus_Button_Speed01 - 1;
            EEPROM.write(0, Plus_Button_Speed01);
            delay(time_for_EEPROM);
          }

          else
          {
            Minus_Button_Speed01 = Minus_Button_Speed01 + 1;
            EEPROM.write(1, Minus_Button_Speed01);
            delay(time_for_EEPROM);
          }
        }
        
    
    }
}



void Speed_02_ON()
{
    unsigned long currentTime = micros();
    
    if(currentTime - previousuS > interval_Speed_02) {
        previousuS = currentTime;
   
        if (PWMstate == LOW)
          PWMstate = HIGH;
       
            
        else
          PWMstate = LOW;
        
            
        
        digitalWrite(PWM, PWMstate);
        PlusButtonState = digitalRead(ButtonPlus);
        MinusButtonState = digitalRead(ButtonMinus);
        

        
        if (PlusButtonState == HIGH)
        {
          PlusButtonState = LOW;
          interval_Speed_02 = interval_Speed_02 - stepTime_Speed_02;
          if (Minus_Button_Speed02 >> 0)
          {
            Minus_Button_Speed02 = Minus_Button_Speed02 - 1;
            EEPROM.write(3, Minus_Button_Speed02);
            delay(time_for_EEPROM);
          }

          else
          {
            Plus_Button_Speed02 = Plus_Button_Speed02 + 1;
            EEPROM.write(2, Plus_Button_Speed02);
            delay(time_for_EEPROM);
          }
        }

        if (MinusButtonState == HIGH)
        {
          MinusButtonState = LOW;
          interval_Speed_02 = interval_Speed_02 + stepTime_Speed_02;
          if (Plus_Button_Speed02 <= 255)
          {
            Plus_Button_Speed02 = Plus_Button_Speed02 - 1;
            EEPROM.write(2, Plus_Button_Speed02);
            delay(time_for_EEPROM);
          }

          else
          {
            Minus_Button_Speed02 = Minus_Button_Speed02 + 1;
            EEPROM.write(3, Minus_Button_Speed02);
            delay(time_for_EEPROM);
          }
        }
        
    
    }

    
}



void loop() {
Serial.println("LOOP");
if (Xspeed_01 == true)
{
  Speed_01_ON();
}

if (Xspeed_02 == true)
{
  Speed_02_ON();
}


  ButtonTest();


}

ondraN
Příspěvky: 932
Registrován: 08 srp 2019, 20:01
Reputation: 0

Re: Frekvenční výstup

Příspěvek od ondraN » 03 srp 2021, 07:40

Teda, ten program je dost děsnej bastl. Všechny proměnné jsou globální, jakákoli funkce je může dle libosti měnit, není počítáno s přetečením čítače mikros (to nastává jednou za hodinu a kousek). Těžko radit, co v takovém guláši způsobuje zmíněné problémy a jak to vyřešit. Zkusit najít něco jiného, nebo to napsat znovu a nějak kulturně.

QRocky
Příspěvky: 36
Registrován: 28 zář 2017, 16:30
Reputation: 0

Re: Frekvenční výstup

Příspěvek od QRocky » 03 srp 2021, 07:53

ondraN píše:
03 srp 2021, 07:40
Teda, ten program je dost děsnej bastl. Všechny proměnné jsou globální, jakákoli funkce je může dle libosti měnit, není počítáno s přetečením čítače mikros (to nastává jednou za hodinu a kousek). Těžko radit, co v takovém guláši způsobuje zmíněné problémy a jak to vyřešit. Zkusit najít něco jiného, nebo to napsat znovu a nějak kulturně.
Tak díky, no. I když základ funguje, učím se.
S čím bys začal?

ondraN
Příspěvky: 932
Registrován: 08 srp 2019, 20:01
Reputation: 0

Re: Frekvenční výstup

Příspěvek od ondraN » 03 srp 2021, 09:42

Sorry, netušil jsem, že jsi to psal ty :oops: ,podle těch komentářů to vypadalo jako stažený projekt.
Pokud potřebuji generovat signál do stovky Hz s rozlišením 0,1Hz, tak bych nepoužíval mikrosekundy, ale jel bych s milisekundami.
Je dobré oddělit globální proměnné a lokální, které jsou uvnitř funkcí. Globální proměnné předávat funkcím jako parametry. Pak se nemůže stát, že nějaká funkce změní jiné hodnoty proměnné. Pokud potřebuji ve funkci proměnnou, která je pouze pro funkci, ale nechci přijít o její hodnotu při opuštění funkce, pak ji ve funkci definovat jako static. Pokud má funkce měnit nějakou globální proměnnou a nestačí to udělat předáním výstupní hodnoty, tak použít odkaz. Funkce držet krátké, používat je jako stavební bloky, nebát se volat z jedné funkce jinou. Usnadní to orientaci v kódu a změny. Dát si pozor na přetečení proměnných a různých počítadel. Třeba počítadlo millis je 32bitů a přetočí se jednou za 2^32 milisekund (cca 50dní). Pokud by moje aplikace měla běřet bez resetu delší dobu, než je tahle, musím si ošetřit situaci přetočení počítadla.
Zkus začít s něčím jednodušším a podívej se, jak s funkcemi pracují jiní. Je to opravdu nezbytný základ a bez toho bude tvůj kód nečitelný a neodladitelný a jakákoli změna téměř nemožná.

QRocky
Příspěvky: 36
Registrován: 28 zář 2017, 16:30
Reputation: 0

Re: Frekvenční výstup

Příspěvek od QRocky » 03 srp 2021, 11:02

ondraN píše:
03 srp 2021, 09:42
Sorry, netušil jsem, že jsi to psal ty :oops: ,podle těch komentářů to vypadalo jako stažený projekt.
Pokud potřebuji generovat signál do stovky Hz s rozlišením 0,1Hz, tak bych nepoužíval mikrosekundy, ale jel bych s milisekundami.
Je dobré oddělit globální proměnné a lokální, které jsou uvnitř funkcí. Globální proměnné předávat funkcím jako parametry. Pak se nemůže stát, že nějaká funkce změní jiné hodnoty proměnné. Pokud potřebuji ve funkci proměnnou, která je pouze pro funkci, ale nechci přijít o její hodnotu při opuštění funkce, pak ji ve funkci definovat jako static. Pokud má funkce měnit nějakou globální proměnnou a nestačí to udělat předáním výstupní hodnoty, tak použít odkaz. Funkce držet krátké, používat je jako stavební bloky, nebát se volat z jedné funkce jinou. Usnadní to orientaci v kódu a změny. Dát si pozor na přetečení proměnných a různých počítadel. Třeba počítadlo millis je 32bitů a přetočí se jednou za 2^32 milisekund (cca 50dní). Pokud by moje aplikace měla běřet bez resetu delší dobu, než je tahle, musím si ošetřit situaci přetočení počítadla.
Zkus začít s něčím jednodušším a podívej se, jak s funkcemi pracují jiní. Je to opravdu nezbytný základ a bez toho bude tvůj kód nečitelný a neodladitelný a jakákoli změna téměř nemožná.
Ahoj, já se nezlobím.
Nakonec máš pravdu a já se rád posunu dál, což bez kritiky nejde.

Takže proměnné se pokusím používat maximálně lokálně, to je ok.
Přetečení micros beru na vědomí, pokusím se tedy použít ms. Přece jen by přetečení způsobovaly skoky na výstupu.

Převod otáček / půl perioda v mS vypadá takto
453 rpm > 11,037 mS (celá perioda 22,075 mS a frekvence 45,3 Hz)
454 rpm > 11,013 mS (celá perioda 22,026 mS a frekvence 45,4 Hz)
455 rpm > 10,989 mS (celá perioda 21,978 mS a frekvence 45,5 Hz)

Jak vidíš, skok o 0,1 Hz v tomto případě znamená rozdíl na setiny mS.
Je vhodné použití float pro tento účel? Uvažoval jsem, že bych nadefinoval skok 0,024 mS, nicméně u float by docházelo k přetečení často ne?
Otázka, jestli je to vhodné.

ondraN
Příspěvky: 932
Registrován: 08 srp 2019, 20:01
Reputation: 0

Re: Frekvenční výstup

Příspěvek od ondraN » 03 srp 2021, 13:23

Jo, tak to opravdu milis nestačí. Z hlediska přesnosti a stability nastavení, bych pouzil šestnáctibitový časovač a na něm bych nechal generování signálu. Interruptová obsluha od toho časovače by podle hodnoty jedné globální proměnné nastavovala frekveci (16bitů). Mimo, v loopu, bych pak řešil přepočet otáček na tu řídící hodnotu, změny frekvence, reakci na tlačítka a všechny jiné opičárny. Je otázka, jestli se svými znalostmi na to stačíš, proto ten návrh, začít něčím jednodušším.

Odpovědět

Kdo je online

Uživatelé prohlížející si toto fórum: Žádní registrovaní uživatelé a 12 hostů