LogicLab PLC runtime porting requirements

From Axel Public Wiki
Jump to navigation Jump to search

Introduction

This page contains the list of sotfware components that are to be implemented on a system in order to perform the LogicLab PLC runtime porting on the system itself.
Usually these software components are provided by the customer. If some or all of these components are non available, Axel can provide for the implementation.
Note: data types used for function prototypes follow the Misra C specification.

Runtime porting main elements

In the following picture are represented the main elements involved in a LogicLab PLC runtime porting.

  • elements in blue color are provided by Axel
  • elements in yellow color should be (usually) provided by the customer


LLRTComponents.png

Task scheduler

The task scheduler is an optional component. The PLC run-time can work also in a single-task (main loop) software environment. The most common implementations of the PLC runtime are:

Single task
(main loop)
Is the basic PLC environment. It results in a non-deterministic PLC execution, all system functions (communication protocol, PLC execution etc.) are executed sequentially.
The PLC has not a fixed execution period.
Interrupt-based scheduling Typical solution for low-resources systems, without an operating systems. It achieves a deterministic PLC execution.
The most common implementation is realized with an interrupt timer and a main loop.
  • the interrupt timer run a PLC deterministic task
  • the main loop manages the communication protocol, the runtime management functions and, usually, a non-deterministic background PLC
Full O.S. scheduler Common implementation on high performance systems. It allows to have a multi-tasking deterministic PLC environment.

Communication protocol

From the run-time point of view, the communication protocol allows the connection between the LogicLab PC environment and the target system.
LogicLab can support almost every kind of communication protocol and communication port. Between communication ports, the most common are:

  • serial ports (UARTs) based on RS232, RS485-422, TTL
  • ethernet
  • CAN
  • USB based (both serial emulation and custo USB protocol)
  • Profibus

The communication protocol can be provided by Axel. Alternatively an already existing customer protocol can be used.
Axel protocols alternatives are:

  • Axel GBD (available with serial ports and ethernet)
  • Modbus RTU (serial ports)
  • Modbus TCP

Customer implemented protocols can be:

  • Standard protocols (CANopen, DeviceNet, Profibus)
  • Custom protocols

Protocol requested features

The communication between the LogicLab IDE and the target system requires the following potocol functions:

  • object/register exchange (like Modbus holding registers R/W functions or CANopen SDO R/W functions)
  • target memory read/write functions

Communication porting issues

As explained above, two options are available for the communication protocol implementation:

Axel protocol The customer has to provide the low-level communication port functions
Customer protocol The protocol functions (object and memory exchange) are to be connected to the runtime core.
If some funtions are missing (e.g. memory exchange) they are to be added to the protocol implementation.

Storage memory

The PLC run-time uses the storage memory to store the PLC executable code received by the LogicLab PC environment and make it available for the subsequent system's start up.
Typical permanent storage memories are:

  • directly accessed flash memory
  • filesystems (based on flash, disk, etc.)

At porting time, the relative read/write functions are needed.

PLC run-time porting software reference

Communication

The basic functions needed for communication protocol porting are:

  • port setup
  • data available status
  • read data from the port
  • write data to the port

Port driver implementation

In order to achieve good performances, buffered I/O is highly recommended. For an optimal porting, expecially in systems without an O.S., all the I/o functions have to return immediately or return as faster as possible. For the above reason the availability of functions that allows to check for data ready are recommended.

Serial communication RS232/RS485

This is a guideline to get the functionality required by the serial protocols driver. It is not necessary to implement the prototypes or the function names exactly in the way proposed.

Enumeratives, data types and function prototypes can be defined by low level driver implementation as required by specific porting.

