Показано с 1 по 6 из 6

Тема: Библиотека Util.lib - ошибка в коде ФБ частотомера FREQ_MEASURE

  1. #1

    По умолчанию Библиотека Util.lib - ошибка в коде ФБ частотомера FREQ_MEASURE

    В проекте необходимо измерить частоту импульсных сигналов на дискретных входах ПЛК.
    Частоты 10-50 Гц (период 20-100 мс) и 0.04-0.5 Гц (2-25 с).
    Использовал простой ФБ FREQ_MEASURE (да, в курсе, что есть решения гораздо лучше, но в первом приближении и это устраивало).

    Описание ФБ в мануале "Руководство пользователя по программированию ПЛК в Codesys 2.3", Приложение D --> Библиотека UTIL.LIB --> Генераторы сигналов --> FREQ_MEASURE.

    Обратил внимание, что выходной флаг VALID, отвечающий за "достоверность" измеренной частоты, для сигнала 10-50 Гц ведет себя примерно так, как описано: при наличии сигнала поднимается после заполнения массива усреднения, а при при исчезновении сигнала - сбрасывается.
    А вот для низкочастотных сигналов этот флаг практически всегда сброшен, VALID = FALSE.

    Полез в код ФБ (несущественные фрагменты опущены).
    Код:
    FUNCTION_BLOCK FREQ_MEASURE
    (* FB to measure the frequency of a signal *)
    VAR_INPUT
    	IN:BOOL;				(* input signal *)
    	PERIODS: INT (1..10) :=1;		(* out is the average frequency during PERIODS (number of periods) *)
    	RESET: BOOL;				(* reset measurement *)
    END_VAR
    VAR_OUTPUT
    	OUT:REAL;				(* frequency [Hz]*)
    	VALID:BOOL;				(* FALSE: not yet PERIODS measurements done OR time distance between two rising edges > 3*OUT *)
    END_VAR
    
    (* ====== исполняемый код =========*)
    
    IF RESET THEN
    	(* код инициализации *)
    END_IF
    
    IF IN AND NOT OLDIN THEN	(*rising edge *)
    
    	(* код вычисления усредненной частоты*)
    
    ELSIF INIT AND VALID AND TIME_TO_DWORD(TIME()-OLDT) > 3000*OUT THEN
    	VALID:=FALSE;
    	V:=0;
    	B:=0;
    END_IF
    
    OLDIN:=IN;
    По смыслу флаг VALID должен сбрасываться, когда на входном сигнале нет импульсов в течение 3 последних измеренных периодов.
    Но OUT - это измеренная ЧАСТОТА, поэтому 3 периода (в миллисекундах) есть 3000/OUT, а не 3000*OUT, как в коде.

    Это явный баг. Чем выше частота, тем дольше будет интервал ожидание сигнала, хотя должно быть ровно наоборот.
    Причем даже описание флага неверно ни в комментарии ФБ, ни в мануале по Codesys.

    Исправленный код (без декларации функции и описания перемененных)
    Код:
    IF RESET THEN
    	(* код инициализации *)
    END_IF
    
    IF IN AND NOT OLDIN THEN	(*rising edge *)
    
    	(* код вычисления усредненной частоты*)
    
    ELSIF INIT AND VALID THEN
    	IF OUT <> 0 THEN
    		IF TIME_TO_DWORD(TIME()-OLDT) > 3000/OUT THEN
    			VALID:=FALSE;
    			V:=0;
    			B:=0;
    		END_IF
    	END_IF
    END_IF
    
    OLDIN:=IN;
    Если IF работает в ST, как в паскале или си (и я не знаю, так ли это), то проверку частоты на 0 можно не выносить в отдельный блок, а сразу написать
    Код:
    	IF OUT <> 0 AND TIME_TO_DWORD(TIME()-OLDT) > 3000/OUT THEN
    		VALID:=FALSE;
    		V:=0;
    		B:=0;
    	END_IF
    Ну и еще. При резком уменьшении частоты даже исправленный ФБ будет работать не очень хорошо: флаг VALID будет сбрасываться, пока не придет новый импульс. Например, частота изменяется скачком с 0.5 Гц (2 с) до 0.1 Гц (10 с) - после 6 с ожидания флаг сбросится и 4 с будет в таком состоянии ждать нового импульса. На высоких частотах лаг, естественно, будет меньше. Так вот, для себя сделал еще одну вещь: завел в ФБ дополнительный параметр - интервал ожидания импульса, не зависящий от частоты. Если я знаю примерный диапазон частот, то задав этот интервал, равный, скажем, 2 или 3 максимальным периодам, получу, что флаг VALID не упадет при резком изменении частоты входного сигнала, а свалится только когда импульса не будет в течении заданного интервала. Если же взять интервал равный нулю, то ФБ будет работать как обычно - по тройному интервалу ожидания, зависящему от частоты. Если нужно, выложу код.

    Вообще, прошу проверить все эти выкладки, а то может и накосячил где.

  2. #2

    По умолчанию

    Вообще лишнее
    Ух ты! Что, прямо так в рантайме делить на 0 можно?? Без последствий?
    Если да, то что получится: 3000 или 0xFFFFFFFF?

    И да, как к специалисту, вопрос: IF будет вычислять все свои условия или до результата (на который остальные уже не влияют)?

    Как раз хорошо.
    Это кому как.
    Если на какой-нибудь панели или в Скаде на флажок повешена проверка частоты с генерацией аларма, то будет не гут: это же не сигнал исчез, а просто частота изменилась, а у нас аларм: "хватай мешки, вокзал отходит".

  3. #3

    По умолчанию

    Спасательный круг посреди пустыни самое оно.
    Мда. "Все люди делятся на два сорта: у одних есть заряженный револьвер, а другие копают."
    ОК, буду копать.

    PS. Я понял про проверку OUT на 0. Действительно, не нужна. Заходят в ветку, когда уже не 0.


    А откуда Вы узнали что частота изменилась ДО того как пришел сигнал ? Вы руками щупаете/глазами видите/??
    Да нет же!
    Речь шла про еще одну доработку ФБ.
    Когда известен диапазон частот входного сигнала.
    И я знаю максимально возможный период.
    И буду ждать этот сигнал не больше чем этот период. Или немного больше. Сколько задам.
    И флаг не будет падать.

    А если частота сигнала заранее неизвестна, то поставлю в этом параметре ФБ 0 и он будет работать как обычно.

    N. Турбина улетела к еб..ям.
    Это уже фэйл на этапе проектирования - вешать такой критичный параметр на низкочастотный сигнал.


    Лучше я выложу уже код, а Вы его раскритикуете.

    Код:
    FUNCTION_BLOCK FREQ_MEASURE_EX
    (* FB to measure the frequency of a signal *)
    VAR_INPUT
    	IN:BOOL;				(* input signal *)
    	PERIODS: INT (1..10) :=1;		(* out is the average frequency during PERIODS (number of periods) *)
    	RESET: BOOL;				(* reset measurement *)
    	INTERVAL: TIME := T#0s;			(* absolute interval of signal inactivity *)
    END_VAR
    VAR_OUTPUT
    	OUT:REAL;				(* frequency [Hz]*)
    	VALID:BOOL;				(* FALSE: not yet PERIODS measurements done OR time distance between two rising edges > 3*OUT *)
    END_VAR
    VAR
    	OLDIN: BOOL;
    	INIT: BOOL;
    	OLDT: TIME;
    	DIFF: DWORD;
    	ADIFF: ARRAY[0..9] OF DWORD;
    	V: INT;
    	B:INT;
    	I: INT;
    	INACTIVEINTERVAL: DWORD;
    END_VAR
    
    
    IF RESET THEN
    	B:=0;
    	V:=0;
    	INIT:=FALSE;
    	VALID:=FALSE;
    	OUT:=0;
    	INACTIVEINTERVAL := TIME_TO_DWORD(INTERVAL);
    	RETURN;
    END_IF
    
    IF IN AND NOT OLDIN THEN	(*rising edge *)
    	IF INIT THEN
    		DIFF := TIME_TO_DWORD(TIME()-OLDT);
    		IF Diff > 0 THEN
    			ADIFF[B] := DIFF;
    			B := (B+1) MOD PERIODS;
    			V:= MIN(V+1, PERIODS);
    			IF V=PERIODS THEN
    				OUT := 0;
    				FOR I:=0 TO PERIODS-1 DO
    					OUT := OUT + ADIFF[I];
    				END_FOR
    				OUT := 1000.0 * PERIODS / OUT;
    				VALID:=TRUE;
    			ELSE
    				VALID:=FALSE;
    			END_IF
    		END_IF
    	END_IF
    	INIT := TRUE;
    	OLDT := TIME();
    ELSIF INIT AND VALID THEN
    	IF INACTIVEINTERVAL <> 0 THEN
    		IF TIME_TO_DWORD(TIME()-OLDT) > INACTIVEINTERVAL THEN
    			VALID:=FALSE;
    			V:=0;
    			B:=0;
    		END_IF
    	ELSIF TIME_TO_DWORD(TIME()-OLDT) > 3000 / OUT THEN
    		VALID:=FALSE;
    		V:=0;
    		B:=0;
    	END_IF
    END_IF
    
    OLDIN:=IN;
    Последний раз редактировалось ленивый; 10.05.2020 в 20:00.

  4. #4
    Пользователь Аватар для capzap
    Регистрация
    25.02.2011
    Адрес
    Киров
    Сообщений
    10,243

    По умолчанию

    Цитата Сообщение от Валенок Посмотреть сообщение
    PS
    Напоминаю : За "/OUT" - устная благодарность. И у немцев бывает октоберфест. Да и май сложный.
    стоп стоп, это если код менять а пока что условие начнет выполнятся если есть приращение Diff а оно в свою очередь присвоится ADIFF[B] которая в свою очередь суммируется в OUT и оно никогда не будет равно нулю
    Bad programmers worry about the code. Good programmers worry about data structures and their relationships

    среди успешных людей я не встречала нытиков
    Барбара Коркоран

  5. #5

    По умолчанию

    Хорошо.

    Переименовал ФБ в FREQ_MEASURE_EX.
    Кто захочет использовать - добавит его в Util.lib сам.


    По ключевым вопросам расхождений нет.
    Благодарю за разъяснения и вообще.



    Финальные варианты кодов.

    Исправленный код ФБ FREQ_MEASURE.
    Для использования открыть библиотеку Util.lib, внести исправления в код ФБ FREQ_MEASURE, перекомпилировать библиотеку (F11).
    Код:
    FUNCTION_BLOCK FREQ_MEASURE
    (* FB to measure the frequency of a signal *)
    VAR_INPUT
    	IN:BOOL;				(* input signal *)
    	PERIODS: INT (1..10) :=1;		(* out is the average frequency during PERIODS (number of periods) *)
    	RESET: BOOL;				(* reset measurement *)
    END_VAR
    VAR_OUTPUT
    	OUT:REAL;				(* frequency [Hz]*)
    	VALID:BOOL;				(* FALSE: not yet PERIODS measurements done OR time distance between two rising edges > 3/OUT *)
    END_VAR
    VAR
    	OLDIN: BOOL;
    	INIT: BOOL;
    	OLDT: TIME;
    	DIFF: DWORD;
    	ADIFF: ARRAY[0..9] OF DWORD;
    	V: INT;
    	B:INT;
    	I: INT;
    END_VAR
    
    
    IF RESET THEN
    	B:=0;
    	V:=0;
    	INIT:=FALSE;
    	VALID:=FALSE;
    	OUT:=0;
    	RETURN;
    END_IF
    
    IF IN AND NOT OLDIN THEN	(*rising edge *)
    	IF INIT THEN
    		DIFF := TIME_TO_DWORD(TIME()-OLDT);
    		IF Diff > 0 THEN
    			ADIFF[B] := DIFF;
    			B := (B+1) MOD PERIODS;
    			V:= MIN(V+1, PERIODS);
    			IF V=PERIODS THEN
    				OUT := 0;
    				FOR I:=0 TO PERIODS-1 DO
    					OUT := OUT + ADIFF[I];
    				END_FOR
    				OUT := 1000.0 * PERIODS / OUT;
    				VALID:=TRUE;
    			ELSE
    				VALID:=FALSE;
    			END_IF
    		END_IF
    	END_IF
    	INIT := TRUE;
    	OLDT := TIME();
    ELSIF INIT AND VALID AND TIME_TO_DWORD(TIME()-OLDT) > 3000 / OUT THEN
    	VALID:=FALSE;
    	V:=0;
    	B:=0;
    END_IF
    
    OLDIN:=IN;


    Код ФБ FREQ_MEASURE_EX с расширенными настройками.
    Для использования открыть библиотеку Util.lib, добавить в POU (в папку Signals) новый ФБ FREQ_MEASURE_EX, внести в окно объявления ФБ переменные, внести в окно кода ФБ текст исполняемого кода, перекомпилировать библиотеку (F11).
    Код:
    (* ===== объявление переменных ФБ =====*)
    
        FUNCTION_BLOCK FREQ_MEASURE_EX
        (* FB to measure the frequency of a signal *)
        VAR_INPUT
        	IN:BOOL;				(* input signal *)
        	PERIODS: INT (1..10) :=1;		(* out is the average frequency during PERIODS (number of periods) *)
        	RESET: BOOL;				(* reset measurement *)
        	INTERVAL: TIME := T#0s;			(* absolute interval of signal inactivity *)
        END_VAR
        VAR_OUTPUT
        	OUT:REAL;				(* frequency [Hz]*)
        	VALID:BOOL;				(* FALSE: not yet PERIODS measurements done *)
    						(* OR time distance between two rising edges > INTERVAL if INTERVAL > 0*)
    						(* OR time distance between two rising edges > 3s / OUT if INTERVAL = 0*)
        END_VAR
        VAR
        	OLDIN: BOOL;
        	INIT: BOOL;
        	OLDT: TIME;
        	DIFF: DWORD;
        	ADIFF: ARRAY[0..9] OF DWORD;
        	V: INT;
        	B:INT;
        	I: INT;
        	INACTIVEINTERVAL: DWORD;
        END_VAR
    
    
    (* ===== исполняемый код ФБ =====*)
    
        IF RESET THEN
        	B:=0;
        	V:=0;
        	INIT:=FALSE;
        	VALID:=FALSE;
        	OUT:=0;
        	INACTIVEINTERVAL := TIME_TO_DWORD(INTERVAL);
        	RETURN;
        END_IF
    
        IF IN AND NOT OLDIN THEN	(*rising edge *)
        	IF INIT THEN
        		DIFF := TIME_TO_DWORD(TIME()-OLDT);
        		IF Diff > 0 THEN
        			ADIFF[B] := DIFF;
        			B := (B+1) MOD PERIODS;
        			V:= MIN(V+1, PERIODS);
        			IF V=PERIODS THEN
        				OUT := 0;
        				FOR I:=0 TO PERIODS-1 DO
        					OUT := OUT + ADIFF[I];
        				END_FOR
        				OUT := 1000.0 * PERIODS / OUT;
        				VALID:=TRUE;
        			ELSE
        				VALID:=FALSE;
        			END_IF
        		END_IF
        	END_IF
        	INIT := TRUE;
        	OLDT := TIME();
        ELSIF INIT AND VALID THEN
        	IF INACTIVEINTERVAL <> 0 THEN
        		IF TIME_TO_DWORD(TIME()-OLDT) > INACTIVEINTERVAL THEN
        			VALID:=FALSE;
        			V:=0;
        			B:=0;
        		END_IF
        	ELSIF TIME_TO_DWORD(TIME()-OLDT) > 3000 / OUT THEN
        		VALID:=FALSE;
        		V:=0;
        		B:=0;
        	END_IF
        END_IF
    
        OLDIN:=IN;
    Использование ФБ FREQ_MEASURE_EX
    Код:
    (* объявление глобальных переменных *)
    (* дополнительно дискретный вход 1 в Конфигурации ПЛК должен быть именован как InputSignal  *)
    VAR_GLOBAL
    	FreqMeasure1	:	FREQ_MEASURE_EX;
    	FreqMeasure2	:	FREQ_MEASURE_EX;
    	Frequency1	:	REAL;
    	Frequency2	:	REAL;
    	Valid1	:	BOOL;
    	Valid2	:	BOOL;
    	INIT		:	BOOL := FALSE;
    END_VAR
    
    
    
    
    (* основная программа *)
    
    PROGRAM PLC_PRG
    VAR
    END_VAR
    
    	(* инициализация *)
    IF NOT(INIT) THEN
    	FreqMeasure1(PERIODS:=3, RESET := TRUE, INTERVAL := T#12s);		(* усреднение по 3 периодам, ожидание сигнала не более 12с *)
    	FreqMeasure2(PERIODS:=3, RESET := TRUE);				(* усреднение по 3 периодам, ожидание сигнала 3 последних измеренных периода, как ФБ FREQ_MEASUE *)
    	INIT := TRUE;
    END_IF
    
    	(* измерение частоты *)
    
    	FreqMeasure1.IN := InputSignal;
    	Frequency1 := FreqMeasure1.OUT;
    	Valid1 := FreqMeasure1.VALID;
    
    
    	FreqMeasure2.IN := InputSignal;
    	Frequency2 := FreqMeasure2.OUT;
    	Valid2 := FreqMeasure2.VALID;
    
    	(* подаем на дискретный вход 1 импульсный сигнал с частотой 1 Гц *)
    	(* после выхода измерителей на режим Vald1 = Valid2 = TRUE резко уменьшаем частоту до 0.1 Гц*)
    	(* флаг Valid1 сбросится в FALSE примерно через 3с и восстановится через еще через 7с с новым значением частоты*)
    	(* флаг Valid2 будет удерживаться 12с и дождется нового значения частоты*)
    	(* новое значение частоты будет усредненным и выйдет на 0.1Гц примерно через 30с (3 периода) после изменения*)

  6. #6

    По умолчанию

    Цитата Сообщение от capzap Посмотреть сообщение
    стоп стоп, это если код менять а пока что условие начнет выполнятся если есть приращение Diff а оно в свою очередь присвоится ADIFF[B] которая в свою очередь суммируется в OUT и оно никогда не будет равно нулю
    Проверка на таймаут не производится, пока VALID = FALSE. Оно станет TRUE только после PERIODS сигналов на входе. А тогда частота OUT уже будет не ноль. Нулем она может стать только после ресета. Но тогда VALID опять FALSE и все снова повторится.

Похожие темы

  1. Ответов: 7
    Последнее сообщение: 14.03.2017, 10:00
  2. Где лежит библиотека Util.lib
    от эндрю в разделе ПЛК1хх
    Ответов: 23
    Последнее сообщение: 13.01.2017, 15:54
  3. Библиотека pid_reg2 ошибка подключения
    от Delusion в разделе ПЛК63/73
    Ответов: 4
    Последнее сообщение: 29.03.2012, 12:36
  4. CHARCURVE в Util.lib
    от zilog в разделе ПЛК1хх
    Ответов: 5
    Последнее сообщение: 26.06.2008, 12:41
  5. FREQ_MEASURE И COUNTER В PLC150
    от YURI в разделе ПЛК1хх
    Ответов: 2
    Последнее сообщение: 01.03.2007, 04:03

Ваши права

  • Вы не можете создавать новые темы
  • Вы не можете отвечать в темах
  • Вы не можете прикреплять вложения
  • Вы не можете редактировать свои сообщения
  •