Você está na página 1de 10

Eu costumo comentar com as pessoas que o divisor de águas entre quem

pode afirmar que conhece, e bem o PLC da Siemens e quem sabe o usar o
PLC, é exatamente o conhecimento e o uso do tipo ANY.
Genericamente falando o ANY é um dado muito pretensioso, pois ele pode
ser qualquer coisa e apontar para qualquer coisa dentro do PLC. Tradução
literal, ANY pode ser qualquer coisa. E ele é. Veja as figuras abaixo.

Formatação do ANY para variáveis na memória.

Formatação do ANY para blocos de dados (parâmetros).

Essas figuras documentam a formatação do dado ANY, que possui em sua


estrutura 80 bits. Com essa formatação ele pode simplesmente apontar
para qualquer área no PLC. Isso é mais do que ser uma forma de
endereçamento variável.
Geralmente, associamos o termo endereçamento variável ou
endereçamento indireto ao uso de dados que ora apontam para uma
região de memória e ora apontam para outra região. Não é esse o caso,
pois o ANY pode inclusive apontar para funções, atuando quase que como
um ponteiro para função do C/C++.
Se você precisa somente de um ponteiro que aponte para endereços de
memória, você poderia utilizar o tipo POINTER do PLC.
Veja a figura abaixo.
Formatação do Pointer

O uso do POINTER e ANY de forma a apontarem para endereços de


memória são muito semelhantes, sendo que o POINTER possui em sua
estrutura 48 bits, mas por hora me atentarei ao uso do ANY, que é muito
mais completo e muito mais mutante.
Vamos analisar a estrutura completa do ANY.
Primeiramente vamos analisar a estrutura apontando para endereços de
variáveis na memória do PLC.

