Full Control by using PiControl

Although we’ve used its name throughout many newsletters we’ve never really explained it in detail: PiControl (aka “PiCon”) –the central driver of RevPi Core. It is the heart of our firmware as only PiCon enables your application software to exchange data with the central processing image. But PiCon offers more than just a software interface:

  • Allocating 4kByte of memory for process data
  • Providing a device driver for reading from and writing to this memory using standard Linux file access methods
  • Providing a function which returns system configuration data defined in PiCtory
  • Recognition of modules (IO, gateways) connected via PiBridge
  • Configuration of IO and gateway modules via PiBridge
  • Compares detected module arrangement with PiCtory configuration data

Would you like to learn in detail how PiCon works? Just go on reading. But caution! This time it’s this type of deep inside stuff…

During startup piControl.ko is executed and loads the following three components: RS485 communication, Ethernet communication and application interface.

Driver for cyclical data exchange over RS485 channel of PiBridge (e.g. I/O-Modules)

This driver also does scan the PiBridge during startup to detect all connected modules and their physical position. This is important, e.g. if you have connected several RevPi DIO modules and need to sort their data into the correct memory slot of the central processing image which has been defined by our configuration tool PiCtory. To get this information, during startup PiCon (using its third component) does read a JSON file generated by PiCtory and generates a structure which can be compared to the detected module configuration. Only if they are identical the cyclical data exchange is started. In case of a mismatch an error status is set and RevPi Core’s red LED is flashing. In case of a match each detected module gets it specific configuration data from RevPi Core, which is also extracted from the JSON file. The modules which are located at the physical end of PiBridge on both sides are getting a command to switch on their RS485 termination resistors. Then cyclical data exchange over RS485 is started with a cycle time dependent on the amount of data to be transferred but always being as small as possible. The address offsets related to each data item (i.e. the place where data is written to or read from) in the central processing image are part of the configuration structure read from the JSON file during startup. The new functions of DIO (counter inputs and PWM outputs) are drastically increasing the amount of data to be cyclically exchanged. We are currently working on possibilities to decrease this amount by avoiding redundancy: data will only be transmitted if values have changed since last transfer.

All functions of this driver are for internal us only and therefore not part of the interface library.

Driver for cyclical data exchange over Ethernet channels of PiBridge (e.g. Gateways)

Like the RS485 driver this one also uses the explained configuration structure and thus knows, which addresses of the process image are used for cyclical data exchange of a certain gateway module. Gateway data is copied as unstructured complete block of bytes from Ethernet-lines to the process image. For the opposite direction the data is also read as a complete unstructured block from the central processing image and send to the destination gateway via PiBridge’s Ethernet channel. For a future version we plan to still exchange the gateway data as an unstructured block between gateway and central processing image but at the same time allow user applications to access the same data in a structured way. You then may define symbolic names for parts of the data block (e.g. for a single byte). These symbolic names and their associated offset and length information in relation to the central process image may be used by application programs (like our soft SPS logi.CAD3) to get easy access to specific data items belonging to fieldbus networks.

All functions of this driver are for internal us only and therefore not part of the interface library.

„/dev/piControl0“ Linux device driver as interface for application programs and drivers written by users or other vendors

Let’s get practical and look at an example code snippet. It shows how easy your application program or driver gets access to data of the central processing image to cyclically read 2 bytes of data from offset 10:


#include “piControl.h” // This file is provided by KUNBUS
#include // This file is Linux standard driver for file access

int file_hd;
unsigned short inputdata; // This variable is just an example for a
//single data item to be cyclically read from process image

file_hd = open(PICONTROL_DEVICE, O_RDWR); // Name of file has to be set to „/dev/piControl0“

while (…) // This is your main loop with cyclical operations to be done
{
lseek(file_hd, 10, SEEK_SET); // 10 is just an example!
// use offset address in process image
// derived from JSON file by another function
read(file_hd, &inputdata, sizeof(intputdata));
doSomething(inputdata);
}
close(file_hd);

The function “write()” works just as easy as this – so we don’t need to write down an example for you. By the way: both functions of the driver (read() and write()) internally use a so called semaphore which restricts access in a way that only one task at a time gets access. Without such a restriction tasks could easily overwrite on another’s data resulting in corrupt data. E.g. the read() function call in our example would always read 2 bytes which had been both transmitted at the same cycle from RevPi DIO (programmers sometimes call this an “atomic access”). The restriction would not prevent two tasks to alternatively write to the same output. Such restrictions would be you applications responsibility.

Access of small amounts of data from the process image do need about 7 µs – just to give you an impression. A 1 kByte block could be accessed in about 10-13 µs so theoretically you could write or read 80 to 100 MB per second. So the access to process image using these functions is a very high-performance data transfer. If you plan to write your own driver, e.g. for an USB transceiver (radio or RS232), you should use these function calls to integrate your cyclical data into the process image so others may use it for their applications.
In order to use the correct offset for the access (i.e. the correct address) you could search for the symbolic name of your data item in the JSON file and extract the corresponding position and length data. Or you go the easy way and use our function which is declared in piControl.h and which delivers offset and length values for a given symbolic name:


int ret;
SPIVariable var;

strcpy(var.strVarName, “Temperatur”);

ret = ioctl(PiControlHandle_g, KB_FIND_VARIABLE, &var);
if (ret < 0)
{
printf(“could not find variable ‘%s’ in configuration.n”, var.strVarName);
}
else
{
printf(“Variable ‘%s’ is a offset %d and %d bits longn”, var.strVarName, var.i16uAddress, var.i8uLength);
}

Maybe you are one of those nerds who really want to get into it. Then look at the final sample code which explains the function ioctl() to set bit 3 and read bit 4 of 10th byte of process image. The structure “value” is defined in piControl.h It contains a value called i8uBit which uses 0 to 7 to get access to a single bit. Values above 7 will write all bits of the addressed byte to i8uValue:

SPIValue value;

value.i16uAddress = 10; // byte address
value.i8uBit = 3; // bit 3 of byte 10
value.i8uValue = 1; // set to 1

ret = ioctl(file_hd, KB_SET_VALUE, &value);

value.i16uAddress = 10; // byte address
value.i8uBit = 4; // bit 4 of byte 10

ret = ioctl(file_hd, KB_GET_VALUE, &value);

if (value.i8uValue)
printf(“bit 4 is setn”);

// set byte 11 to 166
value.i16uAddress = 11; // byte address
value.i8uBit = 8; // set the whole byte
value.i8uValue = 166;

ret = ioctl(file_hd, KB_SET_VALUE, &value);