/* Open port with specified settings
 * Desired serial port must be opened with specified settings.
 * Low level driver should be able to handle:
 * - number of the port to open
 * - baudrate (es: 9600, 19200, 38400, 57600, 115200)
 * - parity, databits, stopbits to configure port (es: N81, E81)
 * PARAMETERS:
 *     port_id: the id of the port (es: 1 for UART1 or COM1)
 *     baudrate: the desired baudrate (es: 115200 or BAUDRATE_115200)
 *     parity: the desired parity. A value of an enumerative with parity NONE/EVEN/ODD
 *     parity: the desired databits. A value of an enumerative with supported databits
 *     stopbits: the desired stopbits. A value of an enumerative with supported stopbits
 *     rx_callback_func: function that must be called by low level driver when character is received by interrupt routine
 *                                          typedef void (* rx_callback_func_t)(serial_port_handle_t handle, uint8_t rx_char)
 * RETURNS:
 *     Port handle (can be the number of the COM, or the address of the low level driver port).
 *     NULL handle or zero in case of error.
 * NOTE:
 *     No problem to use char_t * port_id to specify port "COM1" or "/dev/tty0"
 *     baudrate_t, parity_t, databits_t, stopbits_t can be replaced by port_settings_t es: LINE_CONF_N81 depending on what is better for low level driver.
 */
serial_port_handle_t SerialPortOpen( uint32_t port_id, baudrate_t baudrate, parity_t parity, databits_t databits, stopbits_t stopbits, rx_callback_func_t rx_callback_func );

/* Close specified port
 * Serial port must be closed. Port will be then available to be opened again with different settings
 * PARAMETERS:
 *     serial_port_handle_t port_handle: the handle of the previously opened port
 */
void SerialPortClose( serial_port_handle_t port_handle );

/* Data receive via interrupt (RECOMMENDED implementation)
 * Calling provided callback from rx interrupt is the most recommended way to implement receive of characters from serial bus.
 * Low level driver should handle the rx interrupt routine and call the rx callback function.
 * Callback function is passed as parameter on the OpenCommPort function
 * PARAMETERS:
 *     port_handle: the handle of the port on which character has been just received
 *     rx_data: the received character
 */
typedef void (* rx_callback_func_t)( serial_port_handle_t handle, uint8_t rx_data );

/* Data byte receive by polling
 * N.B.: this function must be provided only if no rx interrupt handler can be managed
 * with this kind of implementation some speed limitations on serial bus configuration may be required
 * PARAMETERS:
 *     port_handle: the handle of the port to check for received data
 *     rx_data: pointer where to store receved data
 * RETURNS:
 *     TRUE data is available, FALSE no data avalable
 * NOTE:
 *     If no data available the function has to return immediately
 */
bool_t SerialPortGetByte( serial_port_handle_t port_handle, uint8_t * rx_data );

/* Data byte receive by polling with blocking timeout
 * N.B.: this function must be provided only if no rx interrupt handler can be managed
 * with this kind of implementation some speed limitations on serial bus configuration may be required
 * PARAMETERS:
 *     port_handle: the handle of the port to check for received data
 *     rx_data: pointer where to store receved data
 *     msTimeout: timeout in ms
 * RETURNS:
 *     TRUE as soon as data is available, FALSE if no data avalable until timeout elapsed
 * NOTE:
 *     Function return as soon as a byte is availabe or after the specified timeout. This is a blocking function.
 */
bool_t SerialPortGetByteTmo( serial_port_handle_t port_handle, uint8_t * rx_data , uint32_t msTimeout );

/* Data buffer transmission
 * This function is used to send a buffer of data on the serial bus
 * PARAMETERS
 *     port_handle: the handle of the port on which data should be sent
 *     tx_data_buffer: pointer to the data to send
 *     tx_data_size: number of bytes to send
 * NOTE
 *     this function call can be blocking (stop caller until all data have been sent)
 */
uint32_t SerialPortSendData( serial_port_handle_t port_handle, const uint8_t * tx_data_buffer, uint32_t tx_data_size );

