Przekaźnik aktywowany przez przejście przez zero

13

Jak mógłbym zająć się programowaniem przełącznika (opartego na przekaźniku półprzewodnikowym lub triaku), który wyzwala moc przejścia przez zero?

Dla tych, którzy nie są zaznajomieni z tematem: Włącz zasilanie 230 V, gdy fala sinusoidalna linii elektroenergetycznej przekroczy zero - wynikiem jest minimalizacja zakłóceń elektromagnetycznych wynikających z szybkiego wzrostu prądu.

W szczególności wolałbym przenieść jak najwięcej do oprogramowania. Obwód wykrywający składający się z małego transformatora, diody i kilku oporników utrzymujących poziom i prądy w ryzach zapewnia „1”, gdy wejściowa moc prądu przemiennego jest w dodatniej połowie, „0” ujemna, przymocowana do wejściowego pinu GPIO. Wyjście składa się z kilku przekaźników półprzewodnikowych i podstawowych elementów niezbędnych do ich działania (podciągania itp.), Podłączonych do wyjściowych styków GPIO.

Problemem jest taktowanie: przy 50 Hz AC uzyskujemy 100 przejść przez zero w ciągu jednej sekundy, jeden półcykl to 10ms. Aby dostać się w rozsądnej odległości od przejścia przez zero w celu utrzymania wspomnianego EMI na niskim poziomie, nie powinniśmy aktywować wyjścia więcej niż 10% po (lub wcześniej) zdarzeniu przejścia przez zero, co oznacza tolerancję + -1ms. Nie oznacza to 1 ms czasu reakcji - możemy zasadnie oczekiwać, że następne przejście przez zero nastąpi dokładnie 10 ms po pierwszym lub czwartym - 40 ms. Chodzi o ziarnistość - jeśli pozwolimy na reakcję 20 ms, musi ona wynosić między 19 a 21 ms, a nie 18 lub 22.

Jak mogę zaimplementować takie wyjście GPIO z wyzwalaczem czasowym albo w ciągu 1 ms, ponieważ wejście wykrywa zbocze, lub w stałej wielokrotności 10 ms od tego czasu - najlepiej z uwzględnieniem pewnego ujemnego odchylenia (powiedzmy, transformator i przekaźnik wprowadzają opóźnienie 1,6 ms; więc chcę, aby wyzwalacz wyłączył się 8,4+ (n * 10) ms od impulsu wejściowego, w ten sposób uprzedzenie przeciwdziała opóźnieniu wprowadzonemu przez obwód.) - wszystko oczywiście „na żądanie użytkownika”, powiedzmy, użytkownik pisze „1 „do pliku / sys / class / ... i przy najbliższej (z grubsza) okazji wyjście jest„ włączone ”. Użytkownik zapisuje „0”, a gdy nadejdzie przecięcie zera, określony przekaźnik wyłącza się.

Uważam, że wymagałoby to napisania lub zhakowania modułu jądra. Czy możesz wskazać mi, co obsługuje piny GPIO Raspberry Pi w jądrze i jakiego rodzaju timerów mógłbym do niego podłączyć (chyba że są już dostępne), aby uzyskać taką funkcjonalność?

SF.
źródło
całkiem ciekawy projekt, o którym mówisz! Żeby podać przybliżone oszacowanie: najpierw chciałbym połączyć fazę sinusoidalną z oryginalnym sygnałem 50Hz za pomocą logiki wyzwalającej Schmitta z GPIO. Stamtąd generuj przerwanie na zboczu narastającym lub opadającym sygnału. Jesteś teraz zablokowany na 50 Hz prądu przemiennego i możesz „przewidzieć”, kiedy nastąpi następne przejście przez zero. Z pewnością wiąże się to z programowaniem sterowników jądra. Google jest twoim przyjacielem :-)
sparkie
Spójrz na moduł ściemniacza AC Inmojo . Sklonowałem to z powodzeniem dla projektu, korzystając z ich dokumentacji typu open source. Istnieje również przykładowy kod Arduino, który może pomóc w logice kodu.
Butters
@ Butters: Obawiam się, że luka między programowaniem Arduino a programowaniem modułu jądra Linux sprawi, że strona oprogramowania będzie dla mnie bezużyteczna, ale schematy sprzętowe pokrywają 100% tego, czego potrzebuję - dzięki temu zaoszczędziłeś sporo mojej pracy - różnica między tymi dwoma projektami polega wyłącznie na oprogramowaniu (ściemniacz musi cyklicznie przełączać triak, podczas gdy przełącznik po prostu włącza / wyłącza go raz na przełączenie przełącznika.)
SF.
... 100% strony sprzętowej oczywiście.
SF.

