Relais für Nulldurchgang aktiviert

13

Wie könnte ich einen Schalter (basierend auf einem Halbleiterrelais oder einem Triac) programmieren, der bei Nulldurchgangsleistung auslöst?

Für diejenigen, die mit dem Thema nicht vertraut sind: Schalten Sie die 230-V-Stromversorgung ein, wenn die Sinuswelle der Stromleitung Null überschreitet - das Ergebnis ist die Minimierung der elektromagnetischen Störungen, die sich aus einer schnellen Stromspitze ergeben.

Insbesondere würde ich es vorziehen, so viel wie möglich in Software zu investieren. Die Erfassungsschaltung, die aus einem kleinen Transformator, einer Diode und einigen Widerständen besteht, um Pegel und Ströme in Schach zu halten, liefert "1", wenn die Wechselstromeingangsleistung zur positiven Hälfte, "0" zur negativen Hälfte, an einen GPIO-Eingangspin angeschlossen ist. Der Ausgang besteht aus einigen Halbleiterrelais und dem Nötigsten, um sie am Laufen zu halten (Klimmzüge usw.), die an die GPIO-Ausgangspins angeschlossen sind.

Das Problem ist das Timing: Mit 50 Hz Wechselstrom erhalten wir 100 Nulldurchgänge in einer Sekunde, eine Halbwelle ist 10 ms. Um einen angemessenen Abstand zum Nulldurchgang zu erreichen, um die EMI niedrig zu halten, sollten wir den Ausgang nicht mehr als 10% nach (oder vor) dem Ereignis des Nulldurchgangs aktivieren, dh + -1ms Toleranz. Das bedeutet keine Reaktionszeit von 1 ms - wir können davon ausgehen, dass der nächste Nulldurchgang genau 10 ms nach dem ersten oder dem vierten - 40 ms erfolgt. Es geht um Granularität - wenn wir 20 ms Reaktionszeit zulassen, müssen sie zwischen 19 und 21 ms liegen, nicht zwischen 18 und 22.

Wie kann ich einen solchen Timer - Triggerausgang GPIO entweder innerhalb von 1 ms, da der Eingang eine Flanke erkennt, oder innerhalb eines festen Vielfachen von 10 ms seitdem implementieren - vorzugsweise unter Berücksichtigung einer negativen Vorspannung (z. B. führen der Transformator und das Relais eine Verzögerung von 1,6 ms ein; Ich möchte also, dass der Trigger 8,4+ (n * 10) ms abläuft, da der Eingangsimpuls auf diese Weise die von der Schaltung eingebrachte Verzögerung ausgleicht.) - alles natürlich "auf Benutzerwunsch", sagt der Benutzer "1" msgstr "in eine / sys / class / ... - Datei und bei der nächsten (ungefähren) Gelegenheit geht die Ausgabe auf" on ". Der Benutzer schreibt "0", und wenn der Nulldurchgang eintrifft, wird das jeweilige Relais deaktiviert.

Ich glaube, dies würde das Schreiben oder Hacken eines Kernelmoduls erfordern. Könnten Sie mir zeigen, wie die GPIO-Pins von Raspberry Pi im Kernel gehandhabt werden und welche Art von Timern könnte ich daran anbringen (sofern nicht bereits welche vorhanden sind), um diese Art von Funktionalität zu erhalten?