/* Set port in receive mode direction.
 * This function is required for RS485 serial only to drive RS485 direction signal in RX mode.
 * PARAMETERS
 *     port_handle: the handle of the port to put in receive mode
 * NOTE
 *     this function is called just after SerialPortSendData function
 */
void SerialPortSetRxMode( serial_port_handle_t port_handle );

/* Set port in transmission mode direction.
 * This function is required for RS485 serial only to drive RS485 direction signal in RX mode.
 * PARAMETERS
 *     port_handle: the handle of the port to put in transmission mode
 * NOTE
 *     this function is called just before SerialPortSendData function
 */
void SerialPortSetTxMode( serial_port_handle_t port_handle );

CAN port communication

This is a guideline to get the functionality required by the CAN protocols driver. It is not necessary to implement the prototypes or the function names exactly in the way proposed.

Enumeratives, data types and function prototypes can be defined by low level driver implementation as required by specific porting.

For CAN communication and/or CANopen protocol porting, buffered and interrupt driven CAN I/O is crucial for good overall performances.

/* Initialize CAN driver at specified baudrate
 * PARAMETERS:
 *     port: CAN channel to open
 *     baudrate: CAN baudrate
 *     rx_callback_func: function that must be called by low level driver when CAN frame is received by interrupt routine
 *                       typedef void (* rx_callback_func_t)(can_port_handle_t port_handle, uint32_t mailbox)
 */
can_port_handle_t CANPortInit( uint16_t port, can_driver_baudrate_t baudrate, rx_callback_func_t rx_callback_func );

/* Stop communication and deinitialize CAN port.
 * Once closed, CAN port can be reopened with different settings.
 * PARAMETERS:
 *     port_handle: CAN port handle to close
 */
can_port_handle_t CANPortClose( can_port_handle_t port_handle );

/* Configure mailbox for specified cobid.
 * PARAMETERS:
 *     port_handle: can port handle
 *     mailbox: mailbox to assign
 *     msgID: cobid to assign to specified mailbox (or zero if accept all filter is specified)
 *     rtr: this flag should be managed for mailbox assignement
 *     filter_mode: assign filter for specified mailbox (es: mailbox can receive only the specified cobid, no filter specified)
 * NOTE:
 *     N.B.: CANopen master implementation requires only one mailbox with no filters to be configured
 *           DS301 CANopen slave full implementation requires several mailbox with exact cobid match filter
 *     low level driver shoud publish available mailboxes definitions
 */
void CANSetMailbox( can_port_handle_t port_handle, uint32_t mailbox, uint32_t msgID, bool_t rtr, filter_mode_t filter_mode );

/* Configure mailbox for specified cobid.
 * PARAMETERS:
 *     port_handle: CAN port handle
 *     mailbox: mailbox to unassign
 * NOTE:
 *     low level driver shoud publish available mailboxes definitions
 */
void CANDelMailbox( can_port_handle_t port_handle, uint32_t mailbox );

/* CAN port start.
 * This function is called by driver once port has been opened and mailboxes have been configured.
 * Enable Rx/Tx
 * PARAMETERS:
 *     port_handle: CAN port handle
 */
bool_t CANPortStart( can_port_handle_t port_handle );

/* Callback function is passed as parameter on the CANPortInit function.
 * If specified, this function must be called by low level driver on the rx interrupt handler.
 * PARAMETERS:
 *     port_handle: the handle of the port on which character has been just received.
 *     mailbox: the mailbox in which received CAN message is available.
 */
typedef void (* rx_callback_func_t)( can_port_handle_t port_handle, uint32_t mailbox );

/* Read a message from the CAN controller
 * PARAMETERS:
 *     port_handle: CAN port handle
 *     mailbox: CAN mailbox to read
 *     msgID: pointer for storing the ID of the CAN message received
 *     len: pointer for storing the length of the CAN message received
 *     rtr: request to receive flag
 *     data: pointer to storage for the 8 bytes of the CAN message
 * RETURNS:
 *     TRUE data frame is available, FALSE no data frame available
 * NOTE:
 *     This function is usually called by RX callback function or in polling mode if RX callback is not used/specified
 */