Odpowiedzi:

6

Nie musisz hakować jądra. Wystarczy przenieść proces z kolejki programu planującego.

    #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;
    }

Od tej chwili nasz proces odbiera cat /proc/sys/kernel/sched_rt_runtime_usmilisekundy z każdego cat /proc/sys/kernel/sched_rt_period_ussegmentu czasowego milisekund, nieprzerwanego wykonywania bez ryzyka uprzedzenia w tym czasie (w praktyce domyślnie w BerryBoot: 0,95 s na sekundę.) Jeśli potrzebujesz więcej, zadzieraj z tymi wartościami, ale tutaj nie potrzebuję więcej.

Używam funkcji timera w milisekundach (to jest potrzebna precyzja) w oparciu o clock_gettime()taktowanie moich opóźnień.

Wywołanie timer(1)resetuje go, a połączenie timer(0)zwraca czas od zresetowania.

    #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;
    }

Aby rtto skompilować, musisz połączyć się z biblioteką - dodaj -lrtdo polecenia gcc.

Teraz dla głównej pętli. Używam przełącznika do „żądania użytkownika”, ale możesz użyć sieci, timera lub cokolwiek innego. Wszystko, czego potrzebujesz, to uzyskać wartość logiczną 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.
źródło
Czy to zadziałałoby w przypadku wdrożenia ściemniacza sterowanego pi dla sieci klimatyzacji? Wyobrażam sobie, że będę musiał 1) zmienić rozdzielczość do czegoś znacznie mniejszy (zamiast co 100ms) i 2), a nie tylko ustawienie TRIAC_PINsię in, że będę musiał ustawić TRIAC_PINna 1, odczekać określoną ilość czasu (w proporcji do żądany poziom ściemniacza), a następnie ustaw z TRIAC_PINpowrotem na 0. Czy to zadziała?
rinogo,
Przypuszczam, że w głównej pętli chciałbym również zmienić linię if(in==out) continue;na if(out==0) continue;, prawda? Właściwie jestem zupełnie nowy w programowaniu dla pi, więc może to nie jest konieczne - domyślam się, że wszystko dzieje się synchronicznie (tj. Nie musimy się martwić wywołaniem głównej pętli, podczas gdy pętle zagnieżdżone wciąż działają)
rinogo,
(Wszystko to przy użyciu wspomnianego modułu ściemniacza Inmojo, oczywiście: inmojo.com/store/inmojo-market/item/… )
rinogo
2
Jest z tym problem. Dla stabilnej aktywności systemu MUSISZ okresowo poddawać system kontroli i naprawdę wątpię, czy przywrócisz go w czasie tak krótkim (mniej niż) 20 ms. Tak więc, te wydajności spowodują brakujące impulsy, aw rezultacie miganie żarówki. Ja zapytałem pytanie o to, ale nie dostał odpowiedzi. Możesz ustawić zarówno schedul_rt_runtime_us, jak i schedule_rt_period_us na -1, aby całkowicie wyłączyć wyprzedzanie systemu, ale jeśli w ogóle nie planujesz_sield () lub usleep (), to z pewnością spowoduje problemy.
SF.
2
To znaczy: w przypadku SCHED_FIFO po rozpoczęciu odcinka czasu trwa ono nieprzerwanie, dopóki nie ustąpisz dobrowolnie (lub upłynie Sched_rt_runtime_us), ale system nie gwarantuje , że otrzymasz odcinek czasu. W moim przypadku zauważyłem, że podczas normalnej pracy czas między rozmowami (podając przedziały czasowe do zadania) może wydłużyć się do 0,1 s przy maksymalnym obciążeniu procesora. Być może ten okres może być dostrojony i wymuszony krótszy, ale nie wiem jak.
SF.