过零激活继电器


13

如何编程过零电源触发的开关(基于固态继电器或三端双向可控硅开关)?

对于不熟悉此主题的人员:打开230V电源,当电源线的正弦波过零时-结果是将电流快速尖峰引起的电磁干扰降至最低。

具体来说,我希望尽可能多地迁移到软件中。当交流输入功率为正一半时,检测电路由一个小变压器,一个二极管和一对电阻器组成,以保持电平和电流处于检测状态,当连接到输入GPIO引脚时,检测电路提供“ 1”,而为负“ 0”。输出包括一些固态继电器和使它们继续运行的基本要素(上拉等),并连接到输出GPIO引脚。

问题是时序:使用50Hz AC时,我们在一秒钟内得到100个零交叉,一个半周期为10ms。为了使过零距离保持合理的距离以保持较低的EMI,我们在过零事件发生之前(或之前)不应激活超过10%的输出,这意味着+ -1ms容差。这并不意味着1毫秒的反应时间-我们可以合理预期下一个零交叉恰好发生在第一个零点之后的10毫秒,或者是第四个零点-40毫秒。关于粒度-如果我们允许20毫秒的反应时间,则必须在19到21毫秒之间,而不是18或22。

我该如何实现这样的计时器-在输入检测到边沿后的1ms内触发输出GPIO,或者在此后的10ms的固定倍数内触发输出GPIO-最好允许一些负偏置(例如,变压器和继电器引入1.6ms延迟;因此我希望触发器自输入脉冲起关闭8.4+(n * 10)ms,这样偏置就抵消了电路引入的延迟。)-当然,这都是“根据用户要求”,例如,用户写“ 1到/ sys / class / ...文件,并在最近(大约)机会将输出打开。用户写“ 0”,并且过零到达时,特定的继电器断开。

我相信这将需要编写或修改内核模块。您能否指出我在内核中处理Raspberry Pi的GPIO引脚的是什么,我可以将其附加到哪种定时器(除非已经有一些定时器)才能获得这种功能?


您谈论的非常有趣的项目!仅作一个粗略的估计:我首先将来自50Hz原始信号的正弦波通过一些施密特触发器逻辑连接到GPIO。从那里在信号的上升沿或下降沿产生一个中断。您现在被锁定在50Hz AC上,可以“预测”下一个零交叉点的发生时间。当然,这涉及一些内核驱动程序编程。Google是您的朋友:-)
sparkie 2013年

看看Inmojo的交流调光器模块。我已经使用他们的开源文档成功地将其克隆到了项目中。还有一些Arduino样本鳕鱼可以帮助您理解代码的逻辑。
巴特斯

@Butters:恐怕对Arduino进行编程与对Linux内核模块进行编程之间的差距会使软件方面对我无用,但是硬件原理图满足了我所需要的100%-谢谢,您节省了我很多的工作-这两个项目之间的区别仅在于软件(调光器必须周期性地开关三端双向可控硅开关,而开关每次拨动一次开关才将其开/关。)
SF。

...当然是硬件的100%。
SF。

Answers:


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_us每个cat /proc/sys/kernel/sched_rt_period_us毫秒的时间段中获取毫秒数,执行不间断,而在这段时间内没有被抢占的风险(实际上,默认情况下,BerryBoot每秒有0.95s的时间)。这些值,但在这里我不需要更多。

我正在使用计时器功能(以毫秒为单位)(这大约是我需要的精度),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实施pi控制的调光开关是否可行?我想我会1)分辨率更改为更小的(而不是每100ms)和2),而不是只设置TRIAC_PINin,我必须设置TRIAC_PIN为1,等待确定的时间量(按比例所需的调光器级别),然后将其设置TRIAC_PIN回0。这行得通吗?
rinogo

我想在主循环中,我也想将行更改if(in==out) continue;if(out==0) continue;,对吗?实际上,我对pi编程完全陌生,所以也许没有必要-我猜这都是同步发生的(即,我们不必担心嵌套循环仍在执行时调用主循环)
rinogo 2013年

(当然,这全部使用前面提到的Inmojo调光器模块:inmojo.com/store/inmojo-market/item/…
rinogo 2013年

2
这是有问题的。为了稳定的系统活动,您必须定期对系统进行控制,我真的怀疑您是否会在短于20ms(不到20ms)的时间内恢复它。因此,这些产量将导致脉冲丢失,结果灯泡闪烁。我了一个问题,但是没有答案。您可以将sched_rt_runtime_us和sched_rt_period_us都设置为-1以完全禁用系统抢占,但是如果根本不使用sched_yield()或usleep(),那肯定会造成问题。
SF。

2
也就是说:使用SCHED_FIFO,一旦您开始一个时间片,它就会持续不间断,直到您自愿屈服(或sched_rt_runtime_us已逝去)为止,但是系统无法保证何时获得该时间片。就我而言,我注意到在正常操作下,两次调用之间的时间(给任务分配时间片)可以在CPU负载最大的情况下延长至0.1s。也许那个时期可以被微调并且被迫缩短,但是我不知道如何。
SF。
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.