Temporizador de função para o controlador industrial Simatic S7-1200

Mesmo para as séries S7-300 e S7-400, na Etapa 7, as versões clássicas dos cronômetros oferecidos ao desenvolvedor eram suficientes - esses são os cronômetros IEC padrão, implementados como blocos de funções e os cronômetros S5 (que, a propósito, ainda existem para a série S7- 1500). No entanto, em alguns casos, o desenvolvedor não usou ferramentas padrão e implementou seus próprios temporizadores, na maioria das vezes na forma de funções. Tais funções-temporizadores eram necessárias com uma abordagem de “TI” para programação, na qual operavam não com instâncias separadas dos blocos funcionais de equipamentos tecnológicos, com a ligação correspondente de entradas e saídas, mas com matrizes de estruturas. Por exemplo, uma matriz de uma estrutura de tipo de entrada discreta. Ou uma matriz de uma estrutura agregada. Essa abordagem de programação tem o direito de existir, pois permite salvar seriamente a memória de trabalho da CPU, mas,por outro lado, dificulta a leitura do código do programa. Um programador de terceiros e com uma simples aparência de um programa no LAD não consegue descobrir imediatamente, mas montes de índices, matrizes e funções para processá-los estão fora de questão; aqui, sem documentação para o software (e sem meio litro, é claro), em nenhum lugar.

Essas matrizes de estruturas eram normalmente processadas em funções. Em princípio, nada impedia o processamento de blocos funcionais, mas sempre havia uma pergunta importante - como trabalhar com temporizadores nesses casos? Os temporizadores padrão assumem um número (S5) ou uma instância de um bloco de funções (IEC). Isso, lembro a você, trata do processamento de matrizes de estruturas para CLPs Simatic clássicos e de “torcer” os números do temporizador nessas estruturas e, mais ainda, as instâncias são difíceis ou simplesmente impossíveis.

Por esse motivo, criamos nossa própria funcionalidade de timer como uma função. Em princípio, para a operação de qualquer timer, você precisa saber apenas algumas coisas - o estado da entrada, a configuração da hora e quanto tempo se passou desde a ativação.

Para as séries 300 e 400, havia duas maneiras de determinar esse tempo. O primeiro é examinar o tempo de execução do OB1 principal (existe uma variável correspondente no próprio OB1) ou OBs cíclicos e aumentar o acumulador de tempo interno a cada chamada do temporizador, desde que a "verdade" seja inserida. Não é uma boa opção, pois esse tempo é diferente para OB1 e OBs cíclicos. O segundo método é a função do sistema TIME_TCK, que, a cada chamada, retornava um único valor - o contador interno de milissegundos do processador central.

imagem

Assim, para um timer do tipo TON (em atraso), o algoritmo de operação foi o seguinte:

  • na borda ascendente da solicitação de resposta, redefina a saída e lembre-se do valor atual do temporizador do sistema TIME_TCK
  • «» , ( , TIME_TCK 0 (2 ^ 31 — 1), ). , . , «», — «»
  • «»,

Com o advento da série "milésima", a situação mudou um pouco. O fato é que a linha S7-1500 herdou o suporte para a chamada do sistema TIME_TCK e os amantes da abordagem "em pé e na rede" (de que outra forma você pode chamar um programa que apenas processa matrizes de estruturas enquanto opera com índices assustadores?) calmamente continuam a usar suas melhores práticas.

A série S7-1200 de controladores básicos é baseada em uma arquitetura diferente e possui diversas diferenças em relação ao S7-1500. Incluindo a falta de uma chamada do sistema TIME_TCK. Nas fileiras de desenvolvedores que não têm flexibilidade suficiente de pensamento, a insatisfação desapareceu - é impossível executar cópias / pastas de programas antigos. No entanto, a tarefa de determinar quanto tempo passou desde que a chamada anterior pode ser executada usando a função de tempo de execução.