SF.
quelle
ziemlich interessantes Projekt, von dem du sprichst! Nur um eine grobe Schätzung zu geben: Ich würde zuerst die Sinuswelle von einem 50-Hz-Originalsignal über eine Schmitt-Triggerlogik an GPIO anschließen. Von dort wird bei der steigenden oder fallenden Flanke des Signals ein Interrupt erzeugt. Sie sind jetzt auf 50 Hz Wechselstrom eingestellt und können vorhersagen, wann der nächste Nulldurchgang stattfinden wird. Dies beinhaltet sicherlich einige Kernel-Treiber-Programmierung. Google ist dein Freund :-)
sparkie
Schauen Sie sich das AC-Dimmermodul von Inmojo an . Ich habe dies erfolgreich für ein Projekt mithilfe der Open-Source-Dokumentation geklont. Es gibt auch einige Arduino-Codebeispiele, die Ihnen bei der Logik Ihres Codes helfen können.
Butters
@Butters: Ich befürchte, die Lücke zwischen der Programmierung von Arduino und dem Programmieren des Linux-Kernel-Moduls würde die Softwareseite für mich unbrauchbar machen, aber die Hardwareschemata decken zu 100% das ab, was ich brauche - danke, Sie haben einiges von meiner Arbeit gespart - das Der Unterschied zwischen den beiden Projekten besteht nur in der Software (der Dimmer muss den Triac zyklisch schalten, während der Schalter ihn nur einmal ein- / ausschaltet, wenn der Schalter umgeschaltet wird.)
SF.
... natürlich zu 100% hardwareseitig.
SF.

Antworten:

6

Sie müssen den Kernel nicht hacken. Sie müssen nur den Prozess aus der Scheduler-Warteschlange verschieben.

    #include<sched.h>

    struct sched_param param;               
    param.sched_priority = sched_get_priority_max(SCHED_FIFO);
    if( sched_setscheduler( 0, SCHED_FIFO, &param ) == -1 )
    {
            perror("sched_setscheduler");
            return -1;
    }

Von nun an erhält unser Prozess cat /proc/sys/kernel/sched_rt_runtime_usMillisekunden aus jedem cat /proc/sys/kernel/sched_rt_period_usMillisekunden-Zeitsegment, dh eine ununterbrochene Ausführung, ohne dass das Risiko besteht, dass dies während dieser Zeit verhindert wird (in der Praxis standardmäßig bei BerryBoot: 0,95 Sekunden pro Sekunde) mit diesen Werten, aber ich brauche hier nicht mehr für meinen Zweck.

Ich verwende eine Timer-Funktion in Millisekunden (das entspricht der Genauigkeit, die ich benötige), clock_gettime()um meine Verzögerungen zu messen.

Das Aufrufen timer(1)setzt es zurück und das Aufrufen timer(0)gibt die Zeit seit dem Zurücksetzen zurück.

    #include<time.h>
    typedef unsigned long long ulong64;

    ulong64 timer(unsigned char reset)
    {
            struct timespec t;
            static struct timespec lt={0,0};
            clock_gettime(CLOCK_REALTIME, &t);
            if(reset)
            {
                    lt.tv_sec = t.tv_sec;
                    lt.tv_nsec = t.tv_nsec;
            }

            int r = ((ulong64)(t.tv_sec - lt.tv_sec))*1000 + (t.tv_nsec - lt.tv_nsec)/1000000;

            return r;
    }

Sie müssen eine Verknüpfung zur rtBibliothek herstellen, damit diese kompiliert werden kann. Fügen Sie -lrtsie Ihrem gcc-Befehl hinzu.

