En el artículo anterior, decía que puede hacerse un programa para manejar desvíos (y otros artículos electromagnéticos tales como desenganchadores señales de brazo y relés), de una forma muy sencilla, de modo que con un programa así, construyendo una o más placas DEMU02 de mi sistema de control, y comprando una placa Velleman K8055 es posible tener un sistema de control por ordenador para los aparatos de vía que sustituye con ventaja a un cuadro esquemático realizado de forma artesanal.
Esbocé un poco la forma de hacerlo, pero una vez publicado el artículo, pensé que realmente sería muy sencillo hacerlo, tan sencillo, que podría terminarlo, poco menos que en un rato.
Así que me puse manos a la obra, y el resultado es positivo. La imagen de cabecera muestra la pantalla de ordenador con el esquema de vías que he hecho para la prueba. Como se ve, en cada desvío, hay un botón que, al presionarlo actúa sobre el desvío correspondiente y al mismo tiempo cambia de color, con lo que se convierte en una excelente señalización de la posición del desvío. He llamado CzLite a este programa.
En este pequeño vídeo se ve el programa funcionando. Como se puede apreciar no hay más que el ordenador con el programa funcionando, y una conexión por USB a la placa Velleman. Siento no haber tenido disponibles algunas placas DEMU y algunos desvíos para verlos moverse en correspondencia con las pulsaciones del ratón en los botones del programa, pero lo que si se ve muy bien son los leds indicadores de las salidas de la placa, encendiéndose y apagándose. Esto es lo esencial del tema, lo demás está ya super comprobado.
Bueno, pues tal como anticipaba, el programa que hace esto es sencillísimo. De hecho son apenas cincuenta líneas de programa en Visual Basic.
Como es habitual, he puesto en la página de descargas de este blog un enlace a una página de mi web desde la cual es posible descargarse el programa, tanto en modo ejecutable como en modo fuente. Las personas que tengan conocimientos de programación pueden basarse en el programa fuente suministrado para modificarlo y perfeccionarlo de acuerdo con sus necesidades, pero con el programa que se incluye en la descarga es posible ya manejar siete desvíos y es posible modificarlo para manejar una gran cantidad de desvíos (124 exactamente). En esa página de descargas hay instrucciones de como cargar el programa y de como modificarlo para incluir el esquema de vías necesario y situar todos los controles que se necesiten para manejarlo, así que no voy a repetir aquí esas instrucciones.
Lo que si voy a explicar es la forma de conseguir que las órdenes generadas por el programa actúen sobre la placa Velleman.
Como ya comenté recientemente Velleman suministra con su placa una librería DLL. También comenté que es mejor descargarla de la página de Velleman para garantizar que tenemos la última versión. Esta DLL hay que copiarla al directorio Windows/System del ordenador. Esta Dll suministra una serie de funciones, que hay que declarar en el programa. Por eso el programa lleva un módulo de nombre Welleman.bas con la declaración de variables:
Option Explicit
Declare Function OpenDevice Lib "k8055d.dll" (ByVal CardAddress As Long) As Long
' Declare Sub CloseDevice Lib "k8055d.dll" ()
' Declare Function ReadAnalogChannel Lib "k8055d.dll" (ByVal Channel As Long) As Long
' Declare Sub ReadAllAnalog Lib "k8055d.dll" (Data1 As Long, Data2 As Long)
' Declare Sub OutputAnalogChannel Lib "k8055d.dll" (ByVal Channel As Long, ByVal Data As Long)
' Declare Sub OutputAllAnalog Lib "k8055d.dll" (ByVal Data1 As Long, ByVal Data2 As Long)
' Declare Sub ClearAnalogChannel Lib "k8055d.dll" (ByVal Channel As Long)
' Declare Sub SetAllAnalog Lib "k8055d.dll" ()
' Declare Sub ClearAllAnalog Lib "k8055d.dll" ()
' Declare Sub SetAnalogChannel Lib "k8055d.dll" (ByVal Channel As Long)
Declare Sub WriteAllDigital Lib "k8055d.dll" (ByVal Data As Long)
' Declare Sub ClearDigitalChannel Lib "k8055d.dll" (ByVal Channel As Long)
Declare Sub ClearAllDigital Lib "k8055d.dll" ()
' Declare Sub SetDigitalChannel Lib "k8055d.dll" (ByVal Channel As Long)
' Declare Sub SetAllDigital Lib "k8055d.dll" ()
' Declare Function ReadDigitalChannel Lib "k8055d.dll" (ByVal Channel As Long) As Boolean
' Declare Function ReadAllDigital Lib "k8055d.dll" () As Long
' Declare Function ReadCounter Lib "k8055d.dll" (ByVal CounterNr As Long) As Long
' Declare Sub ResetCounter Lib "k8055d.dll" (ByVal CounterNr As Long)
' Declare Sub SetCounterDebounceTime Lib "k8055d.dll" (ByVal CounterNr As Long, ByVal DebounceTime As Long)
Este bloque está copiado directamente del programa ejemplo suministrado por Velleman. Como se puede ver, he dejado sólo tres funciones activas, que son las que se usan en este programa. El resto las he dejado comentadas.
Como decía el programa consta de un módulo denominado Welleman.bas y un único formulario, denominado Form1.frm. Al arrancar el programa se activa el formulario, así que lo primero que se ejecuta es el procedimiento Load del formulario Form1. Este procedimiento contiene las siguientes instrucciones:
Private Sub Form_Load()
Dim AnchoPixel As Integer
Dim AltoPixel As Integer
AnchoPixel = 1024 ' Resolucion de pantalla. Pixels ancho
AltoPixel = 600 ' Resolución de pantalla. Pixels alto
Me.Move 0, 0, AnchoPixel * Screen.TwipsPerPixelX, AltoPixel * Screen.TwipsPerPixelY
Me.Show
If Not AbreCanal(1) Then End ' Intenta abrir el canal de comunicación. Si no lo consigue termina
End Sub
Lo primero que hace este procedimiento es dar valor a dos variables que definen el tamaño que se desea dar a la ventana del programa. Aquí se le ha dado 1024 pixels de ancho por 600 de alto. La instrucción Move ajusta ese tamaño y la instrucción Show lo visualiza.
A continuación hace una llamada a la función AbreCanal. Esta función la veremos a continuación y lo que hace es "encender" la placa de comunicaciones. Como puede haber hasta cuatro placas de comunicaciones se incluye el parámetro 1 indicando que se debe abrir la primera placa. Esta función devuelve un valor True o False en función de si la placa ha respondido a la orden de apertura. Si la respuesta es False el programa termina, puesto que hay algo que no funciona.
Esta es la función AbreCanal:
Function AbreCanal(Canal As Integer) As Boolean
Dim CardAddress As Long 'Variables long para la función
Dim h As Long
CardAddress = Canal - 1 ' Puede abrir los canales 1 2 3 0 4 las direcciones de tarjeta son 0 1 2 y 3
h = OpenDevice(CardAddress) ' llama a la función y devuelve un valor h
Select Case h
Case 0, 1, 2, 3 ' Si devuelve en h la dirección de tarjeta es correcto
MsgBox "Canal " & Canal & " abierto", vbInformation + vbOKOnly
AbreCanal = True
Case -1 ' Si devuelve -1 no ha funcionado
MsgBox "El canal " & Canal & " no funciona", vbCritical + vbOKOnly
AbreCanal = False
End Select
End Function
Aquí hay por primera vez una llamada a una función de las incluidas en la DLL suministrada por Velleman. Se trata de la función OpenDevice que se invoca con la dirección de la tarjeta y devuelve en la variable h el mismo valor, si la apertura fue correcta, o el valor -1 si fué incorrecta. En ambos casos se emite un mensaje y cuando el usuario acepta el mensaje termina la función.
El otro objeto que tiene definido el programa es una matriz de "Command Buttons" de nombre Command1(). En la versión que se puede descargar desde mi página tiene definidos 8 elementos, numerados del 0 al 7. El primero de ellos Command1(0) tiene la propiedad Visible a False para que no se visualice en tiempo de ejecución. Se utiliza en tiempo de diseño para generar copias de si mismo y poder así hacer tantos elementos de la matriz de botones como sean necesarios. El resto son los botones que se activan al pulsarlos con el ratón y pasan de rojo a verde y viceversa, y emiten la orden de cambio.
Cuando presionamos cualquiera de estos botones se ejecuta el evento Command1.Click y la variable Index toma el valor del ídice del elemento pulsado. El código de este evento es el siguiente:
Private Sub Command1_Click(Index As Integer)
Dim Direccion As Integer
Dim Milisegundos As Single
Direccion = 2 * Index 'se asignan diecciones a cada botón segun su indice. De dos en dos
Milisegundos = 200 ' Tiempo de activación de los desvíos
If Command1(Index).BackColor = &HFF00& Then Command1(Index).BackColor = &HFF& Else Command1(Index).BackColor = &HFF00&
If Command1(Index).BackColor = &HFF00& Then ' botón verde
SendData Direccion, Milisegundos ' Envía el dato para posición desvío en recto
End If
If Command1(Index).BackColor = &HFF& Then ' botón rojo
SendData Direccion + 1, Milisegundos ' Envía el dato para posición desvío en curva
End If
End Sub
Aquí vemos que al ejecutarse este procedimiento, se calcula un dato denominado Direccion que es el dato que hay que enviar a la placa. Para hacerlo sencillo he hecho que la dirección se calcule a parttir del indice de cada botón, o sea de la variable Index. Asi para el primer botón Index vale 1 y Dirección se calcula igual a 2. Pra el segundo botón, la Dirección sale 4. Para el tercer botón la Dirección sale 6, y así sucesivamente hasta el botón numero 7 cuya Dirección es 14
La otra variable importante es Milisegundos Define el tiempo que va a durar el impulso que enviamos a cada desvío. Aquí está puesto fijo a 200 que representan 200 milisegundos.
La siguiente instrucción cambia el color de fondo del botón (variable BackColor del elemento de la matriz de botones) Si era verde lo pone rojo y si era rojo lo pone verde.
Y por último sea el color resultante verde o rojo se llama a la función SendData, que es la que efectivamente va a hacer la transmisión del dato. Obsérvese que en el caso de que el botón sea verde, o sea que queremos que el desvío se ponga recto, enviamos el dato Dirección, mientras que si el botón está rojo, queremos que el desvío se ponga en curva, y entonces enviamos el dato Direccion +1
O sea que, por ejemplo si presionamos el botón 4, y lo ponemos verde, llamamos a la función SendData con los parámetros 8 y 200 mientras que si lo ponemos rojo llamamos a la función con los parámetros 9 y 200. Se ve claro, el porqué asignamos la Dirección con valores de dos en dos.
Y por último, aquí está la función SendData:
Sub SendData(Dato As Integer, Tiempo As Single)
Dim segundos As Single
Dim t As Long
Dim N As Long
If Dato = 0 Then Exit Sub
segundos = Tiempo / 1000
N = Dato
WriteAllDigital N
t = Timer: Do While t + segundos > Timer: DoEvents: Loop
ClearAllDigital
End Sub
Como se ve, sencillamente convierte la variable de dirección (que aquí llama Dato) a entero largo con el nombre N También convierte el tiempo de transmisión que venía en milisegundos a segundos.
Entonces llama a la función de Velleman WriteAllDigital con el parámetro N. Es decir ordena que la salida de la placa muestre el valor N
Despues hace un loop para esperar el tiempo definido, que en nuestro caso serán 200 milisegundos, y entonces llama a la función de Velleman ClearAllDigital, que borra la salida de la placa.
Y eso es todo!. En este artículo he reproducido el código fuente del programa CzLite completo, tal como puede descargarse desde la página de descargas.
Como ya advertí en un artículo anterior, la parte de comunicaciones que a priori parece lo más difícil, es realmente mucho más sencilla de lo que cualquiera puede esperar, mientras que la parte gráfica, sobre todo cuando hacemos gráficos animados es mucho más dificil de hacer de lo que la mayoría espera. En este programa CzLite, la parte gráfica se ha reducido al mínimo, y por eso el programa ha resultado extraordinariamente simple. Por el contrario la parte de comunicaciones, no ha sido simplificada y de hecho, las funciones empleadas; AbreCanal, y SendData proceden del código del programa "grande" ControlZ.
Como vemos en el vídeo, hay unos Leds que se encienden en la placa Velleman cuando pulsamos los botones del programa. Supongo que muchos de mis lectores lo saben, pero aclaro que esos Leds están mostrando el nivel de tensión (0 voltios o 5 voltios) de los ocho bits de salida de la placa, bits que se activan para formar la expresión hexadecimal del dato que hemos enviado a la salida. Asumiendo que los leds encendidos representan los "unos" y los apagados los "ceros", los datos que enviamos se visualizan así:
0 00000000
1 00000001
2 00000010 --> Desvio 1 recto
3 00000011 --> Desvio 1 curva
4 00000100 --> Desvio 2 recto
5 00000101 --> Desvio 2 curva
6 00000110 --> Desvio 3 recto
7 00000111 --> Desvio 3 curva
8 00001000 --> Desvio 4 recto
------------------------------------------ etc
Las dos primeras direcciones no se usan porque la correspondiente a la posición recta es la 00000000 que en realidad es la posición de reposo, que adopta la salida cuando no hay ninguna orden
Nótese que esta tabla puede continuarse hasta el el valor 255, lo que da el enorme valor de 127 desvíos que sería posible manejar con este programa.
Y para el que no lo tenga claro, lo que hace el conjunto de placas DEMU1 DEMU2 y DEMU3 de mi sistema es activar con 12 voltios cada una de las 255 salidas que puede llegar a tener, en respuesta a las 255 posibles situaciones de la salida de la placa Velleman. Esta salida de 12 voltios se mantiene durante el tiempo que dura la activación de la salida, en definitiva durante 200 milisegundos en este caso.En definitiva hemos mandado un impulso de 12 voltios y 200 milisegundos de duracuón por esa salida. Si en esa salida está conectado el cable de un desvío éste se moverá en consecuencia.
Es el primer comentario que hago, a pesar de que sigo habitualmente, tanto el Blog, como la Web.
ResponderEliminarIgnacio, no se si a medida que vas avanzando te vas viniendo arriba pero lo que estás haciendo es realmente prodigioso. ¡ENHORABUENA! por la impagable aportación para los aficionados al tren sean de la escala que sean.
Un abrazo
Joaquin Pato359
Hola Joaquín
EliminarEncantado de verte por aquí. Muchas gracias por tus palabras.
Yo también me apunto a las felicitaciones. Gracias.
ResponderEliminarHacía mucho tiempo que estaba esperando este artículo, lo necesitaba para poder entender como se programaba la tarjeta.