Relai diaktifkan tanpa penyilangan

13

Bagaimana saya bisa memprogram saklar (berdasarkan pada solid-state relay atau triac) yang memicu pada zero-crossing power?

Untuk yang tidak terbiasa dengan subjek: Nyalakan daya 230V, ketika gelombang sinus dari kabel listrik melintasi nol - hasilnya adalah meminimalkan gangguan elektromagnetik yang dihasilkan dari lonjakan arus yang cepat.

Secara khusus, saya lebih suka pindah ke perangkat lunak sebanyak mungkin. Sirkuit deteksi yang terdiri dari transformator kecil, dioda, dan beberapa pasangan untuk menjaga level dan arus tetap memberikan "1" ketika daya input AC berada di setengah positif, "0" negatif, melekat pada pin GPIO input. Output terdiri dari beberapa relay solid state dan bare esensial agar tetap berjalan (pull-up dll), melekat pada pin GPIO keluaran.

Masalahnya adalah waktu: dengan 50Hz AC kita mendapatkan 100 zero-crossing dalam satu detik, satu setengah siklus adalah 10 ms. Untuk mendapatkan dalam jarak yang wajar dari zero-crossing untuk menjaga kata EMI rendah kita tidak harus mengaktifkan output lebih dari 10% melewati (atau sebelumnya) peristiwa zero-crossing, yang berarti toleransi -1 -1ms. Itu tidak berarti waktu reaksi 1 ms - kita dapat berharap bahwa zero-crossing berikutnya terjadi tepat 10 ms setelah yang pertama, atau yang keempat - 40 ms. Ini tentang granularitas - jika kita membiarkan 20 ms untuk bereaksi, itu harus antara 19 dan 21 ms, bukan 18 atau 22.

Bagaimana saya dapat mengimplementasikan timer seperti itu - memicu keluaran GPIO baik dalam 1ms karena input mendeteksi suatu sisi, atau dalam kelipatan tetap 10ms sejak itu - lebih disukai dengan penyisihan untuk beberapa bias negatif (katakanlah, transformator dan relai memperkenalkan penundaan 1,6ms; jadi saya ingin pemicu mati 8,4+ (n * 10) ms sejak pulsa input, dengan cara itu bias melawan penundaan yang diperkenalkan oleh rangkaian.) - semua tentu saja "sesuai permintaan pengguna", katakanlah, pengguna menulis "1 "ke file / sys / class / ... dan pada kesempatan terdekat (secara kasar) hasilnya berjalan" pada ". Pengguna menulis "0", dan ketika zero-crossing tiba, relay spesifik dilepaskan.

Saya percaya ini membutuhkan penulisan atau peretasan modul kernel. Bisakah Anda mengarahkan saya ke apa yang menangani pin GPIO Raspberry Pi di kernel, dan timer seperti apa yang bisa saya lampirkan (kecuali ada beberapa yang sudah ada) untuk mendapatkan fungsionalitas seperti ini?

SF.
sumber
proyek yang cukup menarik yang Anda bicarakan! Hanya untuk memberikan perkiraan kasar: Saya pertama-tama akan antarmuka gelombang sinus dari sinyal asli 50Hz melalui beberapa logika pemicu schmitt ke GPIO. Dari sana menghasilkan interupsi pada sisi naik atau turun dari sinyal. Anda sekarang dikunci pada 50Hz AC dan bisa 'memprediksi' ketika nol persimpangan berikutnya akan terjadi. Tentu saja ini melibatkan beberapa pemrograman driver kernel. Google adalah teman Anda :-)
sparkie
Lihatlah modul dimmer AC Inmojo . Saya telah berhasil mengkloning ini untuk proyek menggunakan dokumentasi open source mereka. Ada juga beberapa contoh kode Arduino yang dapat membantu Anda melalui logika kode Anda.
Butters
@Butters: Saya khawatir kesenjangan antara pemrograman Arduino dan pemrograman modul Kernel Linux akan membuat sisi perangkat lunak tidak berguna bagi saya, tetapi skema perangkat keras mencakup 100% dari apa yang saya butuhkan - terima kasih, Anda telah menyelamatkan sedikit dari pekerjaan saya - the perbedaan antara kedua proyek hanya dalam perangkat lunak (dimmer harus mengaktifkan triac secara siklik sedangkan saklar hanya mengaktifkannya on / off satu kali per toggling switch.)
SF.
... 100% dari sisi perangkat keras tentunya.
SF.