Nun zur Hauptschleife. Ich benutze einen Schaltereingang für "Benutzeranfrage", aber Sie können Netzwerk, Timer oder was auch immer verwenden. Alles was Sie brauchen, ist den booleschen Wert in zu bekommen in.

    while(1)
    {
            //when idle, return a lot of CPU time back to the system. 
            //A call every 100ms is perfectly sufficient for responsive reaction.
            usleep(100000); 

            in  = bcm2835_gpio_lev(SWITCH_PIN);
            out = bcm2835_gpio_lev(TRIAC_PIN);

            if(in==out) continue;   //nothing to do; wait user input, return control to system.

            //The output needs to be changed.
            //First, let's wait for zero-crossing event.
            timer(TIMER_RESET);
            zx = bcm2835_gpio_lev(ZEROXING_PIN);

            //We don't want to freeze the system if the zero-xing input is broken.
            //If we don't get the event within reasonable time, 
            // (like three half-sines of the power; ZEROXING_TIMEOUT = 70)
            // we're going to bail.
            while(timer(TIMER_READ) < ZEROXING_TIMEOUT)
            {
                    if(zx != bcm2835_gpio_lev(ZEROXING_PIN))
                    {
                            //Event detected.                  
                            timer(TIMER_RESET);
                            break;
                    }
            }
            if(timer(TIMER_READ) >= ZEROXING_TIMEOUT) continue;     //Zero-crossing detection is broken, try again soon.

            //Now we are mere milliseconds after zero-crossing event arrived
            // (but it could have taken some time to arrive) so let's wait for the next one, making adjustments for the system delay.
            // This is to be worked out using an oscilloscope and trial and error.
            // In my case BIASED_DELAY = 19.

            while(timer(TIMER_READ)<BIASED_DELAY) ;

            //We can reasonably expect if we perform this right now:
            bcm2835_gpio_set_pud(TRIAC_PIN, in);
            //the signal will reach the output right on time.

            // The 100ms delay on return to start of the loop should be enough 
            // for the signals to stabilize, so no need for extra debouncing.
    }
SF.
quelle
Würde dies für die Implementierung eines pi-gesteuerten Dimmerschalters für die Klimaanlage funktionieren? Ich stelle mir vor, ich müsste 1) die Auflösung auf etwas viel Kleineres ändern (anstatt alle 100ms) und 2) anstatt nur das auf TRIAC_PINzu setzen in, müsste ich das TRIAC_PINauf 1 setzen, eine bestimmte Zeit warten (im Verhältnis zu gewünschte Dimmerstufe) und dann TRIAC_PINwieder auf 0 stellen. Würde das funktionieren?
Rinogo
Ich in der Hauptschleife annehmen, würde ich wollen auch die Linie ändern if(in==out) continue;zu if(out==0) continue;, nicht wahr? Eigentlich bin ich total neu für pi zu programmieren, so vielleicht ist das nicht nötig - Ich vermute , dies alles synchron passiert ist (dh wir haben keine Sorgen zu machen über die Hauptschleife genannt wird , während verschachtelte Schleifen noch ausgeführt werden )
rinogo
(Dies alles verwendet natürlich das oben erwähnte Inmojo-Dimmer-Modul: inmojo.com/store/inmojo-market/item/… )
rinogo
2
Daran liegt ein Problem. Für eine stabile Systemaktivität MÜSSEN Sie dem System in regelmäßigen Abständen die Kontrolle überlassen, und ich bezweifle wirklich, dass Sie es innerhalb von (weniger als) 20 ms wiederherstellen würden. Diese Ausbeuten führen also zu fehlenden Impulsen, und als Ergebnis blinkt die Glühbirne. Ich habe eine Frage dazu gestellt, aber keine Antworten bekommen. Sie können sowohl sched_rt_runtime_us als auch sched_rt_period_us auf -1 setzen, um die Systemvoraussetzung vollständig zu deaktivieren. Wenn Sie jedoch sched_yield () oder usleep () überhaupt nicht festlegen, führt dies zwangsläufig zu Problemen.
SF.
2
Das heißt: Mit SCHED_FIFO dauert es nach dem Start einer Zeitscheibe ununterbrochen, bis Sie freiwillig nachgeben (oder sched_rt_runtime_us ist abgelaufen), aber das System garantiert nicht, wann Sie diese Zeitscheibe erhalten. In meinem Fall ist mir aufgefallen, dass im Normalbetrieb die Zeit zwischen den Aufrufen (Zeitscheiben für die Aufgabe) bei maximaler CPU-Auslastung bis zu 0,1 s betragen kann. Vielleicht kann diese Zeitspanne präzisiert und verkürzt werden, aber ich weiß nicht wie.
SF.