Реле с нулевым переходом

13

Как я могу запрограммировать переключатель (на основе полупроводникового реле или симистора), который запускает питание при пересечении нуля?

Для тех, кто не знаком с предметом: Включите питание 230 В, когда синусоида линии электропередачи пересекает ноль - в результате минимизируются электромагнитные помехи, вызванные быстрым скачком тока.

В частности, я бы предпочел перенести как можно больше в программное обеспечение. Схема обнаружения, состоящая из небольшого трансформатора, диода и пары резисторов для контроля уровней и токов, выдает «1», когда входная мощность переменного тока находится в положительной половине, «0» - в отрицательной, подключенной к входному выводу GPIO. Выходной сигнал состоит из нескольких твердотельных реле и базовых элементов, необходимых для их работы (подтягивания и т. Д.), Подключенных к выходным выводам GPIO.

Проблема заключается в синхронизации: при 50 Гц переменного тока мы получаем 100 пересечений нуля в секунду, один полупериод равен 10 мс. Чтобы добраться до разумного расстояния от пересечения нуля, чтобы поддерживать низкий уровень электромагнитных помех, мы не должны активировать выход более чем на 10% после (или до) события пересечения нуля, что означает допуск + -1 мс. Это не означает, что время реакции составляет 1 мс - мы можем разумно ожидать, что следующий переход через ноль произойдет ровно через 10 мс после первого или четвертого - 40 мс. Речь идет о гранулярности - если мы допустим 20 мс для реакции, это должно быть между 19 и 21 мс, а не 18 или 22.

Как я могу реализовать такой выход GPIO с триггерным таймером либо в пределах 1 мс, поскольку вход обнаруживает фронт, либо в пределах фиксированного кратного 10 мс с тех пор - предпочтительно с учетом некоторого отрицательного смещения (скажем, трансформатор и реле вводят задержку 1,6 мс; поэтому я хочу, чтобы триггер сработал через 8,4+ (n * 10) мс с момента входного импульса, таким образом, смещение нейтрализует задержку, вносимую схемой.) - все это, конечно, «по требованию пользователя», скажем, пользователь пишет «1 msgstr "в файл / sys / class / ... и при ближайшей (грубо) возможности вывод будет" включен ". Пользователь пишет «0», и когда наступает переход через ноль, конкретное реле отключается.

Я считаю, что это потребует написания или взлома модуля ядра. Не могли бы вы указать мне, что обрабатывает выводы GPIO Raspberry Pi в ядре, и какие таймеры я могу подключить к нему (если они еще не установлены), чтобы получить такую ​​функциональность?

Научная фантастика
источник
довольно интересный проект, о котором вы говорите! Просто для приблизительной оценки: сначала я бы связал синусоидальную волну от исходного сигнала 50 Гц через некоторую логику триггера Шмитта с GPIO. Оттуда генерируйте прерывание по нарастающему или падающему фронту сигнала. Теперь вы заблокированы на частоте 50 Гц переменного тока и можете «предсказать», когда произойдет следующее пересечение нуля. Конечно, это связано с программированием драйвера ядра. Google - твой друг :-)
sparkie
Взгляните на модуль диммера переменного тока Inmojo . Я успешно клонировал это для проекта, используя их документацию с открытым исходным кодом. Существует также пример кода Arduino, который может помочь вам в прохождении логики вашего кода.
Баттерс
@Butters: Я боюсь, что разрыв между программированием Arduino и программированием модуля Linux Kernel сделает программную часть бесполезной для меня, но схемы оборудования покрывают 100% того, что мне нужно - спасибо, вы сэкономили немало моей работы - Разница между этими двумя проектами заключается только в программном обеспечении (диммер должен циклически переключать симистор, в то время как коммутатор просто включает / выключает его один раз за переключение коммутатора.)
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_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. Будет ли это работать?
Риного
Я полагаю , в основном цикле, я также хочу , чтобы изменить линию if(in==out) continue;на if(out==0) continue;, не так ли? На самом деле, я совершенно новичок в программировании для pi, так что, возможно, в этом нет необходимости - я предполагаю, что все это происходит синхронно (то есть нам не нужно беспокоиться о вызове основного цикла, пока вложенные циклы еще выполняются)
Риного
(Конечно, все это с использованием вышеупомянутого модуля диммера Inmojo : inmojo.com/store/inmojo-market/item/… )
rinogo
2
Есть проблема с этим. Для стабильной работы системы вы ДОЛЖНЫ периодически передавать управление системе, и я действительно сомневаюсь, что вы восстановите ее в течение короткого (менее) 20 мс. Таким образом, эти выходы приведут к пропущенным импульсам и, как результат, лампочка будет мигать. Я задал вопрос об этом, но не получил ответов. Вы можете установить для sched_rt_runtime_us и sched_rt_period_us значение -1, чтобы полностью отключить упреждение системы, но если вы вообще не используете sched_yield () или usleep (), это неизбежно создает проблемы.
SF.
2
То есть: с SCHED_FIFO, как только вы запускаете временной интервал, он длится непрерывно до тех пор, пока вы добровольно не уступите (или не истечет sched_rt_runtime_us), но система не гарантирует, когда вы получите этот временной интервал. В моем случае я заметил, что при нормальной работе время между вызовами (с выделением временных интервалов для задачи) может увеличиваться до 0,1 с при максимальной загрузке процессора. Может быть, этот период можно откорректировать и принудительно сократить, но я не знаю как.
SF.