Esta função retorna o tempo decorrido desde a chamada anterior, em segundos, como um número real de precisão dupla LREAL. Os detalhes são descritos na ajuda. Para fins internos, é necessária uma variável MEM adicional (também do tipo LREAL).

Darei as fontes da primeira aproximação da função e darei algumas notas.

Declaração de função:

FUNCTION "PerversionTON" : Void
{ S7_Optimized_Access := 'TRUE' }
VERSION : 0.1
   VAR_INPUT 
      IN : Bool;   //  
      PT : Real;   //    
   END_VAR

   VAR_OUTPUT 
      Q : Bool;   //  
   END_VAR

   VAR_IN_OUT 
      INPrv : Bool;
      MEM : LReal;
      TimeACC : UDInt;
   END_VAR

   VAR_TEMP 
      udiCycle : UDInt;
      udiPT : UDInt;
   END_VAR

Com as entradas / saídas, tudo fica claro: IN, Q e PT. Eu defino a configuração da hora na forma de uma real, são segundos. Só queria (mas em vão, mas mais sobre isso abaixo). Mais sobre as variáveis ​​da área InOut. Como temos uma função, não temos uma área STAT, não há variáveis ​​que retenham seu valor durante a próxima chamada de função e essas variáveis ​​são necessárias:

INPrv - para determinar a borda positiva da solicitação

MEM - variável auxiliar para a chamada do sistema para o tempo de execução do trabalho

TimeACC - acumulador de tempo , que armazenará o número de microssegundos do atraso em execução no momento.

As variáveis ​​TimeACC, udiCycle e udiPT são especificadas no formato UDINT, um número inteiro não assinado, 4 bytes. Apesar de ter especificado o tempo como real e a função de tempo de execução retornar real tanto quanto precisão dupla, prefiro executar operações simples de soma e comparação com operandos inteiros para economizar tempo do processador. O tempo no meu caso é levado em consideração para o microssegundo. O motivo é simples - se você aumentar o tempo para milissegundos, com o OB1 quase vazio (por exemplo, se apenas um temporizador for chamado em todo o programa do controlador e nada mais), serão possíveis "pulos" de ciclos, às vezes o programa é executado por 250 μs. Porém, nesse caso, o valor máximo permitido do acumulador de tempo será de 4.294 segundos, quase 4.295 (2 ^ 32 - 1 = 4.294.967.295). Não há nada a ser feito, tal "otimização" requer sacrifício.

Texto da função.

#udiCycle := LREAL_TO_UDINT(RUNTIME(#MEM) * 1000000); //     
#udiPT := REAL_TO_UDINT(#PT * 1000000); //   

