Que son los punteros en la programación de PLCs, lo básico que hay que saber

Fecha de publicación
Cateogría del artículo Blog Automatas
Visualizaciones del artículo Leído  3799  veces
Tags del artículo

Pere Garriga nos explica cómo funciona el direccionamiento por punteros en la programación de autómatas con ejemplo de cómo trabaja CODESYS

Que son los punteros en la programación de PLCs, lo básico que hay que saber

El propósito de este artículo es dar a conocer cómo funciona el direccionamiento por punteros, para programadores que vienen, más, del sector eléctrico que del mundo informático y no están habituados al empleo de esta técnica. Por otra parte, hay quien los confunde con el direccionamiento indexado de un array. En este articulo también se explica el acceso por referencia y cuando emplear un tipo u otro.

Ubicación de las variables / datos en la memoria

En todos los programas de cualquier tipo de controlador, sea un PLC, un PAC o un PC, los datos se ubican en su memoria. Podemos ver la memoria como una zona de almacenamiento que contiene miles o millones de celdas (Bytes) el compilador es quien asigna en que “celdas” se ubican, o “viven” las variables/datos; según sea el tipo de dato se precisan más o menos “celdas”. A partir de ahora llamaremos Byte o posición de memoria a las “celdas”. Cada Byte de la memoria tiene un número, al que se llama dirección de memoria, supongamos una memoria de 65535 Bytes tendría desde la dirección 16#0000 -se expresa en hexadecimal- hasta la 16#FFFF. En el siguiente gráfico, a modo de ejemplo, se muestran los bytes de la memoria desde la dirección 16#0000 hasta la 16#0087 y su contenido.

En los actuales controladores de tipo PAC o PC se dispone de GBytes de memoria, para direccionar una memoria de 2 Gbytes las direcciones irían de 16#00000000 hasta 16#80000000, lo que serían un total de 2.147.483.648 Bytes

En este caso el compilador ha ubicado en esta zona los tags / variables que se muestran en la tabla de la imagen. El acumulador de piezas wNumPiezas que es del tipo WORD (2 Bytes), el contador de horas de funcionamiento dwHorasFun, del tipo DWORD (4 Bytes), el número de paso inStep_01 del tipo INT (1 Byte), el valor de velocidad máxima rVelMax, del tipo REAL (4 Bytes) y el valor de posición de pick lrPosPick del tipo LREAL (8 Bytes); la estructura de datos stAxisInfo que contiene un valor en coma flotante de doble precisión y dos de simple precisión, que hacen un total de 16 Bytes. En la dirección de memoria 16#0024 se ha ubicado el array de datos ainFifo, de 10 elementos del tipo INT.

Conociendo como está organizada la memoria, podemos acceder directamente a cualquier dirección y leer o escribir su valor.

Hay que tener presente que la memoria contiene también el código del programa, si con un puntero escribimos un dato en la memoria fuera de lugar, las consecuencias serán que el programa se colapsará.

¿Qué es un puntero?

Podríamos decir que es una vara con la que se señala algo, en este caso es un dato que apunta o señala hacia una dirección de memoria; dicho de otra forma, es una variable que contiene la dirección de memoria donde “vive” la variable. Con el empleo de punteros se accede a la memoria de forma directa, por lo que es una buena técnica para reducir el tiempo de ejecución de un programa y otras muchas más funcionalidades que se explican a lo largo de este artículo.

Un puntero de valor 16#0002 señalaría a la variable wNumPiezas o uno con el valor 16#000A señalaría a la variable inStep_01. En el símil de que la memoria fuese una calle, un puntero sería el número de indica la dirección de la vivienda a la que se quiere acceder. O también podríamos decir que un puntero contiene la dirección donde “vive” una variable.

Tipos de punteros

Hay un tipo de puntero para cada tipo de dato, programa, Function Block, funciones, etc. Según sea el “objeto” al que se desea acceder se necesita un puntero de un tipo u otro. Siguiendo con el símil de la calle también hay diversos tipos o formatos de direcciones. P.ej: C/Menorca, 12 o C/Almendros, 42, bloque A, 4º-2ª o Avda. de la riba, 101 Bajos.

