Relè attivato per l'attraversamento dello zero


13

Come potrei fare per programmare un interruttore (basato su relè a stato solido o triac) che si innesca sulla potenza di attraversamento zero?

Per coloro che non hanno familiarità con il soggetto: accendere l'alimentazione a 230 V, quando l'onda sinusoidale della linea di alimentazione attraversa lo zero - il risultato è ridurre al minimo i disturbi elettromagnetici derivanti da un rapido picco di corrente.

In particolare, preferirei passare il più possibile al software. Il circuito di rilevamento costituito da un piccolo trasformatore, un diodo e una coppia di resistori per mantenere sotto controllo livelli e correnti fornisce "1" quando la potenza di ingresso AC è nella metà positiva, "0" in negativo, collegata a un pin GPIO di ingresso. L'uscita è composta da alcuni relè a stato solido e elementi essenziali per mantenerli in esecuzione (pull-up, ecc.), Collegati ai pin GPIO di uscita.

Il problema è il tempismo: con 50Hz AC otteniamo 100 incroci zero in un secondo, un mezzo ciclo è 10ms. Per raggiungere una distanza ragionevole dall'attraversamento zero per mantenere bassa la suddetta EMI, non dovremmo attivare l'output di oltre il 10% oltre (o prima) l'evento di attraversamento dello zero, ciò significa tolleranza + -1ms. Ciò non significa un tempo di reazione di 1 ms - possiamo ragionevolmente aspettarci che il prossimo passaggio per lo zero avvenga esattamente 10 ms dopo il primo, o il quarto - 40 ms. Riguarda la granularità: se consentiamo 20ms di reazione, deve essere compreso tra 19 e 21ms, non 18 o 22.

Come posso implementare un tale timer - innescare l'uscita GPIO entro 1ms poiché l'ingresso rileva un fronte o entro un multiplo fisso di 10ms da allora - preferibilmente con tolleranza per alcuni bias negativi (diciamo, il trasformatore e il relè introducono un ritardo di 1,6ms; quindi voglio che il trigger si disattivi 8.4+ (n * 10) ms dall'impulso di ingresso, in questo modo il bias contrasta il ritardo introdotto dal circuito.) - ovviamente "su richiesta dell'utente", ad esempio, l'utente scrive "1 "in un file / sys / class / ... e all'opportunità (approssimativamente) più vicina l'output diventa" on ". L'utente scrive "0" e quando arriva l'attraversamento zero, il relè specifico si disattiva.

Credo che ciò richiederebbe la scrittura o l'hacking di un modulo del kernel. Potresti indicarmi cosa gestisce i pin GPIO di Raspberry Pi nel kernel e quale tipo di timer posso collegarvi (a meno che non ce ne siano già in atto) per ottenere questo tipo di funzionalità?


progetto abbastanza interessante di cui parli! Giusto per dare una stima approssimativa: per prima cosa interfacciavo l'onda sinusoidale dal segnale originale a 50Hz tramite una logica di trigger schmitt a GPIO. Da lì genera un interruzione sul fronte di salita o di discesa del segnale. Ora sei bloccato a 50Hz AC e potresti "prevedere" quando si verificherà il prossimo passaggio per lo zero. Certamente questo implica una certa programmazione del driver del kernel. Google è tuo amico :-)
sparkie il

Dai un'occhiata al modulo dimmer CA di Inmojo . Ho clonato con successo questo per un progetto usando la loro documentazione open source. C'è anche un po 'di codice Arduino di esempio che può aiutarti nella logica del tuo codice.
Butters il

@Butters: temo che il divario tra la programmazione di Arduino e la programmazione del modulo del kernel Linux mi renderebbe inutile il lato software, ma gli schemi hardware coprono il 100% di ciò di cui ho bisogno - grazie, hai risparmiato un bel po 'del mio lavoro - il la differenza tra i due progetti è solo nel software (il dimmer deve accendere ciclicamente il triac mentre l'interruttore lo accende / spegne una volta per ogni interruttore.)
SF.

... ovviamente il 100% del lato hardware.
SF.

Risposte:


6

Non è necessario hackerare il kernel. Hai solo bisogno di spostare il processo fuori dalla coda dello scheduler.

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

Da ora in poi il nostro processo riceve cat /proc/sys/kernel/sched_rt_runtime_usmillisecondi da ogni cat /proc/sys/kernel/sched_rt_period_ussegmento di millisecondi, di esecuzione ininterrotta senza il rischio di essere anticipati durante quel periodo (in pratica, per impostazione predefinita su BerryBoot: 0,95 secondi al secondo). Se hai bisogno di più, pasticcia con questi valori, ma non ho bisogno di altro per il mio scopo qui.

Sto usando una funzione timer in millisecondi (che riguarda la precisione di cui ho bisogno) in base clock_gettime()al clock dei miei ritardi.

La chiamata lo timer(1)ripristina, la chiamata timer(0)restituisce il tempo dall'azzeramento.

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

Devi rtcompilare il collegamento con la libreria per compilare questo - aggiungi -lrtal tuo comando gcc.

Ora, per il ciclo principale. Sto utilizzando un input switch per "richiesta utente" ma è possibile utilizzare rete, timer o altro. Tutto ciò che serve è ottenere il valore booleano 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.
    }

Funzionerebbe questo per implementare un dimmer controllato da pi per la rete elettrica? Immagino che avrei dovuto 1) modificare la risoluzione a qualcosa di molto più piccolo (anziché ogni 100 ms) e 2) invece di impostare la TRIAC_PINa in, avrei dovuto impostare il TRIAC_PINa 1, attendere un periodo di tempo determinato (in proporzione al livello dimmer desiderato), quindi TRIAC_PINtornare a 0. Funzionerebbe?
Rinogo,

Suppongo che nel ciclo principale vorrei anche cambiare la linea if(in==out) continue;in if(out==0) continue;, giusto? In realtà, sono totalmente nuovo nella programmazione per pi, quindi forse non è necessario - suppongo che tutto ciò avvenga in modo sincrono (cioè non dobbiamo preoccuparci del richiamo del loop principale mentre i loop nidificati sono ancora in esecuzione)
rinogo,

(Tutto questo sta usando il modulo dimmer Inmojo sopra menzionato, ovviamente: inmojo.com/store/inmojo-market/item/… )
rinogo

2
C'è un problema con questo. Per un'attività di sistema stabile DEVI cedere periodicamente il controllo al sistema e dubito davvero che lo ripristineresti in breve tempo (meno di) 20 ms. Quindi, questi rendimenti si tradurranno in impulsi mancati e, di conseguenza, il bulbo lampeggia. Ho fatto una domanda al riguardo, ma non ho avuto risposte. È possibile impostare sia sched_rt_runtime_us che sched_rt_period_us su -1 per disabilitare completamente la prelazione del sistema, ma se non si pianifica affatto sched_yield () o usleep (), ciò è destinato a creare problemi.
SF.

2
Cioè: con SCHED_FIFO una volta avviato un intervallo di tempo, dura ininterrottamente fino a quando non si cede volontariamente (o è scaduto sched_rt_runtime_us) ma il sistema non garantisce quando si ottiene tale intervallo di tempo. Nel mio caso, durante il normale funzionamento ho notato che il tempo tra le chiamate (assegnando intervalli di tempo all'attività) può estendersi fino a 0,1 secondi con il carico massimo della CPU. Forse quel periodo può essere messo a punto e forzato più breve ma non so come.
SF.
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.