Variblenübergabe an Threads
-
Hallo zusammen..
Ich drehe mich seit 1 1/2 Tagen im Kreis und komm nicht weiter.
Das Problem:Für eine Abschlußarbeit vesuche ich ein Portalkran(Containerbeladung) via Velleman Board / Visual c ++ zu programmieren.Wobei ich kann nur c nicht c ++.
Wenn ich im Automatikbetrieb dem Programm sage das der Kran das LAGER1 anfahren soll, sollten alle drei Achsen ( x,y,z) gleichzeitig laufen. nur wie bekomm ich das hin, das ich den 3 Threads unterschiedliche Variablen für die Schleifendurchläufe( Schrittmotorenansteuerung) geben kann?
Weil alle Achsen brauchen 3 unterschiedliche Werte. Für LAGER2 wären das ja dann auch wieder 3 unterschiedliche Werte.
Oder habt ihr andere Idee. Also ich bin E-Techniker und daher nicht so tief in der Materie drin
find das schon recht schwierig. die Handsteuerung ging das noch relativ leichtHier mal das Programm:
// Projekarbeit.cpp : Defines the entry point for the application. // #include "stdafx.h" #define X 1 #define Y 3 #define Z 4 HANDLE thread1=NULL,thread2=NULL,thread3=NULL; //Die Threads :warning: DWORD WINAPI XACHSE (LPVOID hwndDlg) { int i; for(i=1;i<=100;i++) { WriteAllDigital(1); Sleep(5); WriteAllDigital(3); Sleep(5); } return 0; } DWORD WINAPI YACHSE (LPVOID hwndDlg) { return 0; } DWORD WINAPI ZACHSE(LPVOID hwndDlg) { return 0; } //Die Dialogfeldprozedur BOOL WINAPI Fuell(HWND hwndDlg, UINT uMsg,WPARAM wParam,LPARAM lParam) { DWORD dwThreadId1,dwThreadId2,dwThreadId3; int h; int t1,t2,t3; long a,b; switch (uMsg) { case WM_INITDIALOG: break; case WM_TIMER: switch (wParam) { case X: a = ReadDigitalChannel(1) ; b = ReadDigitalChannel(2) ; EnableWindow(GetDlgItem(hwndDlg,IDC_XLINKS),TRUE); EnableWindow(GetDlgItem(hwndDlg,IDC_XRECHTS),TRUE); if(IsDlgButtonChecked(hwndDlg,IDC_XLINKS)) { if (a==1)CheckDlgButton(hwndDlg,IDC_XLINKS,BST_UNCHECKED); EnableWindow(GetDlgItem(hwndDlg,IDC_XRECHTS),FALSE); WriteAllDigital(0); Sleep(2); WriteAllDigital(2); Sleep(2); } if(IsDlgButtonChecked(hwndDlg,IDC_XRECHTS)) { if (b==1)CheckDlgButton(hwndDlg,IDC_XRECHTS,BST_UNCHECKED); EnableWindow(GetDlgItem(hwndDlg,IDC_XLINKS),FALSE); WriteAllDigital(1); Sleep(2); WriteAllDigital(3); Sleep(2); } break; case Y: EnableWindow(GetDlgItem(hwndDlg,IDC_YLINKS),TRUE); EnableWindow(GetDlgItem(hwndDlg,IDC_YRECHTS),TRUE); if(IsDlgButtonChecked(hwndDlg,IDC_YLINKS)) { EnableWindow(GetDlgItem(hwndDlg,IDC_YRECHTS),FALSE); WriteAllDigital(0); Sleep(2); WriteAllDigital(8); Sleep(2); } if(IsDlgButtonChecked(hwndDlg,IDC_YRECHTS)) { EnableWindow(GetDlgItem(hwndDlg,IDC_YLINKS),FALSE); WriteAllDigital(4); Sleep(2); WriteAllDigital(12); Sleep(2); } break; case Z: EnableWindow(GetDlgItem(hwndDlg,IDC_ZLINKS),TRUE); EnableWindow(GetDlgItem(hwndDlg,IDC_ZRECHTS),TRUE); if(IsDlgButtonChecked(hwndDlg,IDC_ZLINKS)) { EnableWindow(GetDlgItem(hwndDlg,IDC_ZRECHTS),FALSE); WriteAllDigital(0); Sleep(2); WriteAllDigital(32); Sleep(2); } if(IsDlgButtonChecked(hwndDlg,IDC_ZRECHTS)) { EnableWindow(GetDlgItem(hwndDlg,IDC_ZLINKS),FALSE); WriteAllDigital(16); Sleep(2); WriteAllDigital(48); Sleep(2); } break; } break; case WM_COMMAND: switch (LOWORD (wParam)) { case IDC_CONNECT: h = OpenDevice(0); ClearAllDigital(); if (h==0) { MessageBox(NULL,"CONNECT","",MB_OK); } break; case IDC_HAND: t1= SetTimer(hwndDlg,X,20,NULL); t2= SetTimer(hwndDlg,Y,20,NULL); t3= SetTimer(hwndDlg,Z,20,NULL); case IDC_AUTOMATIK: break; case IDC_LAGERN: :warning: if(IsDlgButtonChecked(hwndDlg,IDC_LAGER1)) thread1=CreateThread(NULL,0,XACHSE,(LPWORD)hwndDlg,0,&dwThreadId1); break; case IDC_HOLEN: break; case IDC_CANCEL: ClearAllDigital(); CloseDevice(); EndDialog(hwndDlg,wParam); // oder DestroyWindow(hwndDlg); return TRUE; } } return FALSE; } int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow ) { DialogBox(hInstance,MAKEINTRESOURCE(IDD_DIALOG1),NULL,(DLGPROC)Fuell); return 0; }
-
Ja, andere Idee: du machst nur einen Thread der alle 3 (4, 5, ...) Motoren steuert.
Dazu machst du aus jedem Motor eine Art "Statemachine".Die Daten die du pro Motor brauchst damit der Thread weiss was er mit dem Motor anfangen kann packst du in eine Struktur die dann z.B. so aussehen könnte:
typedef struct StepperMotorData_ { int pendingSteps; // Wieviele Schritte noch zu machen sind (> 0: vorlauf, < 0: rücklauf) int state; // 0: Motor "aus", 1: Motor "an" (=Zustand der "Step-Leitung") DWORD timer; // Zeit seit dem letzen Umschalten der "Step-Leitung" int motorBit; // Bit für die "Step-Leitung" int directionBit; // Bit welches die Richtung des Motors steuert } StepperMotorData;Dann bastelst du dir eine Funktion die sich so eine Struktur anguckt, und entsprechend "updated", also den neuen Zustand setzt etc.
Im Motor-Thread lässt du diese Funktion dann auf alle Motor-Structs los. Danach sammelst du die Zustände für alle Motoren, bastelst die in einen Wert zusammen, und schreibst den zur Hardware raus.Das ganze machst du in einer Schleife, am besten mit "Sleep(1)" dazwischen. Das Timing der Motoren wird dabei über die "timer" Variable in der Struktur gemacht, d.h. du musst jedes mal gucken ob seit "p->timer" schon genug Zeit vergangen ist dass du schon wieder umschalten kannst ohne dass es zu schnell für den Motor wird.
Dann musst du noch den Zugriff auf diese Daten synchronisieren, damit der GUI-Thread da auch drinnen lesen/schreiben kann (vermutlich wird der nur die pendingSteps Variable lesen & schreiben, und vielleicht noch die "state" Variable lesen).
Das macht man üblicherweise über eine CRITICAL_SECTION, sieht dann inetwa so aus:#define MOTOR_COUNT 3 int g_runMotorThread = 1; CRITICAL_SECTION g_motorDataCS; StepperMotorData g_motors[MOTOR_COUNT]; void MotorThreadLoop() { int i; int controlBits; timeBeginPeriod(1); // damit Sleep() kürzere Intervalle verdaut (sonst min. ~15 msec) while (g_runMotorThread) { Sleep(1); // Pause, damit der GUI Thread auch mal drankommt (aber nicht zulange) EnterCriticalSection(&g_motorDataCS); // ab hier dürfen wir auf die Daten zugreifen // Motor Zustände updaten for (i = 0; i < MOTOR_COUNT; i++) UpdateMotor(&(g_motors[i])); // Steuerbits sammeln controlBits = 0; for (i = 0; i < MOTOR_COUNT; i++) controlBits |= GetMotorBits(&(g_motors[i])); LeaveCriticalSection(&g_motorDataCS); // danach dürfen wir nichtmehr auf die Daten zugreifen // Steuerbits rausschreiben WriteAllDigital(controlBits); } timeEndPeriod(1); } // ... int main() { InitializeCriticalSection(&g_motorDataCS); // CRITICAL_SECTION erstellen // g_motors mit den nötigen Daten befüllen // thread starten // GUI rausstarten, Message Loop laufen lassen etc. // thread beenden: g_runMotorThread = 0; // auf Ende des Threads warten (WaitForSingleObject()) // aufräumen was aufzuräumen ist DeleteCriticalSection(&g_motorDataCS); // CRITICAL_SECTION freigeben return 0; } void OnLalaButtonClicked() // irgendein Message Handler der im GUI Thread läuft { EnterCriticalSection(&g_motorDataCS); // ab hier dürfen wir auf die Daten zugreifen // "Aufträge" der Motoren verändern g_motors[0].pendingSteps += 10; g_motors[1].pendingSteps -= 10; g_motors[2].pendingSteps += 20; LeaveCriticalSection(&g_motorDataCS); // danach dürfen wir nichtmehr auf die Daten zugreifen }Das ist natürlich kein fertiger, lauffähiger Code, aber als grobe Richtlinie/Beispiel hilft es dir vielleicht weiter.
Wenn du die 3 Motoren mit unterschiedlicher Geschwindigkeit fahren lassen möchtest/musst wird das ganze etwas komplizierter, aber auch nicht wild. In dem Fall musst du halt den State pro Motor so erweitern dass die UpdateMotor Funktion die nötigen Daten hat, also z.B. die gewünschte Geschwindigkeit.
Und nochwas: in deinem Beispiel hast du mehrere Stellen die im Prinzip das gleiche machen, noch dazu in verschiedenen Threads. Und zwar meine ich dass du in der "Dialogfeldprozedur" die Motoren ansteuerst, und in den Threads auch. Das ist nicht gut, das schreit danach dass sich die 2 in die Queere kommen.
Besser ist es IMHO wenn du die Motoren immer nur aus dem Motor Thread ansteuerst, und dem Motor Thread über möglichst wenige "kommunikations-variablen" (wie z.B. "pendingSteps") mitteilst was zu tun ist. Die Zugriffe (Lesen UND Schreiben!) auf diese Variablen müssen dann (in allen Threads!) über die CRITICAL_SECTION synchronisiert werden, und mit etwas Glück funktioniert das ganze dann wie es soll

-
Uff da hast du dir aber viel Arbeit gemacht.Vielen dank..
Aber ehrlich gesagt ist das gerade noch ein Tacken zu hoch für mich. Ich versuch mal ein wenig rumzubasteln und nachzulesen was du da meinst.
-
Ok mal anders gefragt:
Mein Motor für die X Achse kann z.B. 5 Positionen anfahren.
Demnach sag ich vorab wieviel Schleifenläufe das jedesmal sein soll.
heißt doch ich schreibe z.B Wohin eigentlich? Global nehm ich mal an
int SCHLEIFE[4]; SCHLEIFE[0]=100; SCHLEIFE[1]=250; etcHIER soll das verarbeitet werden:
DWORD WINAPI XACHSE (LPVOID hwndDlg) // hier muss ich SCHLEIFE[] irgendwie übergeben.Soll ja auch für alle anderen Position gelten { int i; for(i=1;i<=SCHLEIFE[];i++) { WriteAllDigital(1); Sleep(5); WriteAllDigital(3); Sleep(5); } return 0; }Und hier ruf ich die zweite Position mit einem Button auf
case IDC_LAGERN: if(IsDlgButtonChecked(hwndDlg,IDC_LAGER1)) thread1=CreateThread(NULL,0,XACHSE,(LPWORD)hwndDlg,0,&dwThreadId1);So ,wie kann ich createThread sagen,das ich jetzt SCHLEIFE[1] haben soll? Wie übergebe ich das dem THREAD XACHSE. Das wäre für mein Programm ausreichend. Sonst glaubt mein Lehrer eh nicht das ich selber darauf gekommen bin

-
nich Global du definierst
int SCHLEIFE[4];ebenfalls in der strukt anweisung und übergibst den parameter beim start