Declaración de punteros en CODESYS

El compilador de CODESYS necesita conocer todos los punteros que se vayan a emplear en el proyecto, por lo que hay que declararlos, como cualquier otra variable. En el código se muestra el script necesario para la declaración de varios tipos de punteros:

Como saber qué dirección asignar al puntero

Para poder acceder a una variable mediante un puntero se necesita conocer su dirección de memoria, del mismo modo que se necesita saber una dirección postal para enviar una carta. Para ello se dispone de un operador llamado ADR que asigna la dirección de la variable deseada, al puntero. Es conveniente verificar que el valor del puntero no es cero, antes de utilizarlo.

Por otra parte, para poder leer / escribir el valor de la variable, a la que señala el puntero, se dispone del operador de contenido ^. Cuando se hace referencia al contenido, de la dirección de memoria apuntada, se habla de des referenciar el puntero. En el siguiente código se muestra un ejemplo:

¿Qué es un acceso indirecto?

Lo primero, decir que no tiene nada que ver con un puntero.

Un acceso indirecto permite elegir un número de elemento dentro de un array, hay una variable, llamada índice, que contiene el número del elemento del array al que se desea acceder. En este caso no se puede acceder a ninguna otra variable más allá de los elementos del array, insisto en que no tiene nada que ver con los punteros.

Con un puntero se puede acceder a cualquier dato u objeto que esté en la memoria del control. Con un acceso indirecto solo se puede acceder a los elementos de un array.

En el siguiente código se muestra unos ejemplos de acceso indirecto a un array

A este mismo array se puede acceder empleando un puntero, como se verá más adelante, lo que resulta más rápido en tiempo de ejecución, pero no tan claro para quien no suele usar los punteros.

Acceso a una estructura de datos mediante punteros

El proceso es el mismo que ya se ha visto para acceder a una variable del tipo INT, pero se tendrá que declarar un puntero del tipo adecuado, que coincida con el tipo de estructura a la que se desea acceder, veámoslo en el siguiente código:

Acceso a un array mediante punteros

El proceso es el mismo que ya se ha visto para acceder a una variable del tipo INT, pero se tendrá que declarar un puntero a un array del número de elementos y tipo de datos adecuados, veámoslo en el siguiente código:

Acceso a datos por referencias.

El acceso por referencia no deja de ser un acceso por puntero, pero en este caso la dirección de una referencia es la misma que la del objeto al que apunta. Un puntero tiene su propia dirección y esta contiene la dirección del objeto al que se quiere hacer referencia. Las referencias se inicializan al principio del programa y no pueden cambiar durante su ejecución. A un puntero se le puede cambiar su dirección tanto como sea necesario durante la ejecución del programa.

Otra forma de entender las referencias es como si fuesen otra manera de referirse a un mismo objeto / variable, como si fuese un alias.

Frente a los punteros, las referencias presentan las siguientes ventajas:

  1) Facilidad de uso
  2) Sintaxis más sencilla a la hora de pasar parámetros a funciones
  3) Minimiza errores en la escritura del código.

El resumen de todo esto, que se puede prestar a mucha confusión, es que, como se verá más adelante, el gran valor de las referencias es a la hora de pasar grandes cantidades de datos como parámetros de entrada a funciones.

Nota: Hay que decir que la norma IEC 61131-3 no permite referencias

Diversas formas de pase de parámetros a funciones.

Normalmente una función realiza unas operaciones con unos parámetros de entrada y retorna un valor - o varios - como resultado.

En el ejemplo que veremos seguidamente se trata de una función para calcular el área de un rectángulo, a la que le pasaremos los valores del lado A y el lado B para que nos retorne el resultado del área. Lo primero definiremos un tipo de dato [stRectángulo] que contendrá el lado A, el B y el área. Crearemos tres rectángulos, [stRectangulo01], [stRectangulo02] y [stRectangulo03]. Junto con tres variantes de la función para el cálculo del área, [Fc_AreaCalcVal] - pase por valores -, [Fc_AreaCalcPoint] - pase por puntero - y [Fc_AreaCalcRef] - pase por referencia –