Jawaban:

6

Anda tidak perlu meretas kernel. Anda hanya perlu memindahkan proses dari antrian 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;
    }

Mulai sekarang, proses kami menerima cat /proc/sys/kernel/sched_rt_runtime_usmilidetik dari setiap cat /proc/sys/kernel/sched_rt_period_ussegmen waktu milidetik, dari eksekusi tanpa gangguan tanpa risiko tidak dapat digunakan selama waktu tersebut (dalam praktiknya, secara default di BerryBoot: keluar 0,95 detik dari setiap detik). Jika Anda membutuhkan lebih banyak, berantakan dengan nilai-nilai ini, tetapi saya tidak perlu lagi untuk tujuan saya di sini.

Saya menggunakan fungsi timer dalam milidetik (itu tentang presisi yang saya butuhkan) berdasarkan clock_gettime()jam saya penundaan.

Memanggil timer(1)menyetel ulang, memanggil timer(0)waktu mengembalikan sejak reset.

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

Anda perlu menautkan ke rtperpustakaan untuk dikompilasi - tambahkan -lrtke perintah gcc Anda.

Sekarang, untuk loop utama. Saya menggunakan input sakelar untuk "permintaan pengguna" tetapi Anda dapat menggunakan jaringan, penghitung waktu atau apa pun. Yang Anda butuhkan hanyalah mendapatkan nilai boolean 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.
sumber
Apakah ini bekerja untuk menerapkan sakelar dimmer yang dikontrol pi untuk a / c listrik? Saya membayangkan saya harus 1) mengubah resolusi untuk sesuatu yang jauh lebih kecil (bukan setiap 100ms) dan 2) bukan hanya pengaturan TRIAC_PINuntuk in, saya harus mengatur TRIAC_PINuntuk 1, menunggu jumlah waktu yang ditentukan (dalam proporsi tingkat redup yang diinginkan), lalu atur TRIAC_PINkembali ke 0. Apakah ini berfungsi?
rinogo
Saya kira dalam lingkaran utama, aku juga ingin mengubah baris if(in==out) continue;ke if(out==0) continue;, kan? Sebenarnya, saya benar-benar baru dalam pemrograman untuk pi, jadi mungkin itu tidak perlu - Saya kira ini semua terjadi secara serempak (yaitu kita tidak perlu khawatir tentang loop utama dipanggil sementara loop bersarang masih mengeksekusi)
rinogo
(Ini semua menggunakan modul dimmer Inmojo yang disebutkan di atas, tentu saja: inmojo.com/store/inmojo-market/item/… )
rinogo
2
Ada masalah dengan itu. Untuk aktivitas sistem yang stabil, Anda HARUS menghasilkan kontrol ke sistem secara berkala dan saya benar-benar ragu Anda akan mengembalikannya dalam waktu sesingkat (kurang dari) 20 ms. Jadi, hasil ini akan menghasilkan pulsa yang terlewat dan sebagai hasilnya, bola lampu berkedip. Saya mengajukan pertanyaan tentang itu tetapi tidak mendapat jawaban. Anda mungkin mengatur sched_rt_runtime_us dan sched_rt_ Period_us ke -1 untuk menonaktifkan pre-emption sistem sepenuhnya, tetapi jika Anda tidak sched_yield () atau usleep () sama sekali, itu pasti akan menimbulkan masalah.
SF.
2
Yaitu: dengan SCHED_FIFO setelah Anda memulai slice waktu, itu berlangsung tanpa gangguan sampai Anda menghasilkan secara sukarela (atau sched_rt_runtime_us telah berlalu) tetapi sistem tidak menjamin ketika Anda mendapatkan slice waktu itu. Dalam kasus saya, saya perhatikan dalam operasi normal waktu antara panggilan (memberikan irisan waktu untuk tugas) dapat meluas hingga 0,1 detik dengan beban CPU maksimum. Mungkin periode itu bisa diperbaiki dan dipaksa lebih pendek tapi saya tidak tahu caranya.
SF.