1. Byte 0:
Indica o header do ANY, sendo que no S7 é obrigatório que esse byte
possua o valor 10h (B#16#10).
2. Byte 1:
Indica o tipo de dados representado pela ANY, podendo ser:

Código Hexadecimal Tipo do Dado Descrição


B#16#00 NIL Ponteiro Nulo
B#16#01 BOOL Bits
B#16#02 BYTE Byte (8 bits)
B#16#03 CHAR Caracteres (8 bits)
B#16#04 WORD Palavra (16 bits)
B#16#05 INT Inteiro (16 bits)
B#16#06 DWORD Dupla Palavra (32 bits)
B#16#07 DINT Duplo Inteiro (32 bits)
B#16#08 REAL Número de Ponto
Flutuante (32 bits)
B#16#09 DATE Data
B#16#0A TIME_OF_DAY (TOD) Hora do Dia
B#16#0B TIME Tempo
B#16#0C S5TIME Tempo tipo S5TIME
B#16#0E DATE_AND_TIME (DT) Data e Hora (64 bits)
B#16#13 STRING Texto

3. Byte 2 e Byte 3 (Word 2):


Indica o fator de repetição, ou seja, quantos dados esse ANY
representa.
4. Byte 4 e Byte 5 (Word 4):
Indica o número do DB, caso o ANY esteja representando dados de
um DB em específico, ou o valor zero caso não aponte para nenhum
DB.
5. Byte 6:
Indica o tipo de memória representada pelo ANY, podendo ser:

Código Hexadecimal Área Descrição


B#16#81 I Área de Entrada
B#16#82 Q Área de Saída
B#16#83 M Área de Bit Memory
(Flags ou Markers)
B#16#84 DB Bloco de Dados
B#16#85 DI Bloco de Dados
Instanciados
B#16#86 L Área Local (L stack)
B#16#87 V Área de Local Anterior

6. Byte 7, 8 e 9:
Nesses bytes está representado o endereço (no
padrão Byte.Bit,ou 00000bbbbbbbbbbbbbbbb.xxx, onde b=número
do byte e x número do bit).

Agora vamos analisar a estrutura apontando para parâmetros de blocos na


memória do PLC

1. Byte 0:
Indica o header do ANY, sendo que no S7 é obrigatório que esse byte
possua o valor 10h (B#16#10).
2. Byte 1:
Indica o tipo de dados representado pela ANY em questão, podendo
ser:
Código Hexadecimal Tipo do Dado Descrição
B#16#17 BLOCK_FB Número do FB
B#16#18 BLOCK_FC Número do FC
B#16#19 BLOCK_DB Número do DB
B#16#1A BLOCK_SDB Número do SDB
B#16#1C COUNTER Número do Contador
B#16#1D TIMER Número do
Temporizador

3. Byte 2 e Byte 3 (Word 2):


Não relevantes, sendo que o valor dessa Word será sempre 1.
4. Byte 4 e Byte 5 (Word 4):
Não relevante, sendo que o valor dessa Word será sempre 0.
5. Byte 6:
Não relevante, sendo que o valor dessa Word será sempre 0.
6. Byte 8 e 9 (Word 8):
Indica o número do FB, FC, DB, SDB, Contador ou Temporizador
representado pelo ANY.

Bom, até aqui eu somente fiz uma tradução simples do help do S7. O que
por um lado é bom, e por outro é ruim.
O bom é que por mais complexo que pareça, o TIPO ANY é simples de
entender, o ruim é que o help básico do S7 não contempla bons exemplos
com o ANY. O que pode ser facilmente corrigido, fazendo uma procura
no www4.ad.siemens.de.

Mas façamos o seguinte, outro dia o Williams me perguntou como fazer


um FC para reescrever os valores das entradas.
De bate pronto eu comentei com ele que é plenamente possível fazer isso
com:
A I0.0
NOT
= I0.0
Ou seja, se o código acima (em STL) estiver rodando na primeira linha do
OB1, a entrada I0.0 será invertida a cada começo de ciclo de CPU (observe
que estou desconsiderando a possibilidade de utilizar-se o particionamento
de imagem de periferia do S7) e terá o valor invertido durante este ciclo
em qualquer parte do programa.
Para tentar ser mais claro, poderia fazer o seguinte:
A M4.3
= I0.0
Se esse código estiver rodando na primeira linha do OB1, a entrada (ou
mais correto dizer, o endereço) I0.0 será cópia do valor de M4.3.
Se você não sabia desse fato, fique sabendo agora, e saiba também que
todo PLC a principio segue esse comportamento.
Devido a esse comportamento, que eu vivo criando ferramentas de
simulação de I/O para tudo quanto é tipo de PLC, assim como queria fazer
o Williams.
Legal, mas aonde entra o ANY nisso tudo? Esse é o ponto meu camarada! O
Williams que não é bobo, queria fazer uma função parametrizada que
fizesse isso por ele. Logo ele iria entrar com o endereço inicial da entrada
que ele iria simular e os valores que deveriam ser forçados. Nada mais
prático!
Bom, eu mandei uma sugestão para ele, sendo que poderia criar o seguinte
FC:
FUNCTION FC 3 : VOID
TITLE =
FAMILY : Sim
NAME : SimIO
VERSION : 1.0

VAR_INPUT
IoIn : ANY ; //Pointer entrada digital para ser forçada
IN00 : BOOL ; //Valor da entrada 0
IN01 : BOOL ; //Valor da entrada 1
IN02 : BOOL ; //Valor da entrada 2
IN03 : BOOL ; //Valor da entrada 3
IN04 : BOOL ; //Valor da entrada 4
IN05 : BOOL ; //Valor da entrada 5
IN06 : BOOL ; //Valor da entrada 6
IN07 : BOOL ; //Valor da entrada7
END_VAR

VAR_TEMP
DB_NR : WORD ;
END_VAR BEGIN NETWORK
TITLE =Inicializa apontador
//Ver que esta sendo usado um parametros do tipo ANY
//Deverá ser chamado este bloco passando o primeiro bit da área a ser
//sobreescrita.
///////////////////////////////////////////////////////////////////////////////////
///////////
L P##IoIn;
LAR1 ;
//Abre o DB, se estiver sendo utilizado
L W [AR1,P#4.0];
T #DB_NR;
OPN DB [#DB_NR];
//carrega pointeiro de area cruzada. Ver ponteiro any
L D [AR1,P#6.0];
LAR1 ;
///////////////////////////////////////////////////////////////////////////////////
///////////
NETWORK
TITLE =Sobreescreve os bits apontados pelo parametro IoIn
///////////////////////////////////////////////////////////////////////////////////
///////////
//Entrada 00
A #IN00;
= [AR1,P#0.0];
//Entrada 01
A #IN01;
= [AR1,P#0.1];
//Entrada 02
A #IN02;
= [AR1,P#0.2];
//Entrada 03
A #IN03;
= [AR1,P#0.3];
//Entrada 04
A #IN04;
= [AR1,P#0.4];
//Entrada 05
A #IN05;
= [AR1,P#0.5];
//Entrada 06
A #IN06;
= [AR1,P#0.6];
//Entrada 07
A #IN07;
= [AR1,P#0.7];
///////////////////////////////////////////////////////////////////////////////////
///////////
END_FUNCTION

Bom, pra ser sincero, o segredo desse FC está em como o parâmetro ANY é
passado para o FC, pois se vocês perceberem, o IoIn é um parâmetro de
entrada. Mas se ele é um parâmetro de entrada, como pode estar sendo
sobrescrito? No mínimo deveria ser um parâmetro do tipo Input/Output.
Não vamos nos desviar do nosso foco, o ANY. A grande sacada é perceber
que o ANY nos informa o tipo e o endereço do dado que está sendo
passado, e com isso poderiamos avaliar todas as informações possíveis
sobre este dado.
No FC acima, foi executada a instrução
L P#IoIn
LAR1
Mas que raio isso significa?
A instrução L P#IoIn está carregando o ponteiro de 32 bits no acumulador 1
da CPU (ou o ACCU1), ela retorna algum valor do tipo DW#16#8700XXYY,
onde:
B#16#87: Corresponde a memória local anterior. E o que significa isso?
Resumidamente, significa que o ANY em questão está armazenado na área
local (L stack) do bloco que o chamou. Aqui é o segredo da coisa. Pois, com
a próxima instrução, LAR1, o conteúdo do ACCU1 será copiado para o
registrador de endereço 1 (ou o AR1).
Como o AR1 está apontando exatamente para o ANY que foi passado, basta
copiarmos a parte do ponteiro de 32 Bits de área cruzada desse ANY para o
AR1, e logo o AR1 irá apontar exatamente para a variável apontanda
inicialmente pelo IoIn. Dessa forma:
L D[AR1,P#6.0]
LAR1
Uma vez que você tem o ponteiro de onde sua informação está
armazenada, é só acessá-la. Como? Veja isso:
A #IN01
= [AR1,P#0.0]
O que significa esse raio pior ainda?
Calma, vamos analisar.
A instrução A #IN01 não deveria ser problema para quem está querendo
aprender o ANY. No caso essa instrução está iniciando uma operação lógica
com o bit IN01 (parâmetro de entrada do tipo BOOL, ver declaração do FC).
Ok, mas o que significa =[AR1,P#0.0]? Significa que o resultado da operação
lógica (RLO para os íntimos) será atribuída ao endereço apontado por AR1
(que guardou o endereço do parâmetro IoIn, conforme código acima)
deslocada de zero bit, conforme o OFFSET P#0.0. Lindo, não?
Se você quisesse escrever no endereço apontado por AR1, porém
deslocado de 2 bits você poderia fazer o seguinte:
Opção A:
= [AR1, P#0.2]
Dessa forma você está mudando o deslocamento através do OFFSET da
instrução de escrita.
Opção B:
L P#0.2
+AR1
= [AR1,P#0.0]
Sim, amigo. Existe notação de ponteiros no PLC da Siemens. No caso as
instruções L P#0.2 +AR1, somou o deslocamento de 2 bits ao endereço
apontado por AR1. Evidentemente nesse caso, perdeu-se o endereço
apontado por IoIn.

Conclusão:

Percebe-se por este pequeno post, que o uso do ANY é muito mais
complexo do que a sua documentação sugere. Até porque fizemos um uso
até que simples para ele.
Poderiamos aumentar as possibilidade utilizando dados complexos
(maiores que 32 bits) como , array, structs, UDT, date and time e outros,
porém tudo parte do principio que o ANY pode apontar para esses dados
através de seus 80 bits.
Evidentemente e forçadamente eu tentei evitar o seu uso em lógicas
simples, como para intertravamentos ou o uso do ANY onde outros meios
fariam a mesma coisa e com uma simplicidade de entendimento e
desenvolvimento muito melhor. Logo NÃO SE USA O ANY AONDE NÃO É
PRECISO, porém o seu uso pode facilitar e muito certos problemas.
Exemplos aonde o ANY pode ser utilizado:
Leituras e Escritas de records extensos;
Criação de funções com buffers e vetores;
Funções que precisam avaliar o tipo de dado que está sendo passado, e
que o mesmo pode mudar ao longo da execução do programa.
Blocos de Comunicação (quase todos os que eu conheço usam o ANY como
parâmetro).
Criação de funções que demandem alto processamento de copy, move, set
e reset.
Funções que demandem acessos indexados.

No caso de funções que demandem somente acessos indexados simples,


eu sugiro antes de usar o ANY o estudo do uso do seguintes acessos
indexados:
Ponteiro de 16 Bits com uso de memória (para DBs, temporizadores e
contadores). Ex.:
OPN DB[ MW100]
SE[MW100]
CD[MW100];
Ponteiro de 32 Bits com uso de memória (com área interna). Ex.:
L P#0.0
T MD100
L IW[MD100]
Ponteiro de 32 Bits com uso de registrador indireto (com área interna). Ex.:
L P#0.0
LAR1
L IW[AR1,P#0.0]
Ponteiro de 32 Bits com uso de registrador indireto (com área cruzada). Ex.:
L P#I0.0
LAR1
L W[AR1,P#0.0]
Ponteiro de 48 Bits com uso de registrador indireto (com área cruzada).
Este permite o acesso ao número do DB utilizado.
Este ponteiro funciona como o ponteiro de 32 Bits com uso de registrador
indireto e com área cruzada. A principal diferença é que ele pode ser
declarado como parâmetro em FB e FC. Sua estrutura segue o padrão da
figura abaixo.

Formatação do Pointer

O uso de parâmetros complexos, como o Ponteiro de 48 Bits e o ANY (80


Bits) tem restrições quando utilizados em FB e FC, porém não vou entrar
em detalhes, pois o próprio Step7 não deixará você utilizá-los de forma
errada, porém faço grandes advertências em relação ao uso dos
registradores de endereço, AR1 e AR2.

Cuidados com o uso do AR1 e do AR2:


Cuidado com o uso do AR1 em FCs que chamam blocos que pedem dados
complexos (string, array,structure e UDT) como parâmetro. O mesmo vale
para FBs que passam esses parâmetros como IN_OUT.
Cuidado com o uso do AR2 dentro de FBs, pois ele é utilizado para acessar
os dados do DB Instance, logo dependendo da forma como você alterá-lo,
pode impedir o acesso aos dados do DB instance desse FB.

Uma luz no fim do túnel:


Pra quem está dizendo:
Não entendi nada!
Que porcaria esse ANY e esses acessos indexados!
Tô perdido!

Eu dou um conselho. Não entre em pânico!


Felizmente você pode passar a vida toda sem se preocupar em ter que
aprender o ANY e seus derivados, pois o seu processo pode ser
extremamente simples e não demandar algo do tipo. Ou então você pode
precisar de algo do tipo e acabar de vez com os seus problemas
programando o PLC com a linguagem SCL (o texto estruturado da
IEC61131-3). Veja que facilidade em fazer um acesso indexado sobre um
DB indexado sendo que o componente interno desse DB também será
acessado de forma indexada:
word_to_db(número_DB).DW[número_componente].
Nada mais simples!
Evidentemente o que está rolando por trás é muito acesso indexado com o
ANY e seus derivados. Mas você nem precisaria saber isso!