IF (#IN AND (NOT #INPrv)) THEN //         
    #TimeACC := 0;
    #Q := FALSE;
ELSIF (#IN AND #INPrv) THEN //     ""
    #TimeACC += #udiCycle; //     "    "
    IF #TimeACC >=  #udiPT THEN //      
        #Q := TRUE; //  ""
        #TimeACC := #udiPT; //  
    ELSE //      
        #Q := FALSE; // 
    END_IF;
ELSE //    -      
    #Q := FALSE;
    #TimeACC := 0;
END_IF;

#INPrv := #IN; //  

ENO := #Q; // ENO         LAD  FBD

As duas primeiras linhas são o recálculo da configuração do timer do número de segundos especificado no formato REAL para o número de microssegundos. O tempo em microssegundos decorrido da chamada de bloco do programa anterior também é determinado.

Além disso, o algoritmo é o seguinte, e eu já o dei:

  • na borda ascendente da entrada IN, restaure a saída Q e reinicie o acumulador
  • se a "verdade" continuar a ser inserida, aumentamos o acumulador de tempo pelo valor já conhecido do udiCycle e o comparamos com a configuração de tempo. Se a configuração do tempo for excedida, o temporizador funcionou, dê a saída "true", caso contrário, dê a saída "false"
  • no caso de aplicar entrada falsa à entrada IN, redefina a saída Q e redefinir o acumulador de tempo.

No final da função, para determinar a borda da entrada IN, lembre-se do seu valor anterior. Também forneça à saída ENO (ao usar uma função em linguagens gráficas, como LAD) o valor da saída Q.

Asseguramos que a função esteja funcionando, após o que se torna interessante avaliar sua velocidade e, se necessário, melhorar (já parece à primeira vista que vários cálculos vão para ocioso e desperdiçando tempo da CPU em vão). Para avaliar o desempenho, declaro uma matriz de 1000 estruturas de dados de timer.

Declaração da estrutura. Seus campos duplicam as variáveis ​​de entrada e saída da função timer.

TYPE "typePervTONdata"
VERSION : 0.1
   STRUCT
      IN : Bool;   //  
      PT : Real;   //   
      Q : Bool;   //  
      INPrv : Bool;   //    
      MEM : LReal;   //    
      TimeACC : UDInt;   //  
   END_STRUCT;

END_TYPE

Uma matriz de estruturas é declarada no bloco de dados globais do TortureTON:

TONs : Array[0..999] of "typePervTONdata";

O código a seguir é executado no bloco organizacional OB1:

FOR #i := 0 TO 999 DO
    "TortureTON".TONs[#i].IN := "startton";
    "PerversionTON"(IN := "TortureTON".TONs[#i].IN,
                    PT := "TortureTON".TONs[#i].PT,
                    Q := "TortureTON".TONs[#i].Q,
                    INPrv := "TortureTON".TONs[#i].INPrv,
                    MEM := "TortureTON".TONs[#i].MEM,
                    TimeACC := "TortureTON".TONs[#i].TimeACC);
END_FOR;

Anunciados 1000 "instâncias" de temporizadores, cada um com um tempo de 10 segundos. Todos os 1000 temporizadores começam a contar o tempo pelo valor da variável do marcador startton.

Inicio as funções de diagnóstico do controlador (S7-1214C DC / DC / DC, versão FW 4.4, versão Step7 - V16) e observo o tempo do ciclo de varredura do controlador. Em "inativo" (quando "falso" chega à entrada dos cronômetros), todo o mil é processado em média por 36 a 42 milissegundos. Durante a contagem regressiva de dez segundos, essa leitura cresce em cerca de 6-8 milissegundos e às vezes rola por 50 ms.

Examinamos o que pode ser aprimorado no código de função. Primeiro, as linhas no início do bloco do programa:

#udiCycle := LREAL_TO_UDINT(RUNTIME(#MEM) * 1000000); //     
#udiPT := REAL_TO_UDINT(#PT * 1000000); //   

Eles sempre são chamados, independentemente de o cronômetro contar o tempo, não contar ou já contar. Um grande desperdício de dinheiro é carregar a CPU não muito poderosa da série 1200 com cálculos envolvendo materiais de dupla precisão. É razoável transferir ambas as linhas para a parte do código que processa a contagem regressiva (se a "verdade" continuar aparecendo). Também é necessário duplicar o cálculo do udiCycle em um código que processa uma margem positiva na entrada do timer. Isso deve aliviar a "operação ociosa" do timer quando o valor de entrada for falso. Na prática, os temporizadores nos controladores lógicos programáveis ​​geralmente trabalham "inativos". Por exemplo, o tempo de filtragem do retorno do contato é dezenas de milissegundos. O pulso de controle de uma saída discreta é de algumas centenas de milissegundos, geralmente de 0,5 a 1,0 segundos.O tempo para monitorar a execução do comando da unidade (por exemplo, o tempo em que a válvula se abre completamente) é de dezenas de segundos a vários minutos. O PLC na produção funciona 24 horas por dia e 365 (e às vezes mais!) Dias por ano. Ou seja, na maioria das vezes a entrada do timer é "zero" e o timer não conta nada, ou uma "unidade" chega por um longo tempo e o timer já conta tudo. Para descarregar a CPU do segundo estágio inativo (o temporizador já contou), é necessário verificar no estágio “a entrada continua recebendo a verdade” - se o temporizador já contou o tempo todo e configurou a saída como verdadeira. Nesse caso, nenhum cálculo deve ser realizado.na maioria das vezes, a entrada do timer é "zero" e o timer não conta nada, ou uma "unidade" chega por um longo período de tempo e o timer já conta tudo. Para descarregar a CPU do segundo estágio inativo (o temporizador já contou), é necessário verificar no estágio “a entrada continua recebendo a verdade” - se o temporizador já contou o tempo todo e configurou a saída como verdadeira. Nesse caso, nenhum cálculo deve ser realizado.na maioria das vezes, a entrada do timer é "zero" e o timer não conta nada, ou uma "unidade" chega por um longo período de tempo e o timer já conta tudo. Para descarregar a CPU do segundo estágio inativo (o temporizador já contou), é necessário verificar no estágio “a entrada continua recebendo a verdade” - se o temporizador já contou o tempo todo e configurou a saída como verdadeira. Nesse caso, nenhum cálculo deve ser realizado.

Para fazer essas alterações, é necessário transferir a saída do Q timer da área OUTPUT para a área IN_OUT, e o valor da saída será armazenado em variáveis ​​externas (neste exemplo, em uma matriz de estruturas). Após o refinamento, todo o código de função, incluindo a declaração, é o seguinte:

FUNCTION "PerversionTON" : Void
{ S7_Optimized_Access := 'TRUE' }
VERSION : 0.1
   VAR_INPUT 
      IN : Bool;   //  
      PT : Real;   //    
   END_VAR

   VAR_IN_OUT 
      Q : Bool;   //  
      INPrv : Bool;
      MEM : LReal;
      TimeACC : UDInt;
   END_VAR

   VAR_TEMP 
      udiCycle : UDInt;
      udiPT : UDInt;
   END_VAR


BEGIN
	IF (#IN AND (NOT #INPrv)) THEN //         
	    #TimeACC := 0;
	    #Q := FALSE;
	    #udiCycle := LREAL_TO_UDINT(RUNTIME(#MEM) * 1000000); // " "  
	ELSIF (#IN AND #INPrv) THEN //     ""
	    IF (NOT #Q) THEN
	        #udiCycle := LREAL_TO_UDINT(RUNTIME(#MEM) * 1000000); //     
	        #udiPT := REAL_TO_UDINT(#PT * 1000000); //   
	        #TimeACC += #udiCycle; //     "    "
	        IF #TimeACC >= #udiPT THEN //      
	            #Q := TRUE; //  ""
	            #TimeACC := #udiPT; //  
	        END_IF;
	    END_IF;
	ELSE //    -      
	    #Q := FALSE;
	    #TimeACC := 0;
	END_IF;
	
	#INPrv := #IN; //  
	
	ENO := #Q; // ENO         LAD  FBD
END_FUNCTION

Depois disso, o tempo de execução melhora: o processamento do tempo ocioso dos temporizadores é de 23 ms, com um tempo de filtragem de trabalho de 37 a 40 ms.

Este código de função não verifica se há um valor inválido na configuração do temporizador - um valor negativo (se o material for convertido em um número inteiro não assinado, a configuração será distorcida) ou um valor superior a 4294,9 segundos (a configuração de tempo transbordará e distorcerá). Você deve controlar o valor do valor de PT no código ou confiar a tarefa de verificar o intervalo da configuração de tempo (de 0 a 4294,9 segundos) no sistema do operador de nível superior. A verificação da faixa por meio do programa PLC aumenta o tempo de processamento para aproximadamente 45-46 ms (e, em geral, a maneira mais correta é ajustar o tempo do temporizador não no formato REAL, mas no formato UDINT em milissegundos e sem sentido).

O projeto de aplicativo com um cronômetro para o ambiente do TIA Portal Etapa 7 versão 16 está disponível aqui .

All Articles