Реле з нульовим перетином


13

Як я міг займатися програмуванням перемикача (заснованого на твердотільному реле або триака), який спрацьовує на потужність, що перетинає нуль?

Для цих питань, які не знайомі з темою: Увімкніть живлення 230 В, коли синусоїда лінії електропередачі перетне нуль - результат мінімізує електромагнітні порушення, спричинені швидким сплеском струму.

Зокрема, я вважаю за краще перенести якомога більше програмного забезпечення. Схема виявлення, що складається з малого трансформатора, діода і пари резисторів для контролю рівнів і струмів у контролі, забезпечує "1", коли вхідна потужність змінного струму знаходиться в позитивній половині, "0" в негативному, прикріплена до входу GPIO-штифта. Вихід складається з декількох твердотільних реле та голих предметів, щоб підтримувати їх роботу (підтяжки тощо), прикріплених до вихідних штифтів GPIO.

Проблема в термінах: за допомогою змінного струму 50 Гц ми отримуємо 100 нульових перетинів за секунду, один півцикл - 10 мс. Щоб потрапити на розумну відстань від нульового перетину, щоб зберегти вказану EMI низькою, ми не повинні активувати вихід більш ніж на 10% після (або раніше) події нульового перетину, це означає + -1 мс допуску. Це не означає час реакції 1 мс - ми можемо з розумом очікувати, що наступний перетин нуля відбудеться точно через 10 мс після першого, або четвертого - 40 мс. Йдеться про деталізацію - якщо ми допускаємо 20 мс для реакції, вона повинна бути між 19 і 21 мс, а не 18 або 22.

Як я можу реалізувати такий вихідний GPIO на таймері або в межах 1 мс, оскільки вхід виявляє край, або в межах фіксованого кратного 10 мс відтоді - бажано з урахуванням деякого негативного зміщення (скажімо, трансформатор і реле вводять затримку 1,6 мс; тому я хочу, щоб тригер вийшов з 8.4+ (n * 10) мс, оскільки вхідний імпульс, таким чином зміщення протидіє затримці, введеній ланцюгом.) - звичайно, "на вимогу користувача", скажімо, користувач пише "1 "до файлу a / sys / class / ... і при найближчій (приблизно) можливості висновок" увімкнено ". Користувач пише "0", і коли нульовий перехід приходить, конкретні реле відключаються.

Я вважаю, що для цього потрібно буде написати або зламати модуль ядра. Не могли б ви вказати мені на те, що обробляє GPIO-штифти Raspberry Pi в ядрі, і які таймери я можу приєднати до нього (якщо таких вже немає), щоб отримати такий функціонал?


досить цікавий проект, про який ви говорите! Тільки для того, щоб дати приблизну оцінку: я спершу інтерфейс синусоїди від вихідного сигналу 50 Гц через деяку логіку тригера шмітта до GPIO. Звідти генерують переривання на висхідній чи падаючій частині сигналу. Зараз ви заблоковані на 50 Гц змінного струму і могли б "передбачити", коли відбудеться наступне перетин нуля. Звичайно, це стосується деяких програмувань драйверів ядра. Google - ваш друг :-)
sparkie

Погляньте на модуль змінного струму Inmojo . Я успішно клонував це для проекту, використовуючи їх документацію з відкритим кодом. Існує також деякий зразок тріски Arduino, який може допомогти вам через логіку вашого коду.
Масло

@Butters: Я боюся, що розрив між програмуванням Arduino та програмуванням модулем ядра Linux зробить сторону програмного забезпечення для мене марною, але апаратні схеми охоплюють 100% того, що мені потрібно - дякую, ви зберегли досить багато моєї роботи - Різниця між двома проектами полягає лише в програмному забезпеченні (диммер повинен циклічно перемикати тріак, тоді як комутатор просто вмикає / вимикає один раз за включення перемикача.)
SF.

... 100% апаратної сторони звичайно.
СФ.

Відповіді:


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,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.
    }

Чи буде це спрацьовувати у впровадженні пі-керованого диммерного вимикача для електромережі? Я вважаю , я повинен був би 1) змінити дозвіл на що - то набагато менше (замість кожного 100мса) і 2) замість того , щоб просто встановивши TRIAC_PINна in, я мав би встановити TRIAC_PIN1, почекати певну кількість часу (пропорційно бажаний рівень затемнення), а потім встановіть TRIAC_PINзначення 0. Чи це буде працювати?
rinogo

Я думаю, що в основному циклі я також хотів би змінити рядок if(in==out) continue;на if(out==0) continue;, правда? Насправді я абсолютно новачок у програмуванні для pi, тому, можливо, це не потрібно - я здогадуюсь, що це все відбувається синхронно (тобто нам не потрібно турбуватися про те, що буде викликано основний цикл, поки виконуються вкладені петлі)
rinogo

(Для цього використовується звичайно згаданий вище модуль диммер Inmojo : inmojo.com/store/inmojo-market/item/… )
rinogo

2
У цьому є проблема. Для стабільної системної активності Ви ОБОВ'ЯЗКОВО періодично контролювати систему, і я дуже сумніваюся, що Ви відновите її протягом часу (менше ніж) 20 мс. Отже, ці врожаї призведуть до пропущених імпульсів і, як результат, лампочка блимає. Я задав питання з цього приводу, але не отримав відповіді. Ви можете встановити і sched_rt_runtime_us, і sched_rt_period_us до -1, щоб повністю відключити системне попередження, але якщо ви взагалі не sched_yield () або usleep (), це може створити проблеми.
СФ.

2
Тобто: щойно ви запускаєте часовий відрізок, SCHED_FIFO триває безперервно, поки ви не почнете добровільно (або не закінчиться sched_rt_runtime_us), але система не гарантує, коли ви отримаєте цей часовий відрізок. У моєму випадку я помітив, що в звичайній роботі час між дзвінками (надання часових відрізків завданням) може подовжуватися до 0,1 с при максимальному завантаженні процесора. Можливо, цей період може бути налагоджений і примушений коротший, але я не знаю як.
СФ.
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.