Jump to content

Schrittmotor im Intervallbetrieb


Recommended Posts

Hallo

Ich habe ein Dosiersystem mit drei Peristaltikpumpen, gesteuert mit je einem Stepper Brick. Die Software ist in C/C++ geschrieben. Das Ganze funktioniert grundsätzlich. Bei folgender Problemstellung komme ich aber nicht weiter:

Wenn die Dosiervolumina sehr klein sind, drehen sich die Motoren auch entsprechend langsam. Dann würde ich gerne auf Intervallbetrieb umstellen (d.h. 1 Minute dosieren, 9 Minuten Pause, 1 Minute dosieren, 9 Minuten ....).

Hat jemand eine Idee, wie man das umsetzen kann?

Zusatzinfo:

Der TF-Stapel besteht aus Step-Down Power Supply, RED-Brick, 3 Stepper-Bricks, 3 Rotary Encoder 2.0 und 1 LCD128x64-Display.

Ein Real Time Clock Bricklet wäre vorhanden ist aber bisher nicht integriert.

Herzlichen Dank für eure Tips

Uwe

 

Link to post
Share on other sites

Hallo Uwe,

ich verstehe dein Problem nicht ganz. Geht es dir um ein eine Hardwarelösung oder eine Softwarelösung?
Du könntest ja prüfen ob die Menge die benötigt wird einen Grenzwert unterschreitet und dann das takten(Intervallbetrieb) in einen Thread auslagern.

Grüße
Markus

Link to post
Share on other sites

Hallo Markus.

Vielen Dank für Deine Rückmeldung. Das Problem ist, dass ich eigentlich noch Programmier-Anfänger bin. Was ich suche:

Wenn das Dosiersystem gestartet ist, brauche ich für jede Pumpe einen Timer, der sagt "Dosiere jetzt x Min, danach pausiere bitte y min". Ich weiss, dass es eine sleep-Funktion gibt. Den Begriff "Thread" habe ich zwar schon gehört, hab mich aber bisher nicht damit beschäftigt.

Mir fehlt im Moment die Idee, wie man solche Timer in C/C++ erstellt und in ein Programm einbindet (Stichwort Interrupt?)

Grüsse Uwe

Link to post
Share on other sites

Da ich eigentlich so gut wie alles in Python programmiere ist meine Antwort jetzt vielleicht etwas python spezifisch. Müsste aber in C genauso möglich sein.
Es gibt hier natürlich wie immer mehrere Möglichkeiten deine Anforderung zu lösen.
Möglich wäre z.B.:
Du erstellst dir eine Klasse Dosierung wobei nun jede Pumpe eine Instanz bzw. ein Objekt der Klasse wird.
Du baust dir eine Schleife welche immer wieder alle Pumpen überprüft und führst diese regelmäßig in deinem Programm aus, ob die gewünschte Menge gefördert und die Zeit schon abgelaufen ist für das nächste Intervall.
Zum Beispiel könnte ein Attribute die Pausenzeit definieren welche bei den Intervallen eingehalten wird.
Alternativ könntest du jetzt natürlich auch einen eigenen Thread erzeugen der nur alle Pumpen prüft und diese entsprechend einstellt.
Oder du erzeugst pro Pumpe einen Thread was dann beim erzeugen der Objekte möglich ist. Würde ich bei vielen Pumpen aber eher davon abraten.

Vielleicht erklärst du mal was genau du dir schon überlegt hast?

Ein Thread ist wie ein Task den du auslagern kannst welcher unabhängig von deinem main abgearbeitet wird(parallel). Kann hilfreich sein.

Bzgl. Thread hier ein Link: https://stackoverflow.com/questions/266168/simple-example-of-threading-in-c

Sowie hier noch ein Link der dich auch interessieren könnte:

 

Link to post
Share on other sites

Hallo Uwe,

das Ganze ist ja erst einmal unabhängig von der Programmiersprache.

Ich hatte eine ähnliche Problemstellung in C und das grob so gelöst.

Pro Gerät einen Thread erstellen.

Innerhalb des Threads dann per sleep die Wartezeit eingebaut. Da sleep() threatsafe ist, funktioniert das für jedes Gerät seperat.

Grüße

Jürgen

Link to post
Share on other sites

Guten Morgen Jürgen.

Vielen Dank für Deine Info.

Ich habe zum Thema "Thread" mal ein Video (c++ Tutorial von Pilzschaf Nr. 68) angeschaut. Jetzt weiss ich so ungefähr, was man unter einem Thread versteht. Machen Threads auch beim "SingleCore"-Prozessor des RED-Bricks (Cortex A8) Sinn?

Meinen aktuellen Source-Code habe ich mal als Datei angehängt. Das Programm compiliert fehlerfrei und läuft auch fehlerfrei. _P202007MS02_Dosiersystem.cpp.

Allerdings tritt folgendes Problem auf:

Wenn ich eine der Pumpen im Intervall-Betrieb (LCD-Button "Inter" auf dem angehängten Bild) starte, dann macht diese Pumpe genau das, was sie soll. Das LCD ist jedoch für weitere Eingaben blockiert. Erst nach einer unreprodzierbaren Zeitspanne, sind dann plötzlich Eingaben möglich.

Die zugehörige Funktion:

/**
* Die folgende Funktion startet die Pumpe und macht die Anzahl Schritte, die übergeben wurde.
* Die Motorsteps per second werden ebenfalls zuvor berechnet und übergeben.
* Über die Callbackfunktion wird sie nach einer gewissen Zeit (Delay-Time) wieder aufgerufen.
*
*/
void PumpMotorStartIntervalMode(PeristalticPump *DummyPump)
{
/* Anfang  - Variablen lediglich zur Kontrolle und nicht für Betrieb */
    uint16_t currVel;
    int32_t currPos;
    int32_t remainSteps;
    uint16_t stackVolt;
    uint16_t externVolt;
    uint16_t currConsumpt;
    char retPos;
/* Ende Variablen zur Kontrolle */

    uint16_t DosingFactor = 1;
    uint16_t MotorSpeedmLPerHour;
    uint16_t MotorSpeedStepsPerSecond;
    uint32_t NumberOfStepsPerHour;
    uint32_t NumberOfStepsPerDosingPeriod;
    uint8_t mode = STEPPER_STEP_MODE_EIGHTH_STEP;

    DosingFactor = (DummyPump->DosingIntervalTimeMinute_Value + DummyPump->DelayIntervalTimeMinute_Value) / DummyPump->DosingIntervalTimeMinute_Value;
    NumberOfStepsPerHour =  PUMP_FULL_STEPS_PER_ROUND * mode * DummyPump->RoundsPer100mL / 100 * DummyPump->FlowMLPerHour_Value * DummyPump->CalibrationFactorPercent_Value / 100;

    NumberOfStepsPerDosingPeriod = NumberOfStepsPerHour * (DummyPump->DosingIntervalTimeMinute_Value + DummyPump->DelayIntervalTimeMinute_Value) / 60;
    MotorSpeedStepsPerSecond = NumberOfStepsPerHour / 3600 * DosingFactor;


    stepper_set_motor_current(DummyPump->pStepper, STEPPER_CURRENT);
	stepper_set_step_mode(DummyPump->pStepper, mode);
    stepper_set_max_velocity(DummyPump->pStepper, MotorSpeedStepsPerSecond);

	stepper_enable(DummyPump->pStepper);
    for (uint16_t i = 1; i < DosingFactor; ++i)
    {


    stepper_set_current_position(DummyPump->pStepper, 0);
//    stepper_drive_forward(DummyPump->pStepper);
    stepper_set_steps(DummyPump->pStepper, NumberOfStepsPerDosingPeriod);
    millisleep(DummyPump->DelayIntervalTimeMinute_Value * 6000);
    }
//    stepper_set_steps(DummyPump->pStepper, 5000);
/** Kontrollausgabe zur Überprüfung am Bildschirm */
    std::cout<< std::endl << "PumpMotorStartIntervalMode(PeristalticPump *DummyPump)\n";
    sprintf(text03, "Dos: %2d\tDel: %2d\t DF: %2d\t Steps/h: %d\t Steps/Period: %d\t, Step/sec: %d", DummyPump->DosingIntervalTimeMinute_Value,
            DummyPump->DelayIntervalTimeMinute_Value, DosingFactor, NumberOfStepsPerHour, NumberOfStepsPerDosingPeriod, MotorSpeedStepsPerSecond);
    std::cout << "text03: " << text03 << std::endl;
/* Ende Kontrollausgabe*/

}

Vielleicht liegt die Ursache des Problems aber gar nicht in der Funktion selbst, sondern in den CallBack-Einstellungen in der main():

  	lcd_128x64_register_callback(&pLCD128x64,
	                             LCD_128X64_CALLBACK_GUI_TAB_SELECTED,
	                             (void (*)(void))cb_gui_tab_selected, &ActivePump
                                );
    lcd_128x64_register_callback(&pLCD128x64,
                                 LCD_128X64_CALLBACK_GUI_BUTTON_PRESSED,
                                 (void (*)(void))cb_gui_button_pressed01, &LCDTabIndex
                                 );

    rotary_encoder_v2_register_callback(pump[0].pEncoder, ROTARY_ENCODER_V2_CALLBACK_COUNT,
                                        (void (*)(void))cb_InputEncoder, &pump[0]);
    rotary_encoder_v2_register_callback(pump[1].pEncoder, ROTARY_ENCODER_V2_CALLBACK_COUNT,
                                        (void (*)(void))cb_InputEncoder, &pump[1]);
    rotary_encoder_v2_register_callback(pump[2].pEncoder, ROTARY_ENCODER_V2_CALLBACK_COUNT,
                                        (void (*)(void))cb_InputEncoder, &pump[2]);

    rotary_encoder_v2_set_count_callback_configuration(pump[0].pEncoder, 200, true, 'x', 0, 0);
    rotary_encoder_v2_set_count_callback_configuration(pump[1].pEncoder, 200, true, 'x', 0, 0);
    rotary_encoder_v2_set_count_callback_configuration(pump[2].pEncoder, 200, true, 'x', 0, 0);

    lcd_128x64_set_gui_button_pressed_callback_configuration(&pLCD128x64, 300, true);
	lcd_128x64_set_gui_tab_selected_callback_configuration(&pLCD128x64, 300, true);

 

Noch eine Anmerkung:

Derzeit compiliere und starte ich das Programm noch auf einem Windows-PC mit Code::Blocks.

 

Herzlichen Dank für weitere Tips im Voraus.

Uwe

 

IMG_0006.jpg

Link to post
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • Create New...