ゼロクロス作動リレー


13

ゼロクロス電力でトリガーするスイッチ(ソリッドステートリレーまたはトライアックに基づく)をプログラミングするにはどうすればよいですか?

主題に精通していない人のために:電力線の正弦波がゼロを横切るとき、230Vの電源をオンにします-結果は、電流の急速なスパイクから生じる電磁妨害を最小限に抑えます。

具体的には、できる限りソフトウェアに移行したいです。レベルと電流を抑えるための小さなトランス、ダイオード、およびカップル抵抗器で構成される検出回路は、AC入力電力が正の半分の場合「1」、負の「0」を入力GPIOピンに接続します。出力は、出力GPIOピンに接続されたいくつかのソリッドステートリレーとそれらを実行し続けるための最低限の要素(プルアップなど)で構成されます。

問題はタイミングです。AC50Hzでは、1秒間に100個のゼロ交差が発生し、1半サイクルは10msです。ゼロクロッシングから妥当な距離内に移動して前述のEMIを低く抑えるために、ゼロクロッシングのイベントを10%以上過ぎた(またはその前に)出力をアクティブにしないでください。これは、1msの反応時間を意味するものではありません。次のゼロ交差は、最初のゼロ交差、または4番目-40msの10ms後に正確に起こると合理的に予想できます。これは粒度に関するものです-反応に20ミリ秒を許可する場合、18または22ではなく、19〜21ミリ秒にする必要があります。

このようなタイマーを実装するにはどうすればよいですか-入力がエッジを検出してから1ms以内、またはそれから10msの固定倍数以内で出力GPIOをトリガーします-負のバイアスを許容して(たとえば、トランスとリレーは1.6msの遅延を導入します;そのため、入力パルス以降、トリガーが8.4+(n * 10)msオフになるようにしたいので、バイアスは回路によって導入された遅延に対抗します。)-もちろん、「ユーザーの要求に応じて」、ユーザーは「1 「/ sys / class / ...ファイルに出力し、最も近い(おおよそ)機会に出力が「オン」になります。ユーザーは「0」を書き込み、ゼロ交差が到着すると、特定のリレーが解除されます。

これには、カーネルモジュールの作成またはハッキングが必要になると思います。カーネル内でRaspberry PiのGPIOピンを処理するもの、およびこの種類の機能を取得するために(既に配置されているものがない限り)どのような種類のタイマーをアタッチできますか?


あなたが話している非常に興味深いプロジェクト!大まかな見積もりをするために、まず50Hzのオリジナル信号からのシュミットトリガーロジックを介してGPIOに正弦波を接続します。そこから、信号の立ち上がりまたは立ち下がりエッジで割り込みを生成します。これで、50Hz ACにロックされ、次のゼロ交差が発生するときに「予測」できます。確かにこれには、カーネルドライバーのプログラミングが含まれます。Googleはあなたの友達です:-)
sparkie

InmojoのAC調光モジュールご覧ください。私は彼らのオープンソースドキュメントを使用して、これをプロジェクト用にうまくクローンしました。コードのロジックを支援するArduinoサンプルタラもあります。
バター

@Butters:ArduinoのプログラミングとLinuxカーネルモジュールのプログラミングのギャップがソフトウェア側を役に立たないのではないかと心配していますが、ハードウェアの回路図は私が必要とするものの100%をカバーしています。 2つのプロジェクトの違いはソフトウェアのみです(スイッチがトグルするたびにスイッチが電源をオン/オフする間、調光器は周期的にトライアックを切り替える必要があります)
SF。

...もちろん、ハードウェア側は100%です。
SF。

回答:


6

カーネルをハックする必要はありません。スケジューラキューからプロセスを移動するだけです。

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

これ以降、プロセスはcat /proc/sys/kernel/sched_rt_runtime_uscat /proc/sys/kernel/sched_rt_period_usミリ秒のタイムセグメントからミリ秒を受信し、その時間中にプリエンプションされるリスクなしに実行が中断されません(実際には、BerryBootのデフォルトでは毎秒0.95秒)。これらの値を使用しますが、ここでの目的にはこれ以上必要ありません。

私はclock_gettime()遅延を計るのに基づいて、ミリ秒単位のタイマー機能を使用しています(これは必要な精度です)。

呼び出しはtimer(1)それをリセットし、呼び出しtimer(0)はリセットからの時間を返します。

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

rtこれをコンパイルする-lrtには、ライブラリに対してリンクする必要があります-gccコマンドに追加します。

次に、メインループについて説明します。「ユーザーリクエスト」にスイッチ入力を使用していますが、ネットワーク、タイマーなど何でも使用できます。必要なのは、ブール値をに取得することだけです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.
    }

これは、メインA / C用のパイ制御調光スイッチを実装するのに役立ちますか?私は1)解像度をはるかに小さいものに変更する必要があると想像します(100msごとではなく)、2)単にTRIAC_PINtoを設定するのではなくin、1に設定TRIAC_PINする必要があり、決められた時間(目的の調光器レベル)、そしてTRIAC_PIN0に戻します。これは機能しますか?
-rinogo

私はメインループに仮定、私はまた、行を変更したいと思いますif(in==out) continue;if(out==0) continue;、右、?実は、私はので、多分必要はありませんこと、パイのためのプログラミングに全く新たなんだ-私は(私たちは、ネストされたループがまだ実行されている間、呼び出されるメインループを心配する必要はありませんIE)これは、すべての同期起こっている推測しています
rinogo

(これはすべて、前述のInmojo調光モジュールを使用しています。もちろん、inmojo.com / store / inmojo
market

2
それに問題があります。安定したシステムアクティビティを実現するには、定期的にシステムを制御する必要があります。20ms未満(以内)の時間内に復元することは本当に疑わしいです。そのため、これらの歩留まりによりパルスが欠落し、その結果、電球が点滅します。私それについて質問しましたが、返事はありませんでした。sched_rt_runtime_usとsched_rt_period_usの両方を-1に設定して、システムの横取りを完全に無効にすることもできますが、sched_yield()またはusleep()をまったく実行しないと、問題が発生します。
SF。

2
あなたはタイムスライスを開始すると、あなたが自発的に生じる(またはsched_rt_runtime_usが経過される)までSCHED_FIFOと、それが中断されていない持続するが、システムが保証するものではありません:それはあるとき、あなたはそのタイムスライスを取得します。私の場合、通常の操作では、呼び出し間の時間(タスクにタイムスライスを与える)が最大CPU負荷で0.1秒まで延長することに気付きました。たぶん、その期間は微調整されてより短く強制されるかもしれませんが、どのように私は知りません。
SF。
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.