bool_t CANGetMessage( can_port_handle_t port_handle, uint32_t mailbox, uint16_t * msgID, uint8_t * len, bool_t * rtr, uint8_t data[8] );

/* Send a message to the CAN controller.
 * If it is not possible to transmit the message immediately, the request must be put in a queue of transmission by low level driver.
 * Then, message is sent automatically by low level driver as soon as it is possible (usually this is done on TX complete interrupt).
 * N.B.: messages must be transmitted in first in first out order.
 * PARAMETERS:
 *     port_handle: can port handle
 *     msgID: cobid of the CAN message to transmit
 *     len: length of the CAN message to transmit
 *     rtr: request to receive flag to transmit
 *     data: pointer to the 8 bytes (max) of the CAN message to transmit
 * RETURNS:
 *     TRUE transmission executed/queued, FALSE transmission error
 */
bool_t CANPutMessage( can_port_handle_t port_handle, uint16_t msgID, uint8_t len, bool_t rtr, uint8_t data[8] );

Typical TCP/IP port functions

If communication is based on ethernet TCP/IP, standard socket functions or equivalents are suitable.

This is the basic set of functions required:

int bind(int s, const struct sockaddr *name, socklen_t namelen);
int listen(int s, int backlog);
int accept(int s, struct sockaddr *addr, socklen_t *addrlen);
int select(int maxfdp1, fd_set *readset, fd_set *writeset, fd_set *exceptset, struct timeval *timeout);
int recv(int s, void *mem, size_t len, int flags);
int send(int s, const void *dataptr, size_t size, int flags);
int shutdown(int s, int how);

Timers

High resolution timer 1 MHz

Free running high resolution timer (1 MHz) is required to calculate system performance. If it is not available this kind of timer, systick and tick frequency information can be used instead.

/* Get hires timer us
 * RETURNS:
 *     return the number of us elapsed
 * NOTE:
 *     value should start from zero and then count to 0xFFFFFFFF, then automatically restart from zero and count again.
 */
uint32_t GetHiResTimerUs(void);

Storage

Two categories of storage are mentioned below:

  • flash memory
  • file system

Flash memory functions

This is a guideline to get the functionality required by the flash driver.

It is not necessary to implement the prototypes or the function names exactly in the way proposed.

Enumeratives, data types and function prototypes can be defined by low level driver implementation as required by specific porting.

For an optimal performance during code download from LogicLab PC environment to the target system, the flash memory functions shold be executed without requiring to halt the system, entering boot mode, manually rebbot the system etc.

/* Erase flash area
 * PARAMETERS:
 *     address: physical address where data has to be placed
 *     size: size of the area to be erased
 * RETURNS:
 *     0 area correctly erased, <> 0 error code
 * NOTE:
 *     If address is not the start address of an eraseble sector or size does not match with the entire sectors size involved, function must return a specific error code.
 *     Size can be the size of one or more contiguous sectors from the start address. In this case flash driver should erase all required sectors.
 */
flash_err_t FlashErase( addr_t address, uint32_t size );

/* Writes data into the flash
 * PARAMETERS:
 *     address: physical address where data has to be placed
 *     pdata: pointer to the data to be written
 *     size: length of the data to be written
 * RETURNS:
 *     0 data correctly written, <> 0 error code
 * NOTE:
 *     Limitations about writings (es: user can write only four bytes at the time, or user can write only blocks of 128 bytes) should be documented.
 */
flash_err_t FlashWrite( addr_t address, const uint8_t * pdata, uint32_t size );

File system functions

If target system is equipped with a filesystem, the tipical filesystem API functions are suitable for the runtime integration:

  • fopen
  • fclose
  • fread
  • fwrite
  • fseek