A continuación, el código de las tres funciones:

Pase de valores:

Pase por puntero:

Pase por referencia:

Ejemplo de código de llamada a las funciones:

En este caso puede las diferencias pueden parecer insignificantes, puesto que la cantidad de datos que se le pasan a la función son pocos. Pero seguidamente veremos un ejemplo con mayor número de parámetros de entrada para poder apreciar las ventajas del pase de parámetros por, especialmente, referencia, también por puntero.

Caso de pase de grandes cantidades de datos a funciones.

Cuando se precisa pasar estructuras con gran cantidad de datos a funciones o a FB,s, el pase de parámetros por valores no es el método más adecuado puesto que se requieren gran cantidad de parámetros de entrada, cada parámetro implica crear una nueva variable local de la función, o del FB, lo que supone gasto de memoria y tiempo de ejecución en copiar los datos. Caso de estructuras de datos de varios Kbytes, o arrays de centenares o miles de elementos, este método es impensable.

En el caso de tener que pasar grandes cantidades de datos, la solución es el empleo de punteros, o mejor aún, el pase de datos por referencia. Seguidamente se muestra un ejemplo de una función para calcular el valor promedio de un array de 20 elementos, pasando los valores a la función y pasando los valores mediante una referencia.

Código de la función Fc_AverageValues para pase de valores:

Código de la función Fc_AverageReferencia para pase por referencia:

Código de ejemplo de llamada a ambas funciones:

Claramente la llamada a la función pasando los valores por referencia es la mejor. Y en este ejemplo se ha supuesto un ejemplo con solo 20 datos de entrada, pero lo normal es encontrar aplicaciones con estructuras de datos de varios Kbytes.

Resumen / Conclusiones:

• La memoria contiene miles y hasta millones de celdas o byte, en las que se ubica el código del programa y todos los datos / variables. Cada celda tiene su número, al que se llama dirección de memoria y que se suele expresar en hexadecimal 16#FA1204 -como ejemplo-

• Un puntero es una variable, que en lugar de contener un valor contiene una dirección de memoria, en la que “vive” la variable a la que realmente queremos acceder.

• Al igual que cualquier otra variable, hay que declarar los punteros para que el compilador pueda ubicarlos en la memoria. Recordemos que un puntero es una variable, pero que su contenido es una dirección de memoria.

Para cada tipo de variable se precisa el correspondiente tipo de puntero. No se puede acceder a una variable INT con un puntero pensado para acceder a una estructura de datos. Si Félix vive C/Del puntero, x, bloque y, mº-nª y le mando un envió a la dirección Avda. del Gygabyte, x. No le llegará.

• Nada tiene que ver el acceso indirecto a un array mediante una variable de índice, con un puntero. En este caso el acceso está limitado al propio array, con el puntero se puede acceder a cualquier dirección de memoria.

Con punteros se puede acceder a todo tipo de datos, en una simple línea de código se puede copiar una estructura entera de varios Kbytes de datos. Lo que resulta mucho más rápido.

• Una referencia se parece mucho a un puntero, para simplificar podríamos decir que es un “alias” de un objeto y que es algo menos críptico que los punteros, su principal utilidad es la de pasar gran cantidad de parámetros a funciones, de forma muy simple y rápida.

El pase de parámetros a una función se puede realizar de diversas formas, por valores, por punteros o por referencia, el programador deberá elegir el más adecuado para cada aplicación.

• Cuando se trata de grandes cantidades de datos el pase de parámetros por referencia o por punteros, serán los adecuados

• Este tema podría dar mucho de sí. Pero el artículo solo se pretende cubrir lo más básico, con el objetivo de tener una mínima base para poder empezar a utilizarlos, especialmente para aquellos programadores que vienen del sector industrial eléctrico.

> Aquí puedes acceder a manuales y ejemplos de programación en CODESYS

Linkedin Pere Garriga





Descargas